Google BigQuery는 방대한 양의 데이터를 빠르고 효율적으로 분석할 수 있는 강력한 클라우드 데이터 웨어하우스 플랫폼입니다. 그러나 별다른 최적화 없이 무심코 사용하다 보면 예상치 못한 비용 폭탄에 직면하거나 쿼리 성능 저하로 업무에 지장을 받을 수 있습니다.
예를 들어, 단 한번의 쿼리 실행으로도 예상보다 높은 비용이 청구되거나 성능 튜닝이 이루어지지 않은 쿼리로 인해 처리 속도가 현저히 느려지는 상황을 경험하셨을 겁니다. 또한 비효율적인 데이터 처리 방식이 누적되면서 시스템 전반의 생산성이 떨어지는 문제에 직면하기도 합니다.
이번 포스팅에서는 비용과 속도를 동시에 잡을 수 있는 BigQuery 최적화 비법에 대해 소개해 드립니다. 실무에서 적용 가능한 내용 중심으로 구성했으니 데이터 분석과 운영 효율성을 극대화하는데 도움이 될 것입니다.
🔍 왜 BigQuery는 “읽는 만큼 요금”일까?
BigQuery는 스토리지 요금과 쿼리 요금이 분리되어 있으며, 쿼리 요금은 스캔하는 데이터 양(GB 단위)에 따라 계산됩니다.
📌 즉, 얼마나 ‘읽느냐’가 곧 비용이다.
이 말은 곧, 쿼리 성능을 높이는 것 = 비용을 절감하는 일이기도 합니다.
BigQuery의 성능을 극대화하고 비용을 절감할 수 있는 핵심 최적화 기법들을 자세히 살펴보겠습니다.
1. SELECT * 금지 – 필요한 컬럼만 선택
SELECT *는 테이블의 모든 컬럼을 조회하기 때문에 실제로 사용하지 않는 컬럼까지 모두 읽게 됩니다. 이로 인해 쿼리 비용이 불필요하게 증가하고 처리 속도도 느려질 수 있습니다. 특히 수십억 건 이상의 대용량 테이블에서는 이 차이가 더욱 두드러집니다.
SELECT 조회시 필요한 컬럼만 명시적으로 지정하여 사용합니다.
-- Bad
SELECT * FROM trk_page;
-- Good
SELECT user_id, name, dr, dt, regdate FROM trk_page;
2. 파티셔닝으로 데이터 분할하기
파티셔닝(PARTITIONING) 은 데이터를 날짜나 키 값을 기준으로 나눠 저장하는 기법입니다.
데이터를 날짜/시간/범주 기준으로 나누어 필요한 파티션만 읽도록 설정합니다.
CREATE TABLE trk_page
PARTITION BY DATE(regdate) AS
SELECT user_id, name, dr, dt, regdate FROM raw_data
쿼리 시 WHERE절에 파티션 조건이 명확히 있어야 효과가 나타납니다.
SELECT user_id, name, dr, dt, regdate FROM trk_page
WHERE DATE(regdate) = '2025-09-08'
💡 파티셔닝 효과
- 전체 테이블을 스캔하지 않아서 비용과 속도를 대폭 절감
- 일/주/월 단위 분석에 강력함
💡 주의할 점
- WHERE 절에 파티션 컬럼을 꼭 사용해야 적용됨
- TIMESTAMP보다 DATE 파티션이 더 비용 효율적
- WHERE DATE(event_date) = … 처럼 함수를 감싸면 파티션이 무시됨
3. 클러스터링으로 쿼리 범위 좁히기
클러스터링(CLUSTERING)은 테이블 안의 데이터를 특정 컬럼 기준으로 정렬 저장합니다.
데이터를 자주 필터링/조인하는 컬럼 기준으로 정렬해 저장합니다.
WHERE, GROUP BY, ORDER BY 등에 사용되는 자주 조회되는 컬럼에 클러스터링을 적용하면 훨씬 빠르게 검색합니다.
CREATE TABLE clustered_table
PARTITION BY DATE(regdate)
CLUSTER BY user_id
AS SELECT * FROM trk_page
💡 클러스터링 효과
- 특정 유저나 이벤트만 빠르게 찾을 수 있음
- 디스크 I/O 감소로 속도 향상 및 비용을 절감
💡 주의할 점
- WHERE 조건에 자주 사용되는 컬럼으로 사용
- BigQuery는 최대 4개의 클러스터 키 지원
- 자주 업데이트되는 테이블엔 부적절할 수 있음
4. 중간 결과는 MATERIALIZED VIEW 또는 TEMP TABLE로 저장
복잡한 JOIN, SubQuery, 반복 계산이 많은 경우에 중간 결과를 재사용 가능한 뷰로 저장합니다.
1) MATERIALIZED VIEW
일정한 쿼리 결과를 사전에 계산해서 저장해 두는 뷰입니다.
조회시마다 원본 테이블을 다시 계산하지 않고 BigQuery가 자동으로 변경된 부분만 갱신합니다.
-- 일별 매출 합계를 사전 계산해 저장
CREATE MATERIALIZED VIEW mv_daily_orders AS
SELECT
DATE(order_date) AS order_date,
SUM(total_amount) AS total_amount
FROM order
GROUP BY order_date;
-- 이후 매우 빠르고 저렴한 조회 가능
SELECT order_date, total_amount FROM mv_daily_orders WHERE order_date = '2025-09-08';
💡 주의할 점
- WHERE 절에 사용된 조건이 Materialized View의 쿼리와 일치해야 사용됨
- 실시간성이 중요한 경우 뷰 업데이트 지연 가능성 고려
2) TEMP TABLE(임시 테이블)
쿼리 내에서 생성하고 사용 후 자동 삭제되는 임시 테이블입니다.
복잡한 CTE나 중간 결과를 저장하고 재사용할 수 있습니다.
-- 1단계: 최근 30일 매출 필터링 결과를 임시 테이블로 저장
CREATE TEMP TABLE recent_order AS
SELECT customer_id,product,total_amount, order_date
FROM order
WHERE order_date >= DATE_SUB(CURRENT_DATE(), INTERVAL 30 DAY);
-- 2단계: 재사용
SELECT COUNT(*) FROM recent_order;
SELECT customer_id, SUM(total_amount)
FROM recent_order
GROUP BY customer_id;
💡 주의할 점
- 메모리가 아닌 스토리지에 저장되므로 과도한 양의 데이터 생성 시 비용 발생 가능
- 한 세션 내에서만 유효
5. EXPLAIN과 INFORMATION_SCHEMA로 쿼리 진단하기
1) EXPLAIN
EXPLAIN 키워드를 사용하여 쿼리를 실행하지 않고도 실행 계획을 미리 확인합니다. 비용이 발생하지 않으며 쿼리 구조를 분석하고 병목 지점을 찾는 데 유용합니다.
EXPLAIN
SELECT user_id, COUNT(*)
FROM trk_page
WHERE regdate BETWEEN '2005-09-01' AND '2025-09-08'
GROUP BY user_id;
💡 주의할 점
- DML 쿼리에는 사용할 수 없음
- 실제 쿼리를 실행하지는 않지만 비용 추정이 실제와 다를 수 있음
2) INFORMATION_SCHEMA
INFORMATION_SCHEMA는 쿼리 실행 이력, 테이블 메타데이터, 권한, 파티션 정보 등 메타데이터를 조회할 수 있는 시스템 뷰입니다. 쿼리를 이미 실행한 후라면 INFORMATION_SCHEMA를 통해 실제로 실행된 쿼리의 비용, 소요시간, 스캔 데이터 양 등 진단이 가능합니다.
아래 예시는 최근 쿼리 실행 내역을 확인합니다.
SELECT
creation_time,
user_email,
query,
total_bytes_processed,
total_slot_ms
FROM
`region-asia-northeast3`.INFORMATION_SCHEMA.JOBS_BY_USER
WHERE
state = "DONE"
AND creation_time > TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 1 DAY)
ORDER BY
creation_time DESC;
💡 주의할 점
- 리전(region)을 정확히 지정해야 함
- JOBS_BY_PROJECT는 관리자 권한이 필요
BigQuery는 기본적으로 매우 강력한 플랫폼이지만, 사용 방법에 따라 성능과 비용에서 큰 차이가 날수 있습니다.
데이터가 점점 많아지는 지금, 오늘 소개한 최적화 비법들을 잘 활용하면 분석 속도는 높이고 비용은 크게 줄일 수 있습니다.
작은 습관의 변화가 쌓이면 빅쿼리 운영 효율도 완전히 달라질 수 있다는 점, 실제 업무에 꼭 적용해 보시길 바랍니다.
최신 마케팅/고객 데이터 활용 사례를 받아보실 수 있습니다.