본문 바로가기
👩🏻‍💻TECH/개념정리

[GCP] BigQuery 성능 최적화

by Alicia03 2024. 12. 8.

 

💡 이번 포스팅에서는 이런 것들을 다뤄요
BigQuery는 대용량 데이터 분석을 위한 서버리스 데이터 웨어하우스로, 최적화된 쿼리를 작성하면 비용 절감과 처리 속도 향상의 효과를 볼 수 있다. 이번 포스팅에서는 구글 빅쿼리를 최적화 하는 네 개의 팁들을 정리해보았다. 

1. 불필요한 데이터 스캔 줄이기

2. 파티션 및 클러스터링 사용하기 
3. 조인 최적화
4. 뷰(View)/WITH절 활용

 

 

1. 불필요한 데이터 스캔 줄이기

BigQuery는 쿼리할 때 테이블의 스캔한 데이터 양(바이트 단위)에 따라 비용이 청구된다.

따라서 필요한 컬럼과 필요한 행만 조회하여 비용과 쿼리 시간을 줄일 수 있다. 

-- 비효율적인 쿼리 (모든 컬럼과 모든 행을 스캔)
SELECT * 
FROM `my_project.my_dataset.my_table`;

-- 최적화된 쿼리 (필요한 컬럼만 조회하고 WHERE 조건 추가)
SELECT user_id, event_type, event_time 
FROM `my_project.my_dataset.my_table`
WHERE event_date = '2024-01-01';

 

2. 파티션 및 클러스터링 사용하기

BigQuery 테이블을 파티션(partition)과 클러스터(cluster)로 분할하면 쿼리 성능이 향상된다. 

 

  • 파티셔닝: 테이블을 특정 컬럼(예: 날짜, 타임스탬프)으로 나누어 쿼리의 스캔 범위를 줄인다.
  • 클러스터링: 특정 컬럼의 값을 기준으로 데이터를 정렬해, 쿼리 성능을 향상시킨다.

파티셔닝과 클러스터링의 개념을 간단히 검토하고 쿼리 성능이 향상되는 원리를 살펴보자. 

1) 파티셔닝(Partitioning)

파티셔닝(Partitioning)은 테이블을 특정 기준으로 나누어 물리적으로 분할하는 방법이다. 

파티셔닝으로 쿼리가 최적화 되는 과정은 다음과 같다. 

  • 테이블을 파티션 단위로 나누어 저장한다.

 

-- event_date 컬럼을 기준으로 시간 기반 파티셔닝된 테이블 생성
CREATE TABLE `my_project.my_dataset.my_table`
PARTITION BY DATE(event_time)  -- 파티션 기준을 날짜로 설정
AS 
SELECT * 
FROM `my_project.my_dataset.raw_table`;
  • 쿼리가 실행되면, BigQuery가 필요한 파티션만 스캔하여 처리한다.
  • 쿼리할 때 WHERE 조건이 파티션 키에 적용되면, BigQuery는 해당 파티션만 읽어들인다.
-- 특정 날짜의 데이터만 조회 (필요한 파티션만 스캔)
SELECT * 
FROM `my_project.my_dataset.my_table`
WHERE DATE(event_time) = '2024-01-01';

 

2) 클러스터링(Clustering)의 원리

클러스터링(Clustering)은 테이블에 있는 데이터를 특정 컬럼 기준으로 정렬하는 방식이다.

 

클러스터링으로 쿼리가 최적화 되는 과정은 다음과 같다. 

 

  • 테이블 내 데이터를 정렬된 블록으로 저장한다.
-- 클러스터 테이블 생성 (user_id, event_type을 기준으로 정렬)
CREATE TABLE `my_project.my_dataset.my_table`
CLUSTER BY user_id, event_type  -- 클러스터 기준 컬럼
AS 
SELECT * 
FROM `my_project.my_dataset.raw_table`;
  • 쿼리가 실행되면, BigQuery는 필요한 블록만 스캔하여 데이터를 검색한다.
  • 특정 컬럼에 대한 WHERE 조건이 자주 사용될 때 효과적이다.
-- 특정 user_id의 데이터를 조회
SELECT * 
FROM `my_project.my_dataset.my_table`
WHERE user_id = 'user_123';

 

 

👀파티셔닝/클러스터링 비교 정리 

구분 파티셔닝(Partitioning) 클러스터링(Clustering)
목적 테이블을 분할해 데이터 스캔 감소 데이터를 정렬해 스캔 범위 축소
기준 컬럼 보통 날짜, 정수, 범위 컬럼 사용 WHERE 조건에 자주 사용되는 컬럼
물리적 데이터 분리 파티션 단위로 물리적 분할 테이블 블록 내 데이터 정렬
성능 향상 원리 필요한 파티션만 읽음 필요한 블록만 스캔
쿼리 최적화 방식 WHERE 절에 파티션 키 조건 추가 WHERE, GROUP BY, ORDER BY 최적화
주요 사용 상황 특정 기간/범위 데이터 조회 특정 키에 대해 빈번한 필터링 발생
설정 가능 컬럼 수 1개 컬럼만 설정 가능 최대 4개 컬럼 클러스터 가능
대표 예시 DATE(event_time)으로 파티셔닝 user_id, event_type으로 클러스터링
데이터 관리 신규 데이터 삽입 시 파티션 자동 추가 자동 정렬 및 블록 생성

 

3. 조인(Join) 최적화하기

 

  • WITH 절을 사용해 조인 전에 필터링하여 스캔 범위를 줄인다
  • Broad Join을 피하고 키 값에 인덱스 역할을 수행할 수 있는 컬럼으로 조인하기
-- 비효율적인 쿼리 (필터가 조인 후에 적용됨)
SELECT a.user_id, b.order_id 
FROM `my_project.dataset.user_data` a
JOIN `my_project.dataset.order_data` b 
  ON a.user_id = b.user_id 
WHERE a.region = 'US';

-- 최적화된 쿼리 (필터를 조인 전에 적용)
WITH filtered_users AS (
  SELECT user_id 
  FROM `my_project.dataset.user_data` 
  WHERE region = 'US'
)
SELECT a.user_id, b.order_id 
FROM filtered_users a
JOIN `my_project.dataset.order_data` b 
  ON a.user_id = b.user_id;

 

4. 중첩 쿼리 피하고 뷰(View) / WITH절 활용하기

 

  • 자주 사용하는 복잡한 쿼리를 뷰(View)로 생성하고 필요한 곳에서 사용하기
-- 뷰 생성
CREATE VIEW `my_project.dataset.user_summary` AS
SELECT user_id, COUNT(order_id) AS order_count
FROM `my_project.dataset.order_data`
GROUP BY user_id;

-- 뷰를 사용하는 최적화된 쿼리
SELECT * 
FROM `my_project.dataset.user_summary`
WHERE order_count > 10;
  • 중첩 쿼리를 피하고 WITH 절을 사용하는 쿼리 스타일로 변경하기

👀최적화 방법 정리

 

최적화 방법 설명
불필요한 데이터 스캔 줄이기 WHERE 절로 스캔 범위 축소하여 SELECT * 대신 필요한 컬럼만 조회
파티션 및 클러스터링 PARTITION BY, CLUSTER BY
조인 최적화 조인 전에 필터링 수행
뷰(View)/WITH절 활용 복잡한 쿼리를 View, WITH로 재사용