BigQuery 대신 ClickHouse Cloud를 사용하는 이유
BigQuery에서 ClickHouse Cloud로 데이터 가져오기
데이터셋
post, vote, user, comment, badge가 포함되어 있습니다. 이 데이터의 BigQuery 스키마는 아래와 같습니다.
이 데이터셋을 BigQuery 인스턴스에 적재하여 마이그레이션 단계를 테스트하려는 사용자를 위해, 해당 테이블의 데이터를 Parquet 포맷으로 GCS 버킷에 제공하고 있습니다. 또한 BigQuery에서 테이블을 생성하고 로드하는 DDL 명령은 여기에서 확인할 수 있습니다.
데이터 마이그레이션
- 주기적 업데이트가 있는 초기 대량 적재 - 초기 데이터셋을 마이그레이션한 뒤, 정해진 인터벌(예: 매일)마다 주기적으로 업데이트해야 합니다. 여기서 업데이트는 변경된 행을 다시 전송하는 방식으로 처리하며, 변경 여부는 비교에 사용할 수 있는 컬럼(예: 날짜)으로 식별합니다. 삭제는 데이터셋 전체를 주기적으로 다시 로드하는 방식으로 처리합니다.
- 실시간 복제 또는 CDC - 초기 데이터셋을 마이그레이션해야 합니다. 이후 이 데이터셋의 변경 사항은 수초 수준의 지연만 허용되는 거의 실시간으로 ClickHouse에 반영되어야 합니다. 이는 사실상 Change Data Capture (CDC) 프로세스이며, BigQuery의 테이블을 ClickHouse와 동기화해야 한다는 의미입니다. 즉, BigQuery 테이블의 삽입, 업데이트, 삭제가 ClickHouse의 해당 테이블에도 적용되어야 합니다.
Google Cloud Storage (GCS)를 통한 대량 적재
- 7개의 테이블을 GCS로 내보내십시오. 필요한 명령은 여기에서 확인할 수 있습니다.
-
데이터를 ClickHouse Cloud로 가져오십시오. 이를 위해 gcs 테이블 함수를 사용할 수 있습니다. DDL 및 가져오기 쿼리는 여기에서 확인할 수 있습니다. ClickHouse Cloud 인스턴스는 여러 컴퓨트 노드로 구성되므로
gcs테이블 함수 대신 s3Cluster 테이블 함수를 사용합니다. 이 함수는 GCS 버킷에서도 사용할 수 있으며, 데이터를 병렬로 적재하기 위해 ClickHouse Cloud 서비스의 모든 노드를 활용합니다.
- BigQuery의 내보내기 기능은 데이터의 부분 집합을 내보낼 수 있도록 필터를 지원합니다.
- BigQuery는 Parquet, Avro, JSON, CSV 포맷과 여러 압축 유형으로 내보내기를 지원하며, 이 모두를 ClickHouse에서 지원합니다.
- GCS는 객체 수명 주기 관리를 지원하므로, ClickHouse로 내보내고 가져온 데이터를 지정된 기간이 지난 후 삭제할 수 있습니다.
- Google은 하루 최대 50TB까지 GCS로 무료 내보내기를 허용합니다. 사용자는 GCS 스토리지 비용만 지불하면 됩니다.
- 내보내기를 수행하면 자동으로 여러 파일이 생성되며, 각 파일은 최대 1GB의 테이블 데이터로 제한됩니다. 이는 가져오기를 병렬화할 수 있으므로 ClickHouse에 유리합니다.
예약된 쿼리를 통한 실시간 복제 또는 CDC
스키마 설계
posts 테이블에 집중합니다. 이에 대한 BigQuery 스키마는 아래와 같습니다:
타입 최적화
INSERT INTO SELECT를 사용해 간단히 채울 수 있으며, gcs 테이블 함수로 gcs에 내보낸 데이터를 읽어옵니다. ClickHouse Cloud에서는 gcs와 호환되는 s3Cluster 테이블 함수도 사용해 여러 노드에서 적재를 병렬화할 수 있습니다:
ClickHouse 프라이머리 키는 어떻게 다릅니까?
- ClickHouse가 주로 사용되는 규모에서는 메모리와 디스크 효율성이 매우 중요합니다. 데이터는 파트라고 하는 청크 단위로 ClickHouse 테이블에 기록되며, 백그라운드에서 이 파트들을 머지하는 규칙이 적용됩니다. ClickHouse에서는 각 파트가 자체 프라이머리 인덱스를 가집니다. 파트가 머지되면 병합된 파트의 프라이머리 인덱스도 함께 머지됩니다. 다만 이러한 인덱스는 각 행마다 생성되지 않습니다. 대신 파트의 프라이머리 인덱스에는 각 행 그룹마다 하나의 인덱스 엔트리가 있습니다. 이 기법을 희소 인덱싱이라고 합니다.
- 희소 인덱싱이 가능한 이유는 ClickHouse가 파트의 행을 지정된 키 순서대로 정렬해 디스크에 저장하기 때문입니다. 희소 프라이머리 인덱스는 단일 행을 직접 찾는 대신(B-Tree 기반 인덱스처럼), 쿼리와 일치할 가능성이 있는 행 그룹을 빠르게 식별할 수 있게 해줍니다(인덱스 엔트리에 대해 이진 검색 수행). 이렇게 찾아낸 잠재적 일치 행 그룹은 실제 일치 항목을 찾기 위해 병렬로 ClickHouse engine에 순서대로 스트리밍됩니다. 이러한 인덱스 설계 덕분에 프라이머리 인덱스는 작게 유지할 수 있으며(전체가 main memory에 완전히 들어감), 동시에 쿼리 실행 시간을 크게 단축할 수 있습니다. 특히 데이터 분석 사용 사례에서 흔한 범위 쿼리에서 효과적입니다. 자세한 내용은 이 심층 가이드를 참조하십시오.
테이블의 모든 컬럼은 키 자체에 포함되어 있는지와 관계없이 지정된 순서 지정 키의 값을 기준으로 정렬됩니다. 예를 들어CreationDate를 키로 사용하면 다른 모든 컬럼의 값 순서도CreationDate컬럼의 값 순서를 따르게 됩니다. 여러 개의 순서 지정 키를 지정할 수도 있으며, 이 경우SELECT쿼리의ORDER BY절과 동일한 의미로 정렬됩니다.
순서 지정 키 선택하기
데이터 모델링 기법
파티션
PARTITION BY 절로 파티셔닝을 지정합니다. 이 절에는 하나 이상의 컬럼에 대한 SQL 표현식을 포함할 수 있으며, 그 결과에 따라 각 행이 어느 파티션으로 전송될지가 결정됩니다.
데이터 파트는 디스크에서 각 파티션과 논리적으로 연결되며, 각각 독립적으로 쿼리할 수 있습니다. 아래 예시에서는 toYear(CreationDate) 표현식을 사용해 posts 테이블을 연도별로 파티셔닝합니다. 행이 ClickHouse에 삽입되면 이 표현식이 각 행에 대해 평가되며, 이후 각 행은 해당 파티션에 속하는 새로운 데이터 파트 형태로 결과 파티션에 배치됩니다.
적용 사례
- 데이터 관리 - ClickHouse에서는 파티셔닝을 쿼리 최적화 기법이 아니라, 우선적으로 데이터 관리 기능으로 보아야 합니다. 키를 기준으로 데이터를 논리적으로 분리하면 각 파티션을 독립적으로 작업할 수 있으며, 예를 들어 삭제할 수 있습니다. 이를 통해 시간 기준으로 파티션, 즉 부분 집합을 스토리지 계층 간에 효율적으로 이동하거나 데이터를 만료시키거나 클러스터에서 효율적으로 삭제할 수 있습니다. 아래 예시에서는 2008년의 게시물을 제거합니다:
- 쿼리 최적화 - 파티션은 쿼리 성능 향상에 도움이 될 수 있지만, 이는 액세스 패턴에 크게 좌우됩니다. 쿼리가 소수의 파티션만 대상으로 하는 경우(이상적으로는 1개), 성능이 향상될 수 있습니다. 이는 일반적으로 파티셔닝 키가 프라이머리 키에 포함되어 있지 않고, 해당 키로 필터링하는 경우에만 유용합니다. 반면 많은 파티션을 대상으로 해야 하는 쿼리는 파티셔닝을 사용하지 않을 때보다 성능이 더 나빠질 수 있습니다(파티셔닝으로 인해 파트 수가 더 많아질 수 있기 때문입니다). 또한 파티셔닝 키가 이미 프라이머리 키의 앞부분에 있다면, 단일 파티션만 대상으로 할 때의 이점은 매우 미미하거나 사실상 없을 수 있습니다. 또한 각 파티션 내 값이 고유하다면 파티셔닝을 사용해
GROUP BY쿼리 최적화도 할 수 있습니다. 그러나 일반적으로는 먼저 프라이머리 키가 최적화되어 있는지 확인하고, 파티셔닝은 액세스 패턴이 하루 중 예측 가능한 특정 부분 집합만 대상으로 하는 예외적인 경우에만 쿼리 최적화 기법으로 고려해야 합니다. 예를 들어 일 단위로 파티셔닝하고 대부분의 쿼리가 최근 1일치 데이터만 조회하는 경우가 이에 해당합니다.
권장 사항
ORDER BY 표현식의 첫 번째 컬럼으로 두십시오.
내부적으로 ClickHouse는 삽입된 데이터에 대해 파트를 생성합니다. 더 많은 데이터가 삽입될수록 파트 수는 증가합니다. 쿼리 성능을 저하시킬 수 있는 과도한 파트 수(읽어야 할 파일 수가 더 많아지기 때문입니다)를 방지하기 위해, 파트는 백그라운드 비동기 프로세스에서 함께 머지됩니다. 파트 수가 미리 구성된 한도를 초과하면 ClickHouse는 삽입 시 “too many parts” 오류와 함께 예외를 발생시킵니다. 이는 정상적인 운영에서는 발생하지 않아야 하며, ClickHouse가 잘못 구성되었거나 잘못 사용된 경우(예: 작은 삽입이 너무 많은 경우)에만 발생합니다. 파트는 각 파티션마다 서로 독립적으로 생성되므로, 파티션 수가 증가하면 파트 수도 증가합니다. 즉, 파티션 수의 배수가 됩니다. 따라서 카디널리티가 높은 파티셔닝 키는 이 오류를 유발할 수 있으므로 피해야 합니다.
구체화된 뷰(Materialized View)와 PROJECTION
ORDER BY 절을 지정할 수 있습니다.
ClickHouse data modeling에서는 ClickHouse에서 구체화된 뷰(Materialized View)를 사용해
집계를 미리 계산하고, 행을 변환하며, 서로 다른 액세스 패턴에 맞게 쿼리를
최적화하는 방법을 살펴봅니다. 이와 관련해 예시를 소개했습니다. 여기서는
구체화된 뷰가 삽입을 받는 원본 테이블과 다른 순서 지정 키를 가진 대상 테이블로
행을 전달합니다.
예를 들어, 다음 쿼리를 살펴보겠습니다:
UserId가
순서 지정 키가 아니기 때문에 9천만 개의 행을 모두 스캔해야 합니다(그래도 빠르게 수행되기는 합니다).
이전에는 PostId lookup 역할을 하는 구체화된 뷰(materialized view)로 이 문제를 해결했습니다.
같은 문제는 PROJECTION으로도 해결할 수 있습니다.
아래 명령은 ORDER BY user_id가 있는 PROJECTION을 추가합니다.
ALTER 명령으로 생성한 경우, MATERIALIZE PROJECTION 명령을
실행하면 생성은 비동기적으로 수행됩니다. 다음 쿼리로 이 작업의 진행 상태를
확인할 수 있으며, is_done=1이 될 때까지 기다리십시오.
EXPLAIN 명령을 사용하면 이 쿼리를 실행할 때 PROJECTION이 사용되었는지도 확인할 수 있습니다:
PROJECTION을 사용해야 하는 경우
- 데이터의 전체적인 재정렬이 필요한 경우입니다. 이론적으로는 PROJECTION의 표현식에서
GROUP BY,를 사용할 수 있지만, 집계를 유지하는 데에는 구체화된 뷰가 더 효과적입니다. 또한 쿼리 최적화기는SELECT * ORDER BY x처럼 단순한 재정렬을 사용하는 PROJECTION을 활용할 가능성이 더 높습니다. 저장 공간 사용량을 줄이기 위해 이 표현식에서 컬럼의 부분 집합만 선택할 수도 있습니다. - 저장 공간 사용량 증가와 데이터를 두 번 기록하는 오버헤드를 감수할 수 있는 경우입니다. 삽입 속도에 미치는 영향을 테스트하고 저장 공간 오버헤드도 평가하십시오.
ClickHouse에서 BigQuery 쿼리 재작성하기
ClickHouse
집계 함수
argMax 함수를 사용하는 예를 보여줍니다.
BigQuery
ClickHouse
조건식과 배열
HAVING 및 SELECT 절에서 별칭을 재사용할 수 있는 기능 덕분에 얼마나 간결한지 확인해 보십시오.
BigQuery
ClickHouse