ClickHouse Cloud와 BigQuery 비교
ClickHouse Cloud와 BigQuery 비교 (Comparing ClickHouse Cloud and BigQuery)
ClickHouse Cloud와 BigQuery의 리소스 구성 방식을 비교해 봐요. 리소스 계층, 데이터 타입, 쿼리 가속화 기법, 머티리얼라이즈드 뷰, 배열 처리 등 주요 차이점을 살펴봅니다.
출처: 문서
본문
리소스 구성 (Resource organization)
ClickHouse Cloud에서 리소스가 구성되는 방식은 BigQuery의 리소스 계층과 유사합니다. ClickHouse Cloud 리소스 계층을 보여주는 아래 다이어그램을 기반으로 특정 차이점을 설명합니다.
조직 (Organizations)
BigQuery와 유사하게, 조직은 ClickHouse 클라우드 리소스 계층의 루트 노드입니다. ClickHouse Cloud 계정에서 처음 설정하는 사용자는 자동으로 사용자 소유의 조직에 할당됩니다. 사용자는 조직에 추가 사용자를 초대할 수 있습니다.
BigQuery Projects vs ClickHouse Cloud Services
조직 안에서 BigQuery 프로젝트와 대략적으로 동등한 서비스를 만들 수 있습니다. ClickHouse Cloud에 저장된 데이터는 서비스와 연결되기 때문입니다. ClickHouse Cloud에는 여러 서비스 유형이 있습니다. 각 ClickHouse Cloud 서비스는 특정 리전에 배포되며 다음을 포함합니다:
- 컴퓨팅 노드 그룹(현재 Development 티어 서비스는 2개 노드, Production 티어 서비스는 3개). 이 노드들에 대해 ClickHouse Cloud는 수직 및 수평 스케일링을 수동 및 자동으로 지원합니다.
- 서비스가 모든 데이터를 저장하는 객체 스토리지 폴더.
- 엔드포인트(또는 ClickHouse Cloud UI 콘솔로 만든 여러 엔드포인트) — 서비스에 연결하기 위해 사용하는 서비스 URL(예:
https://dv2fzne24g.us-east-1.aws.clickhouse.cloud:8443)
BigQuery Datasets vs ClickHouse Cloud Databases
ClickHouse는 테이블을 논리적으로 데이터베이스로 그룹화합니다. BigQuery 데이터셋처럼, ClickHouse 데이터베이스는 테이블 데이터를 구성하고 접근을 제어하는 논리적 컨테이너입니다.
BigQuery Folders
ClickHouse Cloud에는 현재 BigQuery 폴더와 동등한 개념이 없습니다.
BigQuery Slot 예약과 할당량 (BigQuery Slot reservations and Quotas)
BigQuery slot 예약처럼, ClickHouse Cloud에서 수직 및 수평 자동 스케일링을 구성할 수 있습니다. 수직 자동 스케일링의 경우 서비스의 컴퓨팅 노드에 대해 메모리와 CPU 코어의 최소·최대 크기를 설정할 수 있습니다. 서비스는 그 범위 내에서 필요에 따라 스케일링됩니다. 이러한 설정은 초기 서비스 생성 흐름 중에도 사용할 수 있습니다. 서비스의 각 컴퓨팅 노드는 같은 크기를 가집니다. 수평 스케일링으로 서비스 내 컴퓨팅 노드 수를 변경할 수 있습니다.
또한 BigQuery 할당량과 유사하게, ClickHouse Cloud는 동시성 제어, 메모리 사용량 제한, I/O 스케줄링을 제공해 쿼리를 워크로드 클래스로 격리할 수 있게 해 줍니다. 특정 워크로드 클래스에 공유 리소스(CPU 코어, DRAM, 디스크 및 네트워크 I/O)에 대한 제한을 설정하면, 이 쿼리들이 다른 중요한 비즈니스 쿼리에 영향을 주지 않도록 보장합니다. 동시성 제어는 높은 수의 동시 쿼리 시나리오에서 스레드 과잉 할당(thread oversubscription)을 방지합니다.
ClickHouse는 서버, 사용자, 쿼리 수준에서 메모리 할당의 바이트 크기를 추적해 유연한 메모리 사용량 제한을 허용합니다. 메모리 오버커밋(overcommit)은 보장된 메모리를 넘어 추가 여유 메모리를 쿼리가 사용할 수 있게 하면서도, 다른 쿼리에 대해서는 메모리 제한을 보장합니다. 추가로 집계, 정렬, 조인 절에 대한 메모리 사용량을 제한할 수 있어, 메모리 제한을 초과하면 외부 알고리즘으로 대체할 수 있습니다.
마지막으로 I/O 스케줄링을 통해 최대 대역폭, 진행 중(in-flight) 요청, 정책에 따라 워크로드 클래스에 대한 로컬 및 원격 디스크 접근을 제한할 수 있습니다.
권한 (Permissions)
ClickHouse Cloud는 두 곳에서 사용자 접근을 제어합니다. 클라우드 콘솔과 데이터베이스를 통해서입니다. 콘솔 접근은 clickhouse.cloud 사용자 인터페이스로 관리됩니다. 데이터베이스 접근은 데이터베이스 사용자 계정과 역할로 관리됩니다. 추가로 콘솔 사용자에게 데이터베이스 내 역할이 부여될 수 있으며, 이를 통해 SQL 콘솔로 콘솔 사용자가 데이터베이스와 상호작용할 수 있습니다.
데이터 타입 (Data types)
ClickHouse는 숫자에 대해 더 세분화된 정밀도를 제공합니다. 예를 들어 BigQuery는 숫자 타입 INT64, NUMERIC, BIGNUMERIC, FLOAT64를 제공합니다. 이를 ClickHouse와 대조해 보세요. ClickHouse는 10진수, 부동소수점, 정수에 대해 여러 정밀도 타입을 제공합니다. 이러한 데이터 타입으로 저장 및 메모리 오버헤드를 최적화해 더 빠른 쿼리와 더 낮은 리소스 소비를 얻을 수 있습니다. 아래에서 각 BigQuery 타입에 대한 동등한 ClickHouse 타입을 매핑합니다:
ClickHouse 타입에 여러 옵션이 제시될 때 데이터의 실제 범위를 고려해 가장 낮은 것을 선택하세요. 또한 추가 압축을 위해 적절한 코덱 활용을 고려하세요.
쿼리 가속화 기법 (Query acceleration techniques)
Primary/외래 키와 Primary index (Primary and Foreign keys and Primary index)
BigQuery에서 테이블은 primary key와 foreign key 제약을 가질 수 있습니다. 일반적으로 primary/외래 키는 데이터 무결성을 보장하기 위해 관계형 데이터베이스에서 사용됩니다. primary key 값은 보통 각 행에 대해 고유하며 NULL이 아닙니다. 행의 각 foreign key 값은 primary key 테이블의 primary key 컬럼에 존재하거나 NULL이어야 합니다. BigQuery에서 이러한 제약은 강제되지 않지만, 쿼리 최적화기가 이 정보를 사용해 쿼리를 더 잘 최적화할 수 있습니다.
ClickHouse에서 테이블도 primary key를 가질 수 있습니다. BigQuery와 마찬가지로 ClickHouse는 테이블의 primary key 컬럼 값의 고유성을 강제하지 않습니다. BigQuery와 달리 테이블의 데이터는 primary key 컬럼으로 정렬되어 디스크에 저장됩니다. 쿼리 최적화기는 이 정렬 순서를 활용해 재정렬을 방지하고, 조인에 대한 메모리 사용량을 최소화하며, limit 절에 대한 단락(short-circuit)을 가능하게 합니다. BigQuery와 달리 ClickHouse는 primary key 컬럼 값을 기반으로 (희소한) primary index를 자동으로 만듭니다. 이 인덱스는 primary key 컬럼에 필터가 포함된 모든 쿼리를 가속화하는 데 사용됩니다. ClickHouse는 현재 foreign key 제약을 지원하지 않습니다.
보조 인덱스 (Secondary indexes) (ClickHouse에서만 사용 가능)
테이블의 primary key 컬럼 값으로 만든 primary index에 더해, ClickHouse는 primary key에 없는 다른 컬럼에 보조 인덱스를 만들 수 있게 합니다. ClickHouse는 다양한 타입의 보조 인덱스를 제공하며, 각각 다른 쿼리 유형에 적합합니다:
- Bloom Filter Index:
- 등호 조건(예: =, IN)이 있는 쿼리를 가속화하는 데 사용.
- 확률적 데이터 구조를 사용해 값이 데이터 블록에 존재하는지 판단.
- Token Bloom Filter Index:
- Bloom Filter Index와 유사하지만 토큰화된 문자열에 사용되며 전문(full-text) 검색 쿼리에 적합.
- Min-Max Index:
- 각 데이터 파츠에 대해 컬럼의 최솟값과 최댓값을 유지.
- 지정된 범위에 속하지 않는 데이터 파츠의 읽기를 건너뛰는 데 도움.
검색 인덱스 (Search indexes)
BigQuery의 검색 인덱스와 유사하게, ClickHouse 테이블에서 문자열 값을 가진 컬럼에 대한 전문(full-text) 인덱스를 만들 수 있습니다.
벡터 인덱스 (Vector indexes)
BigQuery는 최근 벡터 인덱스를 Pre-GA 기능으로 도입했습니다. 마찬가지로 ClickHouse는 벡터 검색 사용 사례를 가속화하는 인덱스에 대한 실험적 지원을 가지고 있습니다.
파티셔닝 (Partitioning)
BigQuery와 마찬가지로 ClickHouse는 테이블을 파티션이라는 더 작고 관리하기 쉬운 조각으로 나누어 대규모 테이블의 성능과 관리성을 향상시키는 테이블 파티셔닝을 사용합니다. ClickHouse 파티셔닝에 대한 자세한 설명은 여기에 있습니다.
클러스터링 (Clustering)
클러스터링을 통해 BigQuery는 지정된 몇 개 컬럼의 값을 기반으로 테이블 데이터를 자동으로 정렬하고 최적 크기의 블록에 함께 배치합니다. 클러스터링은 쿼리 성능을 향상시켜 BigQuery가 쿼리 실행 비용을 더 잘 추정할 수 있게 합니다. 클러스터된 컬럼으로 쿼리는 불필요한 데이터 스캔도 제거합니다.
ClickHouse에서 데이터는 테이블의 primary key 컬럼을 기반으로 디스크에 자동으로 클러스터링되며, primary index 데이터 구조를 활용하는 쿼리가 빠르게 찾거나 프루닝할 수 있는 블록으로 논리적으로 구성됩니다.
머티리얼라이즈드 뷰 (Materialized views)
BigQuery와 ClickHouse 모두 머티리얼라이즈드 뷰 — 성능과 효율 향상을 위해 기본 테이블에 대한 변환 쿼리 결과를 기반으로 미리 계산된 결과 — 를 지원합니다.
머티리얼라이즈드 뷰 쿼리 (Querying materialized views)
BigQuery 머티리얼라이즈드 뷰는 직접 쿼리하거나 최적화기가 기본 테이블에 대한 쿼리를 처리하는 데 사용할 수 있습니다. 기본 테이블의 변경이 머티리얼라이즈드 뷰를 무효화할 수 있으면 데이터는 기본 테이블에서 직접 읽힙니다. 기본 테이블의 변경이 머티리얼라이즈드 뷰를 무효화하지 않으면 나머지 데이터는 머티리얼라이즈드 뷰에서 읽히고 변경분만 기본 테이블에서 읽힙니다.
ClickHouse에서 머티리얼라이즈드 뷰는 직접 쿼리만 할 수 있습니다. 다만 기본 테이블 변경 후 5분 이내에(하지만 30분보다 더 자주는 아님) 자동으로 새로고침되는 BigQuery와 달리, 머티리얼라이즈드 뷰는 항상 기본 테이블과 동기화되어 있습니다.
머티리얼라이즈드 뷰 업데이트하기
BigQuery는 기본 테이블에 대해 뷰의 변환 쿼리를 실행해 머티리얼라이즈드 뷰를 주기적으로 완전히 새로고침합니다. 새로고침 사이에 BigQuery는 머티리얼라이즈드 뷰의 데이터와 새 기본 테이블 데이터를 결합해 머티리얼라이즈드 뷰를 사용하면서도 일관된 쿼리 결과를 제공합니다.
ClickHouse에서 머티리얼라이즈드 뷰는 증분적으로 업데이트됩니다. 이 증분 업데이트 메커니즘은 높은 확장성과 낮은 컴퓨팅 비용을 제공합니다: 증분 업데이트된 머티리얼라이즈드 뷰는 기본 테이블에 수십억, 수조 개의 행이 있는 시나리오를 위해 특히 설계되었습니다. 머티리얼라이즈드 뷰를 새로고침하기 위해 계속 커지는 기본 테이블을 반복해서 쿼리하는 대신, ClickHouse는 (오직) 새로 삽입된 기본 테이블 행의 값에서 부분 결과를 계산하기만 합니다. 이 부분 결과는 백그라운드에서 이전에 계산된 부분 결과와 증분적으로 병합됩니다. 이는 전체 기본 테이블에서 머티리얼라이즈드 뷰를 반복적으로 새로고침하는 것에 비해 컴퓨팅 비용을 획기적으로 낮춥니다.
트랜잭션 (Transactions)
ClickHouse와 대조적으로 BigQuery는 단일 쿼리 안에서, 또는 세션을 사용할 때 여러 쿼리에 걸쳐 다중 문 트랜잭션을 지원합니다. 다중 문 트랜잭션을 사용하면 하나 이상의 테이블에서 행 삽입, 삭제 같은 변경 작업을 수행하고 변경 사항을 원자적으로 커밋하거나 롤백할 수 있습니다. 다중 문 트랜잭션은 ClickHouse의 2024 로드맵에 있습니다.
집계 함수 (Aggregate functions)
BigQuery와 비교해 ClickHouse에는 내장 집계 함수가 훨씬 더 많습니다:
- BigQuery에는 18개의 집계 함수와 4개의 근사 집계 함수가 있습니다.
- ClickHouse에는 150개 이상의 사전 구축 집계 함수가 있으며, 사전 구축 집계 함수의 동작을 확장하기 위한 강력한 집계 결합자(aggregation combinators)도 있습니다. 예를 들어 150개 이상의 사전 구축 집계 함수를 테이블 행 대신 배열에 적용할 수 있습니다. -Array 접미사로 호출하기만 하면 됩니다. -Map 접미사로는 어떤 집계 함수든 맵에 적용할 수 있습니다. -ForEach 접미사로는 어떤 집계 함수든 중첩 배열에 적용할 수 있습니다.
데이터 소스와 파일 형식 (Data sources and file formats)
BigQuery와 비교해 ClickHouse는 훨씬 더 많은 파일 형식과 데이터 소스를 지원합니다:
- ClickHouse는 사실상 모든 데이터 소스에서 90개 이상의 파일 형식으로 데이터를 로드하는 것을 네이티브로 지원합니다
- BigQuery는 5개의 파일 형식과 19개의 데이터 소스를 지원합니다
SQL 언어 기능 (SQL language features)
ClickHouse는 많은 확장과 개선을 가진 표준 SQL을 제공해 분석 작업에 더 친화적입니다. 예를 들어 ClickHouse SQL은 람다 함수와 고차 함수를 지원하므로, 변환을 적용할 때 배열을 unnest/explode하지 않아도 됩니다. 이는 BigQuery 같은 다른 시스템에 비해 큰 장점입니다.
배열 (Arrays)
BigQuery의 8개 배열 함수와 비교해 ClickHouse에는 광범위한 문제를 우아하고 간단하게 모델링하고 해결하기 위한 80개 이상의 내장 배열 함수가 있습니다.
ClickHouse의 대표적인 설계 패턴은 groupArray 집계 함수를 사용해 테이블의 특정 행 값을 (임시로) 배열로 변환하는 것입니다. 그러면 이를 배열 함수로 편리하게 처리하고, arrayJoin 집계 함수로 결과를 다시 개별 테이블 행으로 변환할 수 있습니다.
ClickHouse SQL이 고차 람다 함수를 지원하므로, BigQuery에서 자주 요구되는 것처럼 배열을 일시적으로 테이블로 다시 변환하는 대신, 배열 필터링이나 배열 지퍼(zipping) 같은 많은 고급 배열 작업을 내장 고차 함수 중 하나를 호출하기만 하면 달성할 수 있습니다. ClickHouse에서 이러한 작업은 고차 함수 arrayFilter와 arrayZip의 단순 함수 호출일 뿐입니다.
다음에서 BigQuery에서 ClickHouse로 배열 작업의 매핑을 제공합니다:
| BigQuery | ClickHouse |
|---|---|
| ARRAY_CONCAT | arrayConcat |
| ARRAY_LENGTH | length |
| ARRAY_REVERSE | arrayReverse |
| ARRAY_TO_STRING | arrayStringConcat |
| GENERATE_ARRAY | range |
서브쿼리의 각 행에 하나의 요소를 가진 배열 만들기 BigQuery ARRAY 함수
SELECT ARRAY
(SELECT 1 UNION ALL
SELECT 2 UNION ALL
SELECT 3) AS new_array;
/*-----------*
| new_array |
+-----------+
| [1, 2, 3] |
*-----------*/
ClickHouse groupArray 집계 함수
SELECT groupArray(*) AS new_array
FROM
(
SELECT 1
UNION ALL
SELECT 2
UNION ALL
SELECT 3
)
┌─new_array─┐
1. │ [1,2,3] │
└───────────┘
배열을 행 집합으로 변환하기
BigQuery
UNNEST 연산자
SELECT *
FROM UNNEST(['foo', 'bar', 'baz', 'qux', 'corge', 'garply', 'waldo', 'fred'])
AS element
WITH OFFSET AS offset
ORDER BY offset;
/*----------+--------*
| element | offset |
+----------+--------+
| foo | 0 |
| bar | 1 |
| baz | 2 |
| qux | 3 |
| corge | 4 |
| garply | 5 |
| waldo | 6 |
| fred | 7 |
*----------+--------*/
ClickHouse ARRAY JOIN 절
WITH ['foo', 'bar', 'baz', 'qux', 'corge', 'garply', 'waldo', 'fred'] AS values
SELECT element, num-1 AS offset
FROM (SELECT values AS element) AS subquery
ARRAY JOIN element, arrayEnumerate(element) AS num;
/*----------+--------*
| element | offset |
+----------+--------+
| foo | 0 |
| bar | 1 |
| baz | 2 |
| qux | 3 |
| corge | 4 |
| garply | 5 |
| waldo | 6 |
| fred | 7 |
*----------+--------*/
날짜 배열 반환하기 BigQuery GENERATE_DATE_ARRAY 함수
SELECT GENERATE_DATE_ARRAY('2016-10-05', '2016-10-08') AS example;
/*--------------------------------------------------*
| example |
+--------------------------------------------------+
| [2016-10-05, 2016-10-06, 2016-10-07, 2016-10-08] |
*--------------------------------------------------*/
range + arrayMap 함수 ClickHouse
SELECT arrayMap(x -> (toDate('2016-10-05') + x), range(toUInt32((toDate('2016-10-08') - toDate('2016-10-05')) + 1))) AS example
┌─example───────────────────────────────────────────────┐
1. │ ['2016-10-05','2016-10-06','2016-10-07','2016-10-08'] │
└───────────────────────────────────────────────────────┘
타임스탬프 배열 반환하기 BigQuery GENERATE_TIMESTAMP_ARRAY 함수
SELECT GENERATE_TIMESTAMP_ARRAY('2016-10-05 00:00:00', '2016-10-07 00:00:00',
INTERVAL 1 DAY) AS timestamp_array;
/*--------------------------------------------------------------------------*
| timestamp_array |
+--------------------------------------------------------------------------+
| [2016-10-05 00:00:00+00, 2016-10-06 00:00:00+00, 2016-10-07 00:00:00+00] |
*--------------------------------------------------------------------------*/
ClickHouse range + arrayMap 함수
SELECT arrayMap(x -> (toDateTime('2016-10-05 00:00:00') + toIntervalDay(x)), range(dateDiff('day', toDateTime('2016-10-05 00:00:00'), toDateTime('2016-10-07 00:00:00')) + 1)) AS timestamp_array
Query id: b324c11f-655b-479f-9337-f4d34fd02190
┌─timestamp_array─────────────────────────────────────────────────────┐
1. │ ['2016-10-05 00:00:00','2016-10-06 00:00:00','2016-10-07 00:00:00'] │
└─────────────────────────────────────────────────────────────────────┘
배열 필터링하기
BigQuery
UNNEST 연산자로 배열을 일시적으로 테이블로 다시 변환해야 함
WITH Sequences AS
(SELECT [0, 1, 1, 2, 3, 5] AS some_numbers
UNION ALL SELECT [2, 4, 8, 16, 32] AS some_numbers
UNION ALL SELECT [5, 10] AS some_numbers)
SELECT
ARRAY(SELECT x * 2
FROM UNNEST(some_numbers) AS x
WHERE x < 5) AS doubled_less_than_five
FROM Sequences;
/*------------------------*
| doubled_less_than_five |
+------------------------+
| [0, 2, 2, 4, 6] |
| [4, 8] |
| [] |
*------------------------*/
ClickHouse arrayFilter 함수
WITH Sequences AS
(
SELECT [0, 1, 1, 2, 3, 5] AS some_numbers
UNION ALL
SELECT [2, 4, 8, 16, 32] AS some_numbers
UNION ALL
SELECT [5, 10] AS some_numbers
)
SELECT arrayMap(x -> (x * 2), arrayFilter(x -> (x < 5), some_numbers)) AS doubled_less_than_five
FROM Sequences;
┌─doubled_less_than_five─┐
1. │ [0,2,2,4,6] │
└────────────────────────┘
┌─doubled_less_than_five─┐
2. │ [] │
└────────────────────────┘
┌─doubled_less_than_five─┐
3. │ [4,8] │
└────────────────────────┘
배열 지퍼(zipping)
BigQuery
UNNEST 연산자로 배열을 일시적으로 테이블로 다시 변환해야 함
WITH
Combinations AS (
SELECT
['a', 'b'] AS letters,
[1, 2, 3] AS numbers
)
SELECT
ARRAY(
SELECT AS STRUCT
letters[SAFE_OFFSET(index)] AS letter,
numbers[SAFE_OFFSET(index)] AS number
FROM Combinations
CROSS JOIN
UNNEST(
GENERATE_ARRAY(
0,
LEAST(ARRAY_LENGTH(letters), ARRAY_LENGTH(numbers)) - 1)) AS index
ORDER BY index
);
/*------------------------------*
| pairs |
+------------------------------+
| [{ letter: "a", number: 1 }, |
| { letter: "b", number: 2 }] |
*------------------------------*/
ClickHouse arrayZip 함수
WITH Combinations AS
(
SELECT
['a', 'b'] AS letters,
[1, 2, 3] AS numbers
)
SELECT arrayZip(letters, arrayResize(numbers, length(letters))) AS pairs
FROM Combinations;
┌─pairs─────────────┐
1. │ [('a',1),('b',2)] │
└───────────────────┘
배열 집계하기
BigQuery
UNNEST 연산자로 배열을 테이블로 다시 변환해야 함
WITH Sequences AS
(SELECT [0, 1, 1, 2, 3, 5] AS some_numbers
UNION ALL SELECT [2, 4, 8, 16, 32] AS some_numbers
UNION ALL SELECT [5, 10] AS some_numbers)
SELECT some_numbers,
(SELECT SUM(x)
FROM UNNEST(s.some_numbers) AS x) AS sums
FROM Sequences AS s;
/*--------------------+------*
| some_numbers | sums |
+--------------------+------+
| [0, 1, 1, 2, 3, 5] | 12 |
| [2, 4, 8, 16, 32] | 62 |
| [5, 10] | 15 |
*--------------------+------*/
ClickHouse arraySum, arrayAvg, … 함수, 또는 arrayReduce 함수의 인자로 90개 이상의 기존 집계 함수 이름 중 아무거나
WITH Sequences AS
(
SELECT [0, 1, 1, 2, 3, 5] AS some_numbers
UNION ALL
SELECT [2, 4, 8, 16, 32] AS some_numbers
UNION ALL
SELECT [5, 10] AS some_numbers
)
SELECT
some_numbers,
arraySum(some_numbers) AS sums
FROM Sequences;
┌─some_numbers──┬─sums─┐
1. │ [0,1,1,2,3,5] │ 12 │
└───────────────┴──────┘
┌─some_numbers──┬─sums─┐
2. │ [2,4,8,16,32] │ 62 │
└───────────────┴──────┘
┌─some_numbers─┬─sums─┐
3. │ [5,10] │ 15 │
└──────────────┴──────┘