메인 콘텐츠로 건너뛰기
이 문서는 VLDB 2024 학술 논문의 웹 버전입니다. 또한 논문의 배경과 집필 과정에 관한 블로그 글도 게시했으며, ClickHouse CTO이자 창시자인 Alexey Milovidov의 VLDB 2024 발표를 시청할 것을 권장합니다:

초록

지난 수십 년 동안 저장 및 분석되는 데이터의 양은 기하급수적으로 증가했습니다. 산업과 분야를 막론하고 기업들은 제품 개선, 성과 평가, 그리고 비즈니스에 중대한 의사결정을 내리기 위해 이러한 데이터에 의존하기 시작했습니다. 그러나 데이터 규모가 점차 인터넷 규모로 커지면서, 기업들은 과거 데이터와 신규 데이터를 비용 효율적이면서도 확장 가능한 방식으로 관리하는 동시에, 많은 수의 동시 쿼리를 처리하고 실시간에 가까운 지연 시간(예: 사용 사례에 따라 1초 미만)으로 분석해야 하게 되었습니다. 이 논문은 높은 수집 속도로 페타바이트 규모의 데이터 세트에 대해 고성능 분석을 수행하도록 설계된, 널리 사용되는 오픈소스 OLAP 데이터베이스인 ClickHouse를 개괄적으로 소개합니다. ClickHouse의 스토리지 계층은 전통적인 로그 구조 머지(LSM) 트리를 기반으로 하는 데이터 포맷과, 과거 데이터를 백그라운드에서 지속적으로 변환(예: 집계, 아카이빙)하는 새로운 기법을 결합합니다. 쿼리는 사용하기 편리한 SQL 방언으로 작성되며, 선택적 코드 컴파일을 지원하는 최첨단 벡터화된 쿼리 실행 엔진에서 처리됩니다. ClickHouse는 쿼리 처리 시 관련 없는 데이터를 평가하지 않도록 프루닝 기법을 적극적으로 활용합니다. 다른 데이터 관리 시스템은 테이블 함수, 테이블 엔진 또는 데이터베이스 엔진 수준에서 통합할 수 있습니다. 실제 벤치마크는 ClickHouse가 시장에서 가장 빠른 분석형 데이터베이스 중 하나임을 보여줍니다.

1 소개

이 논문에서는 수조 개의 행과 수백 개의 컬럼을 가진 테이블에서 고성능 분석 쿼리를 수행하도록 설계된 열 지향 OLAP 데이터베이스인 ClickHouse를 설명합니다. ClickHouse는 2009년에 웹 규모의 로그 파일 데이터를 위한 필터 및 집계 연산자로 시작되었고, 2016년에 오픈 소스로 공개되었습니다. 그림 1은 이 논문에서 설명하는 주요 기능이 ClickHouse에 언제 도입되었는지를 보여줍니다. ClickHouse는 현대 분석 데이터 관리의 다섯 가지 핵심 과제를 해결하도록 설계되었습니다.
  1. 높은 수집 속도의 방대한 데이터 세트. 웹 분석, 금융, 전자상거래와 같은 산업의 많은 데이터 기반 애플리케이션은 방대하면서도 지속적으로 증가하는 데이터가 특징입니다. 이러한 대규모 데이터 세트를 처리하려면 분석 데이터베이스는 효율적인 인덱싱과 압축 전략을 제공해야 할 뿐만 아니라, 단일 서버의 저장 용량이 수십 테라바이트 수준으로 제한되므로 여러 노드에 데이터를 분산(scale-out)할 수 있어야 합니다. 또한 최신 데이터는 과거 데이터보다 실시간 인사이트에 더 중요한 경우가 많습니다. 따라서 분석 데이터베이스는 새 데이터를 지속적으로 높은 속도로 또는 버스트 형태로 수집할 수 있어야 하며, 동시에 병렬 보고 쿼리의 속도를 늦추지 않으면서 과거 데이터의 우선순위를 지속적으로 “낮출”(예: 집계, 아카이브) 수 있어야 합니다.
  2. 낮은 지연 시간을 요구하는 다수의 동시 쿼리. 쿼리는 일반적으로 애드혹(예: 탐색적 데이터 분석) 또는 반복형(예: 주기적인 dashboard 쿼리)으로 분류할 수 있습니다. 사용 사례의 상호작용성이 높을수록 더 낮은 쿼리 지연 시간이 요구되며, 이는 쿼리 최적화와 실행 측면에서 과제를 야기합니다. 반복형 쿼리는 물리적 데이터베이스 레이아웃을 workload에 맞게 조정할 기회도 제공합니다. 따라서 데이터베이스는 빈번한 쿼리를 최적화할 수 있는 프루닝 기법을 제공해야 합니다. 또한 쿼리 우선순위에 따라, 많은 수의 쿼리가 동시에 실행되더라도 CPU, 메모리, 디스크, 네트워크 I/O와 같은 공유 시스템 자원에 대해 동등하거나 우선적인 접근을 grant할 수 있어야 합니다.
  3. 다양한 데이터 저장소, 저장 위치, 포맷 환경. 기존 데이터 아키텍처와 통합하려면 현대 분석 데이터베이스는 어떤 시스템, 위치, 포맷에 있는 외부 데이터라도 읽고 쓸 수 있도록 높은 수준의 개방성을 제공해야 합니다.
  4. 성능 내부 검사를 지원하는 편리한 쿼리 언어. 실제 OLAP 데이터베이스 사용 환경에서는 추가적인 “비기능적” 요구사항도 제기됩니다. 예를 들어 사용자는 틈새 프로그래밍 언어보다는 중첩된 데이터 타입과 폭넓은 일반 함수, 집계 함수, 윈도우 함수를 지원하는 표현력 있는 SQL 방언으로 데이터베이스와 상호작용하는 것을 선호하는 경우가 많습니다. 또한 분석 데이터베이스는 시스템 전체 또는 개별 쿼리의 성능을 내부 검사할 수 있는 정교한 도구도 제공해야 합니다.
  5. 산업 수준의 견고성과 유연한 배포. 범용 하드웨어는 신뢰성이 낮기 때문에, 데이터베이스는 노드 장애에 대비한 견고성을 위해 데이터 복제를 제공해야 합니다. 또한 데이터베이스는 오래된 노트북부터 고성능 서버까지 어떤 하드웨어에서도 실행되어야 합니다. 마지막으로 JVM 기반 프로그램의 garbage collection 오버헤드를 피하고 베어메탈 수준의 성능(예: SIMD)을 구현하려면, 데이터베이스는 이상적으로 대상 플랫폼용 네이티브 바이너리로 배포되어야 합니다.
그림 1: ClickHouse 타임라인.

2 아키텍처

그림 2: ClickHouse 데이터베이스 엔진의 상위 수준 아키텍처.
그림 2에서 볼 수 있듯이 ClickHouse 엔진은 세 가지 주요 계층으로 나뉩니다. 즉, 쿼리 처리 레이어(4절 4) 참조), 스토리지 계층(3절 3), 통합 계층(5절 5)입니다. 이들 외에도 별도의 액세스 계층이 사용자 세션과 다양한 protocol을 통한 애플리케이션 통신을 관리합니다. 또한 스레딩, 캐싱, 역할 기반 접근 제어, 백업, 지속적인 모니터링을 위한 직교 구성 요소도 있습니다. ClickHouse는 의존성 없이 단일 정적 링크 바이너리로 C++로 작성되었습니다. 쿼리 처리는 입력된 쿼리를 파싱하고, 논리 및 물리 쿼리 계획을 수립하고 최적화한 다음 실행하는 전통적인 패러다임을 따릅니다. ClickHouse는 MonetDB/X100 [11]과 유사한 벡터화 실행 모델을 기회주의적 코드 컴파일 [53]과 함께 사용합니다. 쿼리는 기능이 풍부한 SQL 방언, PRQL [76], 또는 Kusto의 KQL [50]로 작성할 수 있습니다. 스토리지 계층은 테이블 데이터의 포맷과 위치를 캡슐화하는 여러 테이블 엔진으로 구성됩니다. 테이블 엔진은 세 가지 범주로 나뉩니다. 첫 번째 범주는 ClickHouse의 기본 영속성 포맷을 나타내는 MergeTree* 계열의 테이블 엔진입니다. LSM 트리 [60] 개념을 기반으로 테이블은 수평으로 정렬된 파트로 분할되며, 이 파트들은 백그라운드 프로세스에 의해 지속적으로 머지됩니다. 개별 MergeTree* 테이블 엔진은 머지 시 입력 파트의 행을 결합하는 방식이 서로 다릅니다. 예를 들어 오래된 행은 집계되거나 대체될 수 있습니다. 두 번째 범주는 쿼리 실행을 가속하거나 분산하기 위해 사용되는 특수 목적 테이블 엔진입니다. 이 범주에는 딕셔너리라고 하는 인메모리 키-값 테이블 엔진이 포함됩니다. 딕셔너리는 내부 또는 외부 데이터 소스에 대해 주기적으로 실행되는 쿼리 결과를 캐시합니다. 이는 어느 정도의 staleness를 허용할 수 있는 시나리오에서 액세스 지연 시간을 크게 줄여 줍니다. 특수 목적 테이블 엔진의 다른 예로는 임시 테이블에 사용되는 순수 인메모리 엔진과 투명한 데이터 세그먼트 분할을 위한 분산 테이블 엔진이 있습니다(아래 참조). 세 번째 범주의 테이블 엔진은 관계형 데이터베이스(예: PostgreSQL, MySQL), publish/subscribe 시스템(예: Kafka, RabbitMQ [24]), 또는 키/값 저장소(예: Redis) 같은 외부 시스템과 양방향으로 데이터를 교환하기 위한 가상 테이블 엔진입니다. 가상 엔진은 데이터 레이크(예: Iceberg, DeltaLake, Hudi [36]) 또는 객체 스토리지의 파일(예: AWS S3, Google GCP)과도 상호작용할 수 있습니다. ClickHouse는 확장성과 가용성을 위해 여러 클러스터 노드에 걸쳐 테이블의 세그먼트 분할과 복제를 지원합니다. 세그먼트 분할은 세그먼트 분할 표현식에 따라 테이블을 여러 테이블 세그먼트 집합으로 나눕니다. 각 세그먼트는 서로 독립적인 테이블이며, 일반적으로 서로 다른 노드에 위치합니다. 클라이언트는 세그먼트를 직접 읽고 쓸 수 있습니다. 즉, 이를 별도의 테이블로 취급할 수 있습니다. 또는 모든 테이블 세그먼트에 대한 전역 뷰를 제공하는 Distributed 특수 테이블 엔진을 사용할 수도 있습니다. 세그먼트 분할의 주된 목적은 개별 노드의 용량을 초과하는 데이터 집합(일반적으로 수십 테라바이트 규모의 데이터)을 처리하는 것입니다. 세그먼트 분할의 또 다른 용도는 테이블의 읽기-쓰기 부하를 여러 노드에 분산하는 것, 즉 부하 분산입니다. 이와 별개로, 노드 장애에 대비하기 위해 하나의 세그먼트를 여러 노드에 복제할 수 있습니다. 이를 위해 각 Merge-Tree* 테이블 엔진에는 Raft 합의에 기반한 멀티마스터 조정 방식 [59] (Keeper에서 구현되며, Keeper는 C++로 작성된 Apache ZooKeeper의 드롭인 대체품입니다)을 사용하는 대응되는 ReplicatedMergeTree* 엔진이 있으며, 이를 통해 모든 세그먼트가 항상 구성 가능한 수의 레플리카를 갖도록 보장합니다. 복제 메커니즘은 3.6절에서 자세히 설명합니다. 예를 들어, 그림 2는 2개의 세그먼트를 가지며 각 세그먼트가 2개의 노드에 복제된 테이블을 보여줍니다. 마지막으로, ClickHouse 데이터베이스 엔진은 온프레미스, 클라우드, standalone 또는 in-process 모드로 운영할 수 있습니다. 온프레미스 모드에서는 사용자가 ClickHouse를 로컬에서 단일 server 또는 세그먼트 분할 및/또는 복제를 갖춘 다중 노드 클러스터로 구축합니다. 클라이언트는 네이티브, MySQL, PostgreSQL의 바이너리 wire 프로토콜 또는 HTTP REST API를 통해 데이터베이스와 통신합니다. 클라우드 모드는 완전관리형이며 자동 스케일링을 지원하는 DBaaS 서비스인 ClickHouse Cloud로 제공됩니다. 이 문서는 온프레미스 모드에 초점을 맞추고 있지만, 후속 publication에서 ClickHouse Cloud의 아키텍처를 설명할 계획입니다. standalone mode는 ClickHouse를 파일 분석 및 변환용 명령줄 유틸리티로 바꾸어, cat 및 grep 같은 Unix 도구를 대체하는 SQL 기반 대안으로 만듭니다. 이 모드는 사전 구성이 필요 없지만 단일 server로 제한됩니다. 최근에는 Jupyter notebooks [37]와 Pandas 데이터프레임 [61] 같은 대화형 데이터 분석 사용 사례를 위해 chDB라는 in-process 모드 [15]가 개발되었습니다. DuckDB [67]에서 영감을 받은 chDB는 ClickHouse를 호스트 프로세스에 고성능 OLAP 엔진으로 내장합니다. 다른 모드와 비교하면, 동일한 주소 공간에서 실행되므로 복사 없이 데이터베이스 엔진과 애플리케이션 사이에서 원본 데이터와 결과 데이터를 효율적으로 주고받을 수 있습니다.

3 스토리지 계층

이 섹션에서는 ClickHouse의 네이티브 스토리지 포맷인 MergeTree* 테이블 엔진을 설명합니다. 디스크에 저장되는 표현 방식을 설명하고, ClickHouse의 3가지 데이터 프루닝 기법을 살펴봅니다. 이어서 동시 삽입에 영향을 주지 않으면서 데이터를 지속적으로 변환하는 머지 전략을 소개합니다. 마지막으로 업데이트와 삭제의 구현 방식, 데이터 중복 제거, 데이터 복제, 그리고 ACID 컴플라이언스를 설명합니다.

3.1 온디스크 포맷

MergeTree* 테이블 엔진의 각 테이블은 불변의 테이블 파트 모음으로 구성됩니다. 테이블에 행 집합이 삽입될 때마다 새로운 파트가 생성됩니다. 파트는 중앙 카탈로그를 추가로 조회하지 않아도 내용을 해석하는 데 필요한 모든 메타데이터를 포함하므로 자체 완결적입니다. 테이블별 파트 수를 적게 유지하기 위해 백그라운드 머지 작업이 주기적으로 여러 개의 작은 파트를 더 큰 파트로 결합하며, 구성 가능한 파트 크기(기본값 150 GB)에 도달할 때까지 이를 반복합니다. 파트는 테이블의 프라이머리 키 컬럼을 기준으로 정렬되므로(섹션 3.2) 참조), 머지에는 효율적인 k-way merge sort [40]가 사용됩니다. 원본 파트는 비활성 상태로 표시되며, 참조 수가 0으로 떨어지는 즉시, 즉 더 이상 어떤 쿼리도 해당 파트를 읽지 않게 되면 최종적으로 삭제됩니다. 행은 두 가지 모드로 삽입할 수 있습니다. 동기 삽입 모드에서는 각 INSERT 문이 새 파트를 생성해 테이블에 추가합니다. 머지 오버헤드를 최소화하려면 데이터베이스 클라이언트가 튜플을 대량으로 삽입하는 것이 권장됩니다. 예를 들어 한 번에 20,000개 행을 삽입할 수 있습니다. 그러나 데이터를 실시간으로 분석해야 하는 경우에는 클라이언트 측 배칭으로 인한 지연이 허용되지 않는 경우가 많습니다. 예를 들어 관측성 사용 사례에서는 수천 개의 모니터링 에이전트가 소량의 이벤트 데이터와 메트릭 데이터를 지속적으로 전송하는 일이 흔합니다. 이러한 시나리오에서는 비동기 삽입 모드를 활용할 수 있습니다. 이 모드에서 ClickHouse는 동일한 테이블로 들어오는 여러 INSERT의 행을 버퍼링하고, 버퍼 크기가 구성 가능한 임계값을 초과하거나 타임아웃이 만료된 후에만 새 파트를 생성합니다.
그림 3: MergeTree*-engine 테이블의 삽입과 머지.
그림 3은 MergeTree*-engine 테이블에 대해 4회의 동기 삽입과 2회의 비동기 삽입이 수행되는 모습을 보여줍니다. 두 번의 머지로 활성 파트 수가 처음의 5개에서 2개로 줄었습니다. LSM trees [58] 및 여러 데이터베이스에서의 해당 구현 [13, 26, [56]](#page-13-8)과 비교하면, ClickHouse는 파트를 계층 구조로 배치하지 않고 모두 동등하게 취급합니다. 그 결과 머지는 더 이상 같은 수준의 파트로 제한되지 않습니다. 또한 이 방식은 파트의 암묵적인 시간순 정렬도 포기하므로, tombstone에 기반하지 않는 업데이트 및 삭제를 위한 대체 메커니즘이 필요합니다(섹션 3.4) 참조). ClickHouse는 삽입을 디스크에 직접 기록하는 반면, 다른 LSM-tree 기반 저장소는 일반적으로 write-ahead logging을 사용합니다(섹션 3.7) 참조). 파트 하나는 디스크의 디렉터리 하나에 해당하며, 각 컬럼마다 파일이 하나씩 들어 있습니다. 최적화를 위해 작은 파트(기본적으로 10MB 미만)의 컬럼은 읽기와 쓰기 시 공간 지역성을 높일 수 있도록 하나의 파일에 연속해서 저장됩니다. 또한 파트의 행은 논리적으로 8192개 레코드씩 묶인 그룹으로 더 세분되며, 이를 그래뉼이라고 합니다. 그래뉼은 ClickHouse에서 스캔 및 인덱스 조회 연산자가 처리하는 가장 작은 불가분의 데이터 단위를 나타냅니다. 다만 디스크 상의 데이터 읽기와 쓰기는 그래뉼 수준에서 수행되지 않고, 하나의 컬럼 내에서 인접한 여러 그래뉼을 묶은 블록 단위로 수행됩니다. 새 블록은 블록당 설정 가능한 바이트 크기(기본값 1MB)를 기준으로 형성되며, 즉 블록에 포함되는 그래뉼 수는 고정되지 않고 컬럼의 데이터 타입 및 분포에 따라 달라집니다. 또한 블록은 크기와 I/O 비용을 줄이기 위해 압축됩니다. 기본적으로 ClickHouse는 범용 압축 알고리즘으로 LZ4 [75]를 사용하지만, 사용자는 부동소수점 데이터에 대해 Gorilla [63] 또는 FPC [12] 같은 특화된 코덱을 지정할 수도 있습니다. 압축 알고리즘은 체인 형태로 연결할 수도 있습니다. 예를 들어 먼저 델타 코딩 [23]으로 숫자 값의 논리적 중복을 줄인 다음, 고강도 압축을 수행하고, 마지막으로 AES 코덱으로 데이터를 암호화할 수 있습니다. 블록은 디스크에서 메모리로 로드될 때 on-the-fly로 압축 해제됩니다. 압축된 상태에서도 개별 그래뉼에 빠르게 임의 접근할 수 있도록, ClickHouse는 각 컬럼마다 모든 그래뉼 ID를 해당 그래뉼이 속한 압축 블록의 컬럼 파일 내 오프셋 및 비압축 블록 내 그래뉼 오프셋과 연결하는 매핑도 추가로 저장합니다. 컬럼은 추가로 딕셔너리 인코딩될 수 있으며 [2, 77, [81]](#page-13-12), 두 가지 특수 래퍼 데이터 타입을 사용해 널 허용으로 만들 수도 있습니다. LowCardinality(T)는 원래 컬럼 값을 정수 ID로 대체하여 고유값이 적은 데이터의 저장 오버헤드를 크게 줄입니다. Nullable(T)는 컬럼 T에 내부 비트맵을 추가해 컬럼 값이 NULL인지 여부를 나타냅니다. 마지막으로, 테이블은 임의의 파티셔닝 표현식을 사용해 범위, 해시 또는 라운드 로빈 방식으로 파티셔닝할 수 있습니다. 파티션 프루닝을 가능하게 하기 위해, ClickHouse는 각 파티션마다 파티셔닝 표현식의 최솟값과 최댓값도 추가로 저장합니다. 사용자는 선택적으로 더 고급 컬럼 통계(예: HyperLogLog [30] 또는 t-digest [28] 통계)를 생성할 수 있으며, 이러한 통계는 카디널리티 추정치도 제공합니다.

3.2 데이터 프루닝

대부분의 사용 사례에서는 단일 쿼리 하나를 처리하기 위해 페타바이트 규모의 데이터를 스캔하는 방식이 너무 느리고 비용도 많이 듭니다. ClickHouse는 검색 중 대다수의 행을 건너뛸 수 있도록 하는 3가지 데이터 프루닝 기법을 지원하며, 이를 통해 쿼리 성능을 크게 높일 수 있습니다. 첫째, 사용자는 테이블에 프라이머리 키(primary key) 인덱스를 정의할 수 있습니다. 프라이머리 키 컬럼은 각 파트 내 행의 정렬 순서를 결정하므로 인덱스는 로컬하게 클러스터링됩니다. 또한 ClickHouse는 각 파트마다 각 그래뉼의 첫 번째 행에 있는 프라이머리 키 컬럼 값과 해당 그래뉼 ID 사이의 매핑을 저장하므로 인덱스는 희소합니다 [31]. 이렇게 생성되는 데이터 구조는 일반적으로 전체를 메모리에 유지할 수 있을 만큼 충분히 작습니다. 예를 들어 810만 개의 행을 인덱싱하는 데는 1000개의 항목만 필요합니다. 프라이머리 키의 주된 목적은 자주 필터링되는 컬럼에 대한 동등 프레디케이트와 범위 프레디케이트를 순차 스캔 대신 이진 검색으로 평가하는 것입니다(섹션 4.4)). 또한 이러한 로컬 정렬은 파트 병합과 쿼리 최적화에도 활용할 수 있습니다. 예를 들어 정렬 기반 집계에 활용하거나, 프라이머리 키 컬럼이 정렬 컬럼의 접두(prefix)를 이룰 때 물리 실행 계획에서 정렬 연산자를 제거할 수 있습니다. Figure 4는 페이지 노출 통계가 담긴 테이블에서 EventTime 컬럼에 대한 프라이머리 키 인덱스를 보여 줍니다. 쿼리의 범위 프레디케이트와 일치하는 그래뉼은 EventTime을 순차적으로 스캔하는 대신 프라이머리 키 인덱스에서 이진 검색으로 찾을 수 있습니다.
그림 4: 프라이머리 키 인덱스로 필터를 평가합니다.
둘째, 사용자는 테이블 프로젝션을 만들 수 있습니다. 즉, 동일한 행을 포함하지만 다른 프라이머리 키로 정렬된 테이블의 대체 버전입니다 [71]. 프로젝션은 메인 테이블의 프라이머리 키와 다른 컬럼으로 필터링하는 쿼리의 속도를 높일 수 있지만, 그 대가로 삽입, 머지, 저장 공간 활용 측면의 오버헤드가 증가합니다. 기본적으로 프로젝션은 메인 테이블에 새로 삽입된 파트에서만 지연 방식으로 채워지며, 사용자가 프로젝션을 전체 구체화하지 않는 한 기존 파트에는 채워지지 않습니다. 쿼리 최적화기는 추정된 I/O 비용을 기준으로 메인 테이블과 프로젝션 중 어디에서 읽을지 선택합니다. 특정 파트에 프로젝션이 없으면 쿼리 실행은 해당 메인 테이블 파트로 대체됩니다. 셋째, 스킵 인덱스는 프로젝션보다 더 가벼운 대안을 제공합니다. 스킵 인덱스의 기본 아이디어는 여러 개의 연속된 그래뉼 수준에서 소량의 메타데이터를 저장해 관련 없는 행의 스캔을 피하는 것입니다. 스킵 인덱스는 임의의 인덱스 표현식에 대해, 그리고 구성 가능한 세분화 수준, 즉 하나의 스킵 인덱스 블록에 포함되는 그래뉼 수를 지정하여 생성할 수 있습니다. 사용할 수 있는 스킵 인덱스 유형은 다음과 같습니다. 1. Min-max 인덱스 [51]: 각 인덱스 블록에 대해 인덱스 표현식의 최솟값과 최댓값을 저장합니다. 이 인덱스 유형은 절대 범위가 작고 로컬하게 클러스터링된 데이터, 예를 들어 느슨하게 정렬된 데이터에서 잘 동작합니다. 2. Set 인덱스: 구성 가능한 수의 고유한 인덱스 블록 값을 저장합니다. 이러한 인덱스는 로컬 카디널리티가 작은 데이터, 즉 값이 “뭉쳐 있는” 데이터에 가장 적합합니다. 3. 블룸 필터 인덱스 [9]: 행, token 또는 n-그램 값에 대해 구성 가능한 false positive rate로 생성됩니다. 이러한 인덱스는 텍스트 검색을 지원하지만 [73], min-max 및 set 인덱스와 달리 범위 프레디케이트나 부정 프레디케이트에는 사용할 수 없습니다.

3.3 머지 시점 데이터 변환

비즈니스 인텔리전스 및 관측성 사용 사례에서는 지속적으로 높은 속도로 생성되거나 순간적으로 급증하는 데이터를 처리해야 하는 경우가 많습니다. 또한 일반적으로 최근에 생성된 데이터가 과거 데이터보다 의미 있는 실시간 인사이트를 제공하는 데 더 중요합니다. 이러한 사용 사례에서는 데이터베이스가 높은 데이터 수집 속도를 유지하는 동시에 집계나 데이터 에이징과 같은 기법을 통해 과거 데이터의 양을 지속적으로 줄일 수 있어야 합니다. ClickHouse는 다양한 머지 전략을 사용해 기존 데이터를 연속적이고 점진적으로 변환할 수 있도록 지원합니다. 머지 시점 데이터 변환은 INSERT SQL 문의 성능을 저하시키지 않지만, 테이블에 원치 않는 값(예: 오래되었거나 집계되지 않은 값)이 전혀 남지 않음을 보장할 수는 없습니다. 필요한 경우 SELECT SQL 문에서 FINAL 키워드를 지정해 모든 머지 시점 변환을 쿼리 시점에 적용할 수 있습니다. Replacing merges는 해당 튜플이 포함된 파트의 생성 타임스탬프를 기준으로 가장 최근에 삽입된 버전만 유지하고, 이전 버전은 삭제합니다. 프라이머리 키 컬럼 값이 같으면 튜플은 동일한 것으로 간주됩니다. 어떤 튜플을 유지할지 명시적으로 제어하려면 비교용 특수 버전 컬럼을 지정할 수도 있습니다. Replacing merges는 일반적으로 머지 시점 업데이트 메커니즘으로 사용되거나(보통 업데이트가 빈번한 사용 사례에서), 삽입 시점 데이터 중복 제거의 대안으로 사용됩니다(섹션 3.5)). Aggregating merges는 프라이머리 키 컬럼 값이 같은 행을 하나의 집계된 행으로 합칩니다. 프라이머리 키가 아닌 컬럼은 요약 값을 담는 부분 집계 상태여야 합니다. 예를 들어 avg()를 위한 sum과 count 같은 두 개의 부분 집계 상태는 새로운 부분 집계 상태로 결합됩니다. Aggregating merges는 일반 테이블보다 materialized view에서 주로 사용됩니다. Materialized view는 원본 테이블에 대한 변환 쿼리를 바탕으로 채워집니다. 다른 데이터베이스와 달리 ClickHouse는 원본 테이블 전체 내용을 사용해 materialized view를 주기적으로 갱신하지 않습니다. 대신 원본 테이블에 새 파트가 삽입될 때 변환 쿼리의 결과를 반영해 materialized view를 점진적으로 업데이트합니다. Figure 5는 페이지 노출 통계가 있는 테이블에 정의된 materialized view를 보여줍니다. 원본 테이블에 새로 삽입된 파트에 대해 변환 쿼리는 지역별로 그룹화하여 최대 및 평균 지연 시간을 계산하고, 그 결과를 materialized view에 삽입합니다. -State 확장 기능이 붙은 집계 함수 avg()와 max()는 실제 결과 대신 부분 집계 상태를 반환합니다. materialized view에 정의된 aggregating merge는 서로 다른 파트에 있는 부분 집계 상태를 지속적으로 결합합니다. 최종 결과를 얻으려면 사용자는 -Merge 확장 기능이 붙은 avg()와 max())를 사용해 materialized view의 부분 집계 상태를 통합합니다.
Figure 5: materialized view에서의 aggregating merges.
TTL (time-to-live) merges는 과거 데이터의 에이징을 제공합니다. 삭제 머지 및 집계 머지와 달리 TTL 머지는 한 번에 하나의 파트만 처리합니다. TTL 머지는 트리거와 작업으로 구성된 규칙으로 정의됩니다. 트리거는 각 행에 대해 타임스탬프를 계산하는 표현식이며, 이 값은 TTL 머지가 실행되는 시점의 시간과 비교됩니다. 이를 통해 사용자는 행 단위로 작업을 제어할 수 있지만, 저희는 모든 행이 지정된 조건을 만족하는지만 확인한 뒤 전체 파트에 대해 작업을 수행하는 것으로도 충분하다는 점을 확인했습니다. 가능한 작업에는 1. 파트를 다른 볼륨으로 이동(예: 더 저렴하고 느린 스토리지), 2. 파트 재압축(예: 더 무거운 코덱 사용), 3. 파트 삭제, 4. 롤업, 즉 그룹화 키와 집계 함수를 사용한 행 집계가 포함됩니다. 예시로, Listing 1.의 로깅 테이블 정의를 살펴보겠습니다. ClickHouse는 타임스탬프 컬럼 값이 1주일보다 오래된 파트를 느리지만 저렴한 S3 객체 스토리지로 이동합니다.
1 CREATE TABLE tab ( ts DateTime , msg String )
2 ENGINE MergeTree PRIMARY KEY ts
3 TTL ( ts + INTERVAL 1 WEEK ) TO VOLUME 's3 '
목록 1: 1주일 후 파트를 객체 스토리지로 이동.

3.4 업데이트 및 삭제

MergeTree* 테이블 엔진은 추가 전용 워크로드에 유리하도록 설계되었지만, 일부 사용 사례에서는 예를 들어 규제 컴플라이언스를 위해 기존 데이터를 가끔 수정해야 합니다. 데이터를 업데이트하거나 삭제하는 방법은 두 가지가 있으며, 어느 방법도 병렬 삽입을 차단하지 않습니다. 뮤테이션은 테이블의 모든 파트를 제자리에서 재작성합니다. 이 작업으로 인해 테이블(삭제) 또는 컬럼(업데이트)의 크기가 일시적으로 2배로 늘어나는 것을 방지하기 위해, 이 작업은 원자적이지 않습니다. 즉, 병렬 SELECT 문이 뮤테이션된 파트와 뮤테이션되지 않은 파트를 함께 읽을 수 있습니다. 뮤테이션은 작업이 끝나면 데이터가 물리적으로 변경됨을 보장합니다. 삭제 뮤테이션은 모든 파트의 모든 컬럼을 재작성하므로 여전히 비용이 큽니다. 대안으로, 경량한 삭제는 행이 삭제되었는지 여부를 나타내는 내부 비트맵 컬럼만 업데이트합니다. ClickHouse는 삭제된 행이 결과에서 제외되도록 비트맵 컬럼에 대한 추가 필터를 SELECT 쿼리에 적용합니다. 삭제된 행은 이후 미래의 특정되지 않은 시점에 일반 머지를 통해서만 물리적으로 제거됩니다. 컬럼 수에 따라, 경량한 삭제는 SELECT가 더 느려지는 대신 뮤테이션보다 훨씬 빠를 수 있습니다. 동일한 테이블에서 수행되는 업데이트 및 삭제 작업은 논리적 충돌을 피하기 위해 드물게 발생하며 직렬화되어 수행되는 것이 바람직합니다.

3.5 멱등 삽입

실제 운영 환경에서 자주 발생하는 문제 중 하나는, 클라이언트가 테이블에 삽입할 데이터를 서버로 전송한 뒤 연결 타임아웃이 발생했을 때 이를 어떻게 처리해야 하는가입니다. 이런 상황에서는 데이터가 실제로 성공적으로 삽입되었는지 여부를 클라이언트가 구분하기 어렵습니다. 전통적으로는 클라이언트가 데이터를 서버로 다시 전송하고, 프라이머리 키(primary key) 또는 고유 제약 조건(unique constraint)이 중복 삽입을 거부하도록 하는 방식으로 이 문제를 해결합니다. 데이터베이스는 이진 트리 [39, [68]](#page-13-16), radix 트리 [45], 또는 해시 테이블 [29] 기반의 인덱스 구조를 사용해 필요한 점 조회를 빠르게 수행합니다. 이러한 데이터 구조는 모든 튜플에 인덱스를 유지하므로, 대규모 데이터 세트와 높은 수집 속도에서는 공간 및 갱신 오버헤드가 지나치게 커집니다. ClickHouse는 각 삽입이 결국 하나의 파트를 생성한다는 점에 기반한, 더 경량의 대안을 제공합니다. 보다 구체적으로, 서버는 마지막으로 삽입된 N개의 파트(예: N=100)의 해시를 유지하고, 이미 알려진 해시를 가진 파트가 다시 삽입되면 이를 무시합니다. 비복제 테이블과 복제된 테이블의 해시는 각각 로컬과 Keeper에 저장됩니다. 그 결과 삽입은 멱등성을 갖게 됩니다. 즉, 클라이언트는 타임아웃 이후 동일한 행 배치를 다시 전송하기만 하면 되며, 서버가 중복 제거를 처리한다고 가정할 수 있습니다. 중복 제거 과정을 더 세밀하게 제어하려는 경우, 클라이언트는 선택적으로 파트 해시 역할을 하는 삽입 토큰을 제공할 수 있습니다. 해시 기반 중복 제거는 새 행의 해시를 계산하는 오버헤드를 수반하지만, 해시를 저장하고 비교하는 비용은 무시할 수 있을 정도로 작습니다.

3.6 데이터 복제

복제는 고가용성(노드 장애 허용)을 위한 전제 조건일 뿐만 아니라, 부하 분산과 무중단 업그레이드에도 사용됩니다 [14]. ClickHouse에서 복제는 테이블 상태라는 개념에 기반하며, 테이블 상태는 테이블 파트(Section 3.1)와 컬럼 이름 및 타입 같은 테이블 메타데이터의 집합으로 구성됩니다. 노드는 세 가지 작업으로 테이블 상태를 진행시킵니다. 1. 삽입은 상태에 새 파트를 추가합니다. 2. 머지는 상태에 새 파트를 추가하고 기존 파트를 상태에 추가하거나 상태에서 삭제합니다. 3. 뮤테이션과 DDL 문은 구체적인 작업에 따라 파트를 추가하거나, 파트를 삭제하거나, 테이블 메타데이터를 변경합니다. 작업은 단일 노드에서 로컬로 수행되며, 상태 전이의 시퀀스로 전역 복제 로그에 기록됩니다. 복제 로그는 일반적으로 3개의 ClickHouse Keeper 프로세스로 구성된 앙상블이 유지하며, 이들은 Raft 합의 알고리즘 [59]을 사용해 ClickHouse 노드 클러스터를 위한 분산형 고가용 coordination 계층을 제공합니다. 클러스터의 모든 노드는 처음에 복제 로그의 동일한 위치를 가리킵니다. 노드가 로컬 삽입, 머지, 뮤테이션, DDL 문을 실행하는 동안, 복제 로그는 다른 모든 노드에서 비동기적으로 재생됩니다. 그 결과, 복제된 테이블은 결국 일관성(eventual consistency)만 보장합니다. 즉, 최신 상태로 수렴하는 동안 노드가 일시적으로 이전 테이블 상태를 읽을 수 있습니다. 앞서 언급한 대부분의 작업은 정족수(quorum)의 노드(예: 과반수 노드 또는 모든 노드)가 새 상태를 채택할 때까지 동기적으로 실행할 수도 있습니다. 예시로, Figure 6는 3개의 ClickHouse 노드로 이루어진 클러스터에서 초기에 비어 있는 복제된 테이블을 보여줍니다. 먼저 Node 1이 두 개의 insert 문을 수신하고 이를 Keeper 앙상블에 저장된 복제 로그에 기록합니다( 1 2 ). 다음으로 Node 2는 첫 번째 로그 항목을 fetch하고( 3 ) Node 1에서 새 파트를 다운로드하여( 4 ) 이를 재생하며, Node 3는 두 로그 항목을 모두 재생합니다( 3 4 5 6 ). 마지막으로 Node 3는 두 파트를 새 파트로 머지하고, 입력 파트를 삭제한 뒤, 머지 항목을 복제 로그에 기록합니다( 7 ).
Figure 6: 3개 노드 클러스터에서의 복제.
동기화를 가속하기 위한 최적화는 세 가지가 있습니다. 첫째, 클러스터에 새로 추가된 노드는 복제 로그를 처음부터 재생하지 않고, 대신 마지막 복제 로그 항목을 기록한 노드의 상태를 그대로 복사합니다. 둘째, 머지는 로컬에서 다시 수행하거나 다른 노드에서 결과 파트를 fetch하는 방식으로 재생됩니다. 정확한 동작은 구성할 수 있으며 CPU 활용과 네트워크 I/O의 균형을 맞출 수 있습니다. 예를 들어, 데이터 센터 간 복제는 일반적으로 운영 비용을 최소화하기 위해 로컬 머지를 선호합니다. 셋째, 노드는 서로 독립적인 복제 로그 항목을 병렬로 재생합니다. 여기에는 예를 들어 같은 테이블에 연속해서 삽입된 새 파트의 fetch 작업이나, 서로 다른 테이블에 대한 작업이 포함됩니다.

3.7 ACID 컴플라이언스

동시 읽기 및 쓰기 작업의 성능을 극대화하기 위해 ClickHouse는 가능한 한 래치 사용을 피합니다. 쿼리는 쿼리 시작 시점에 생성된, 관련된 모든 테이블의 모든 파트에 대한 스냅샷을 기준으로 실행됩니다. 따라서 병렬 INSERT 또는 머지(섹션 3.1)로 새로 삽입된 파트는 쿼리 실행에 포함되지 않습니다. 처리 중인 파트가 동시에 수정되거나 제거되지 않도록 하기 위해(섹션 3.4)), 해당 파트의 참조 카운트는 쿼리가 실행되는 동안 증가합니다. 형식적으로 이는 버전이 지정된 파트를 기반으로 하는 MVCC 변형 [6]으로 구현된 스냅샷 격리(snapshot isolation)에 해당합니다. 그 결과, 스냅샷이 생성되는 시점의 동시 쓰기가 각각 단일 파트에만 영향을 미치는 드문 경우를 제외하면 SQL 문은 일반적으로 ACID를 준수하지 않습니다. 실제로 ClickHouse의 쓰기 중심 의사결정 사용 사례 대부분은 정전이 발생할 경우 새 데이터가 일부 손실될 수 있는 작은 위험도 감수합니다. 데이터베이스는 이를 활용해 기본적으로 새로 삽입된 파트를 디스크에 커밋(fsync)하도록 강제하지 않으며, 그 대신 원자성을 포기하는 대가로 커널이 쓰기를 일괄 처리할 수 있게 합니다.

4 쿼리 처리 레이어

그림 7: SIMD 유닛, 코어, 노드 전반에 걸친 병렬화.
그림 7에서 볼 수 있듯이 ClickHouse는 데이터 요소, 데이터 청크, 테이블 세그먼트 수준에서 쿼리를 병렬화합니다. 여러 데이터 요소는 SIMD 명령어를 사용해 연산자 내부에서 동시에 처리할 수 있습니다. 단일 노드에서는 쿼리 엔진이 여러 스레드에서 연산자를 동시에 실행합니다. ClickHouse는 MonetDB/X100 [11]과 동일한 벡터화 모델을 사용합니다. 즉, 가상 함수 호출 오버헤드를 최소화하기 위해 연산자는 단일 행이 아니라 여러 행(데이터 청크)을 생성하고 전달하며 소비합니다. 원본 테이블이 서로 겹치지 않는 테이블 세그먼트로 분할되어 있으면 여러 노드가 해당 세그먼트를 동시에 스캔할 수 있습니다. 그 결과 모든 하드웨어 리소스를 최대한 활용할 수 있으며, 노드를 추가해 쿼리 처리를 수평 확장하고 코어를 추가해 수직 확장할 수 있습니다. 이 절의 나머지 부분에서는 먼저 데이터 요소, 데이터 청크, 세그먼트 세분화 수준에서의 병렬 처리를 더 자세히 설명합니다. 이어서 쿼리 성능을 극대화하기 위한 핵심 최적화 몇 가지를 소개합니다. 마지막으로 동시 실행되는 쿼리가 있는 환경에서 ClickHouse가 공유 시스템 리소스를 어떻게 관리하는지 설명합니다.

4.1 SIMD 병렬화

여러 행을 연산자 사이에서 전달하면 벡터화를 활용할 기회가 생깁니다. 벡터화는 수동으로 작성한 인트린식 [64, [80]](#page-13-19) 또는 컴파일러의 자동 벡터화 [25]를 기반으로 합니다. 벡터화의 이점을 얻을 수 있는 코드는 서로 다른 컴퓨트 커널로 컴파일됩니다. 예를 들어, 쿼리 연산자의 내부 핫 루프는 비벡터화 커널, 자동 벡터화된 AVX2 커널, 수동으로 벡터화한 AVX-512 커널로 구현할 수 있습니다. 가장 빠른 커널은 cpuid 명령에 따라 런타임에 선택됩니다. 이 접근 방식을 사용하면 ClickHouse는 최소 요구 사항인 SSE 4.2를 지원하는 15년 이상 된 시스템에서도 실행될 수 있으며, 최신 하드웨어에서는 여전히 상당한 성능 향상을 제공합니다.

4.2 멀티코어 병렬화

그림 8: 3개의 레인이 있는 물리 연산자 계획.
ClickHouse는 SQL 쿼리를 물리 계획 연산자의 방향 그래프로 변환하는 일반적인 접근 방식 [31]을 따릅니다. 연산자 계획의 입력은 네이티브 또는 지원되는 타사 포맷(섹션 5) 참조)으로 데이터를 읽는 특수 소스 연산자로 표현됩니다. 마찬가지로, 특수 싱크 연산자는 결과를 원하는 출력 형식으로 변환합니다. 물리 연산자 계획은 쿼리 컴파일 시점에 구성 가능한 최대 작업자 스레드 수(기본값은 코어 수)와 원본 테이블 크기에 따라 독립적인 실행 레인으로 전개됩니다. 레인은 병렬 연산자가 처리할 데이터를 서로 겹치지 않는 범위로 나눕니다. 병렬 처리 기회를 최대화하기 위해 레인은 가능한 한 늦게 병합됩니다. 예시로, 그림 8의 노드 1 박스는 페이지 노출 통계가 담긴 테이블에 대한 전형적인 OLAP 쿼리의 연산자 그래프를 보여줍니다. 첫 번째 단계에서는 원본 테이블의 서로 분리된 3개 범위를 동시에 필터링합니다. Repartition exchange 연산자는 첫 번째 단계와 두 번째 단계 사이에서 결과 청크를 동적으로 라우팅하여 처리 스레드가 고르게 활용되도록 합니다. 스캔한 범위들의 선택도가 크게 다르면 첫 번째 단계 이후 레인 간 균형이 깨질 수 있습니다. 두 번째 단계에서는 필터를 통과한 행을 RegionID로 그룹화합니다. Aggregate 연산자는 RegionID를 그룹화 컬럼으로 사용하고, avg()를 위한 부분 집계 상태로 각 그룹의 합계와 개수를 유지하는 로컬 결과 그룹을 관리합니다. 로컬 집계 결과는 최종적으로 GroupStateMerge 연산자에 의해 전역 집계 결과로 병합됩니다. 이 연산자는 파이프라인 브레이커이기도 하므로, 세 번째 단계는 집계 결과가 완전히 계산된 후에만 시작할 수 있습니다. 세 번째 단계에서는 결과 그룹을 먼저 Distribute exchange 연산자가 동일한 크기의 서로 분리된 3개 파티션으로 나눈 다음, 이를 AvgLatency 기준으로 정렬합니다. 정렬은 3단계로 수행됩니다. 먼저 ChunkSort 연산자가 각 파티션의 개별 청크를 정렬합니다. 다음으로 StreamSort 연산자는 로컬 정렬 결과를 유지하면서, 유입되는 정렬된 청크를 2-way 머지 정렬로 결합합니다. 마지막으로 MergeSort 연산자가 k-way 정렬을 사용해 로컬 결과를 결합하여 최종 결과를 만듭니다. 연산자는 상태 머신이며 입력 포트와 출력 포트를 통해 서로 연결됩니다. 연산자의 세 가지 가능한 상태는 need-chunk, ready, done입니다. need-chunk에서 ready로 전이하려면 연산자의 입력 포트에 청크가 배치되어야 합니다. ready에서 done으로 전이하려면 연산자가 입력 청크를 처리하고 출력 청크를 생성해야 합니다. done에서 need-chunk로 전이하려면 연산자의 출력 포트에서 출력 청크가 제거되어야 합니다. 서로 연결된 두 연산자에서는 첫 번째와 세 번째 상태 전이가 결합된 하나의 단계로만 수행될 수 있습니다. 소스 연산자(싱크 연산자)는 각각 ready와 done(need-chunk와 done) 상태만 가집니다. 작업자 스레드는 물리 연산자 계획을 지속적으로 순회하면서 상태 전이를 수행합니다. CPU 캐시의 활용도를 높이기 위해 계획에는 같은 레인에서 연속된 연산자를 동일한 스레드가 처리해야 한다는 힌트가 포함됩니다. 병렬 처리는 단계 내에서 서로 분리된 입력 전반에 걸쳐 수평적으로(예: 그림 8에서 Aggregate 연산자들은 동시에 실행됨) 발생하고, 파이프라인 브레이커로 분리되지 않은 단계 사이에서는 수직적으로도(예: 그림 8에서 같은 레인의 Filter 연산자와 Aggregate 연산자가 동시에 실행될 수 있음) 발생합니다. 새 쿼리가 시작되거나 동시 실행 중인 쿼리가 종료될 때 과다 또는 과소 구독을 피하기 위해, 병렬성 수준은 쿼리 시작 시 지정된 해당 쿼리의 최대 작업자 스레드 수와 1 사이에서 쿼리 실행 도중에도 변경될 수 있습니다(섹션 4.5) 참조). 연산자는 추가로 두 가지 방식으로 런타임에 쿼리 실행에 영향을 줄 수 있습니다. 첫째, 연산자는 새로운 연산자를 동적으로 생성하고 연결할 수 있습니다. 이는 주로 메모리 활용량이 구성 가능한 임계값을 초과할 때 쿼리를 취소하는 대신 외부 집계, 정렬 또는 조인 알고리즘으로 전환하는 데 사용됩니다. 둘째, 연산자는 작업자 스레드가 비동기 큐로 이동하도록 요청할 수 있습니다. 이를 통해 원격 데이터를 기다리는 동안 작업자 스레드를 더 효율적으로 활용할 수 있습니다. ClickHouse의 쿼리 실행 엔진과 모르셀 기반 병렬화 [44]는 레인이 일반적으로 서로 다른 코어/NUMA 소켓에서 실행되고, 워커 스레드가 다른 레인의 작업을 가져올 수 있다는 점에서 유사합니다. 또한 중앙 스케줄링 구성 요소가 없으며, 대신 워커 스레드가 연산자 계획을 계속 순회하면서 각자 작업을 선택합니다. 모르셀 기반 병렬화와 달리 ClickHouse는 최대 병렬화 수준을 계획에 미리 반영하고, 기본 모르셀 크기인 약 100,000개 행과 비교해 원본 테이블을 분할하는 데 훨씬 더 큰 범위를 사용합니다. 이로 인해 경우에 따라 정체가 발생할 수 있지만(예: 서로 다른 레인에서 필터 연산자의 런타임 차이가 매우 큰 경우), Repartition과 같은 exchange 연산자를 적극적으로 사용하면 적어도 이러한 불균형이 단계 전반에 걸쳐 누적되는 것은 방지할 수 있다고 봅니다.

4.3 다중 노드 병렬화

쿼리의 원본 테이블이 세그먼트로 분할되어 있으면, 쿼리를 수신한 노드(initiator node)의 쿼리 최적화기는 가능한 한 많은 작업을 다른 노드에서 수행하려고 합니다. 다른 노드의 결과는 쿼리 계획의 여러 지점에서 통합될 수 있습니다. 쿼리에 따라 원격 노드는 1. 원본 테이블의 원시 컬럼을 initiator node로 스트리밍하거나, 2. 원본 컬럼을 필터링한 뒤 남은 행을 전송하거나, 3. 필터 및 집계 단계를 수행하고 부분 집계 상태를 포함한 로컬 결과 그룹을 전송하거나, 또는 4. 필터, 집계, 정렬을 포함한 전체 쿼리를 실행할 수 있습니다. 그림 8의 Node 2 … N은 hits 테이블의 세그먼트를 보유한 다른 노드에서 실행되는 계획 조각을 보여줍니다. 이 노드들은 로컬 데이터를 필터링하고 그룹화한 뒤 그 결과를 initiator node로 전송합니다. Node 1의 GroupStateMerge 연산자는 최종적으로 결과 그룹이 정렬되기 전에 로컬 및 원격 결과를 머지합니다.

4.4 종합적인 성능 최적화

이 절에서는 쿼리 실행의 여러 단계에 적용되는 주요 성능 최적화를 소개합니다. 쿼리 최적화. 첫 번째 최적화 집합은 쿼리의 AST에서 얻은 의미적 쿼리 표현을 기반으로 적용됩니다. 이러한 최적화의 예로는 상수 접기(constant folding, 예: concat(lower(‘a’),upper(‘b’))는 ‘aB’가 됨), 일부 집계 함수에서 스칼라 추출(예: sum(a2)는 2 * sum(a)가 됨), 공통 부분 표현식 제거(common subexpression elimination), 그리고 동등 비교 필터의 논리합을 IN 목록으로 변환하는 것(예: x=c OR x=d는 x IN (c,d)가 됨) 등이 있습니다. 이렇게 최적화된 의미적 쿼리 표현은 이후 논리 연산자 계획으로 변환됩니다. 논리 계획에 대한 최적화에는 filter pushdown과 함수 평가 및 정렬 단계의 재배치가 포함되며, 이는 어느 쪽의 비용이 더 클 것으로 추정되는지에 따라 결정됩니다. 마지막으로 논리적 쿼리 계획은 물리 연산자 계획으로 변환됩니다. 이 변환은 사용되는 테이블 엔진의 특성을 활용할 수 있습니다. 예를 들어 MergeTree 테이블 엔진에서는 ORDER BY 컬럼이 기본 키(primary key)의 접두사를 이루는 경우 데이터를 디스크 순서대로 읽을 수 있으므로 계획에서 정렬 연산자를 제거할 수 있습니다. 또한 집계 시 그룹화 컬럼이 기본 키의 접두사를 이루면 ClickHouse는 정렬 집계(sort aggregation) [33]를 사용할 수 있습니다. 즉, 미리 정렬된 입력에서 동일한 값이 연속으로 나타나는 구간을 직접 집계합니다. 해시 집계와 비교하면 정렬 집계는 메모리 사용량이 훨씬 적고, 각 연속 구간의 처리가 끝나는 즉시 집계 값을 다음 연산자에 전달할 수 있습니다. 쿼리 컴파일. ClickHouse는 LLVM 기반 쿼리 컴파일을 사용하여 인접한 계획 연산자를 동적으로 결합합니다 [38, [53]](#page-13-0). 예를 들어 표현식 a * b + c + 1은 세 개의 연산자가 아니라 하나의 연산자로 결합될 수 있습니다. 표현식뿐 아니라 ClickHouse는 여러 집계 함수를 한 번에 평가할 때(즉, GROUP BY의 경우)와 정렬 키가 둘 이상인 정렬에도 컴파일을 활용합니다. 쿼리 컴파일은 가상 호출 수를 줄이고, 데이터를 레지스터나 CPU 캐시에 유지하며, 실행해야 할 코드 양을 줄여 브랜치 예측에도 도움이 됩니다. 또한 런타임 컴파일은 컴파일러에 구현된 논리 최적화와 피프홀 최적화 같은 다양한 최적화를 가능하게 하며, 로컬 환경에서 사용할 수 있는 가장 빠른 CPU 명령어를 활용할 수 있게 해줍니다. 컴파일은 동일한 일반 표현식, 집계 표현식 또는 정렬 표현식이 서로 다른 쿼리에서 설정 가능한 횟수보다 많이 실행될 때만 시작됩니다. 컴파일된 쿼리 연산자는 캐시되며 이후 쿼리에서 재사용할 수 있습니다.[7] 기본 키 인덱스 평가. ClickHouse는 조건의 연언 정규형에서 필터 절의 부분 집합이 기본 키 컬럼의 접두사를 이룰 경우 기본 키 인덱스를 사용해 WHERE 조건을 평가합니다. 기본 키 인덱스는 사전식으로 정렬된 키 값 범위를 대상으로 왼쪽에서 오른쪽으로 분석됩니다. 기본 키 컬럼에 해당하는 필터 절은 3값 논리로 평가됩니다. 즉, 해당 범위의 값에 대해 모두 참이거나, 모두 거짓이거나, 참과 거짓이 섞여 있는 경우입니다. 마지막 경우에는 범위를 하위 범위로 분할한 뒤 재귀적으로 분석합니다. 필터 조건의 함수에 대해서도 추가 최적화가 있습니다. 첫째, 함수에는 단조성을 설명하는 특성이 있으며, 예를 들어 toDayOfMonth(date)는 한 달 범위 내에서 구간별 단조성을 가집니다. 이러한 단조성 특성을 통해 정렬된 입력 키 값 범위에서 함수가 정렬된 결과를 생성하는지 추론할 수 있습니다. 둘째, 일부 함수는 주어진 함수 결과의 원상(preimage)을 계산할 수 있습니다. 이는 키 컬럼에 대한 함수 호출 결과와 상수를 비교하는 대신, 키 컬럼 값을 원상과 비교하도록 대체하는 데 사용됩니다. 예를 들어 toYear(k) = 2024는 k >= 2024-01-01 && k < 2025-01-01로 대체할 수 있습니다. 데이터 스키핑. ClickHouse는 3.2절에서 소개한 데이터 구조를 사용해 쿼리 런타임 중 데이터 읽기를 피하려고 합니다. 또한 서로 다른 컬럼에 대한 필터는 휴리스틱과 (선택적인) 컬럼 통계를 기반으로 추정된 선택도가 높은 순서대로 순차적으로 평가됩니다. 하나 이상의 일치하는 행을 포함하는 데이터 청크만 다음 프레디케이트로 전달됩니다. 이에 따라 프레디케이트를 거칠수록 읽어야 하는 데이터 양과 수행해야 하는 계산 수가 점진적으로 줄어듭니다. 이 최적화는 선택도가 매우 높은 프레디케이트가 하나 이상 있을 때만 적용됩니다. 그렇지 않으면 모든 프레디케이트를 병렬로 평가하는 경우보다 쿼리의 지연 시간이 오히려 악화되기 때문입니다. 해시 테이블. 해시 테이블은 집계와 해시 조인에 사용하는 핵심 데이터 구조입니다. 올바른 해시 테이블 유형을 선택하는 것은 성능에 매우 중요합니다. ClickHouse는 해시 함수, allocator, 셀 유형, 크기 조정 정책을 가변 요소로 하는 범용 해시 테이블 템플릿에서 다양한 해시 테이블을 인스턴스화합니다 (2024년 3월 기준 30개 이상). 그룹화 컬럼의 데이터 타입, 추정된 해시 테이블 카디널리티, 기타 여러 요인에 따라 각 쿼리 연산자에 가장 빠른 해시 테이블이 개별적으로 선택됩니다. 해시 테이블에 적용된 추가 최적화는 다음과 같습니다:
  • 대규모 키 집합을 지원하기 위한 256개의 하위 테이블(해시의 첫 번째 바이트 기준)로 이루어진 2단계 레이아웃,
  • 문자열 길이에 따라 서로 다른 해시 함수를 사용하고 4개의 하위 테이블을 갖는 문자열 해시 테이블 [79],
  • 키 수가 적을 때 키를 직접 버킷 인덱스로 사용하는(즉, 해싱하지 않는) lookup 테이블,
  • 비교 비용이 큰 경우(예: 문자열, AST) 충돌 해결을 더 빠르게 수행하기 위한 해시가 내장된 값,
  • 불필요한 크기 재조정을 피하기 위해 런타임 통계로 예측한 크기를 기반으로 해시 테이블 생성,
  • 생성/소멸 수명 주기가 같은 여러 개의 작은 해시 테이블을 하나의 메모리 슬랩에 할당,
  • 해시 맵별 및 셀별 버전 카운터를 사용해 재사용을 위해 해시 테이블을 즉시 비우기,
  • 키를 해싱한 후 값 조회를 더 빠르게 하기 위한 CPU 프리페치(__builtin_prefetch) 사용.
조인. ClickHouse는 처음에는 조인을 제한적으로만 지원했기 때문에, 역사적으로 많은 사용 사례에서 비정규화된 테이블을 사용했습니다. 현재 이 데이터베이스는 제공합니다 SQL에서 사용할 수 있는 모든 조인 유형(inner, left- /right/full outer, cross, as-of)과 해시 조인(naïve, grace), sort-merge join, 빠른 키-값 lookup을 지원하는 테이블 엔진(보통 딕셔너리)을 위한 index join 등 다양한 조인 알고리즘을 제공합니다. 조인은 데이터베이스에서 가장 비용이 큰 연산 중 하나이므로, 고전적인 조인 알고리즘의 병렬 변형을 제공하는 것이 중요하며, 이상적으로는 공간/시간 절충도 구성 가능해야 합니다. 해시 조인의 경우 ClickHouse는 [7]의 비차단 공유 파티션 알고리즘을 구현합니다. 예를 들어 그림 9의 쿼리는 페이지 히트 통계 테이블에서 self-join을 사용해 사용자가 URL 사이를 어떻게 이동하는지 계산합니다. 조인의 빌드 단계는 원본 테이블의 서로 겹치지 않는 3개의 범위를 담당하는 3개의 레인으로 나뉩니다. 전역 해시 테이블 대신 파티션된 해시 테이블을 사용합니다. (보통 3개의) 워커 스레드는 해시 함수의 modulo를 계산해 빌드 측 각 입력 행의 대상 파티션을 결정합니다. 해시 테이블 파티션에 대한 접근은 Gather exchange 연산자를 사용해 동기화합니다. 프로브 단계도 비슷한 방식으로 입력 튜플의 대상 파티션을 찾습니다. 이 알고리즘은 튜플당 2번의 추가 해시 계산을 유발하지만, 해시 테이블 파티션 수에 따라 빌드 단계의 래치 경합을 크게 줄여줍니다.
그림 9: 3개의 해시 테이블 파티션을 사용하는 병렬 해시 조인.

4.5 워크로드 격리

ClickHouse는 동시성 제어, 메모리 사용량 제한, I/O 스케줄링을 제공하여 쿼리를 워크로드 클래스로 격리할 수 있게 합니다. 특정 워크로드 클래스에 대해 공유 자원(CPU 코어, DRAM, 디스크 및 네트워크 I/O)의 제한을 설정하면, 해당 쿼리가 다른 중요한 비즈니스 쿼리에 영향을 미치지 않도록 할 수 있습니다. 동시성 제어는 동시 쿼리 수가 많은 상황에서 스레드 과다 할당을 방지합니다. 보다 구체적으로는, 쿼리당 워커 스레드 수가 사용 가능한 CPU 코어 수에 대한 지정된 비율에 따라 동적으로 조정됩니다. ClickHouse는 서버, 사용자, 쿼리 수준에서 메모리 할당의 바이트 크기를 추적하므로 유연한 메모리 사용량 제한을 설정할 수 있습니다. 메모리 오버커밋은 다른 쿼리의 메모리 제한을 보장하면서, 쿼리가 보장된 메모리를 초과하는 추가 여유 메모리를 사용할 수 있게 합니다. 또한 집계, 정렬, join 절의 메모리 사용량도 제한할 수 있으며, 메모리 제한을 초과하면 외부 알고리즘으로 폴백됩니다. 마지막으로, I/O 스케줄링을 통해 최대 대역폭, 진행 중인 요청 수, 정책(예: FIFO, SFC [32])을 기준으로 워크로드 클래스의 로컬 및 원격 디스크 접근을 제한할 수 있습니다.

5 통합 계층

실시간 의사결정 애플리케이션은 여러 위치에 있는 데이터에 효율적으로, 그리고 낮은 지연 시간으로 접근할 수 있어야 하는 경우가 많습니다. OLAP 데이터베이스에서 외부 데이터를 사용할 수 있게 하는 방법은 두 가지입니다. 푸시 기반 데이터 접근에서는 타사 구성 요소가 데이터베이스와 외부 데이터 저장소를 연결합니다. 대표적인 예로, 원격 데이터를 대상 시스템으로 푸시하는 특화된 extract-transform-load(ETL) 도구를 들 수 있습니다. 풀 기반 모델에서는 데이터베이스 자체가 원격 데이터 소스에 연결해 쿼리를 위해 데이터를 로컬 테이블로 가져오거나, 데이터를 원격 시스템으로 내보냅니다. 푸시 기반 방식은 더 범용적이고 널리 사용되지만, 아키텍처가 더 커지고 확장성 병목도 수반합니다. 반면 데이터베이스에 원격 연결 기능을 직접 두면 전체 아키텍처를 단순하게 유지하면서도 로컬 데이터와 원격 데이터 간 조인 같은 유용한 기능을 제공하고, 인사이트를 얻기까지 걸리는 시간도 줄일 수 있습니다. 이 절의 나머지 부분에서는 원격 위치의 데이터에 접근하기 위한 ClickHouse의 풀 기반 데이터 통합 방법을 살펴봅니다. SQL 데이터베이스에서 원격 연결이라는 개념 자체는 새로운 것이 아닙니다. 예를 들어, 2001년에 도입된 SQL/MED 표준 [35]은 외부 데이터를 관리하기 위한 통합 인터페이스로 foreign data wrapper를 제안했으며, PostgreSQL은 이를 2011년부터 구현해 왔습니다 [65]. 다른 데이터 저장소 및 저장 포맷과의 폭넓은 상호운용성은 ClickHouse의 설계 목표 중 하나입니다. 2024년 3월 기준, 저희가 아는 한 ClickHouse는 모든 분석 데이터베이스 가운데 가장 다양한 내장 데이터 통합 옵션을 제공합니다. 외부 연결성. ClickHouse는 ODBC, MySQL, PostgreSQL, SQLite, Kafka, Hive, MongoDB, Redis, S3/GCP/Azure 객체 저장소, 그리고 다양한 데이터 레이크를 포함한 외부 시스템 및 저장 위치와 연결하기 위해 50개 이상의 통합 테이블 함수와 엔진을 제공합니다. 이를 다음 보너스 그림(원래 VLDB 논문에는 포함되지 않음)에 표시된 범주로 더 나눌 수 있습니다.
보너스 그림: ClickBench의 상호운용성 옵션.
통합 테이블 함수를 통한 임시 접근. 테이블 함수는 SELECT 쿼리의 FROM 절에서 호출하여 탐색적인 애드혹 쿼리를 위해 원격 데이터를 읽을 수 있습니다. 또는 INSERT INTO TABLE FUNCTION 문을 사용해 원격 저장소에 데이터를 쓰는 데 사용할 수도 있습니다. 영구적 접근. 원격 데이터 저장소 및 처리 시스템과 영구 연결을 만드는 방법은 세 가지가 있습니다. 첫째, 통합 테이블 엔진은 MySQL 테이블과 같은 원격 데이터 소스를 영구적인 로컬 테이블로 표현합니다. 사용자는 테이블 함수와 SELECT 쿼리를 결합한 CREATE TABLE AS 구문을 사용해 테이블 정의를 저장합니다. 예를 들어 원격 컬럼의 일부만 참조하도록 사용자 지정 스키마를 지정하거나, 스키마 추론을 사용해 컬럼 이름과 이에 대응하는 ClickHouse 타입을 자동으로 결정할 수 있습니다. 런타임 동작은 다시 수동형과 능동형으로 나눌 수 있습니다. 수동형 테이블 엔진은 쿼리를 원격 시스템으로 전달하고, 그 결과로 로컬 프록시 테이블을 채웁니다. 반면 능동형 테이블 엔진은 원격 시스템에서 주기적으로 데이터를 가져오거나, 예를 들어 PostgreSQL의 논리적 복제 프로토콜을 통해 원격 변경 사항을 구독합니다. 그 결과 로컬 테이블에는 원격 테이블의 전체 복사본이 저장됩니다. 둘째, 통합 데이터베이스 엔진은 원격 데이터 저장소의 테이블 스키마에 있는 모든 테이블을 ClickHouse에 매핑합니다. 앞선 방식과 달리, 일반적으로 원격 데이터 저장소가 관계형 데이터베이스여야 하며 DDL 문도 제한적으로 지원합니다. 셋째, 딕셔너리는 해당 통합 테이블 함수나 엔진을 통해 거의 모든 종류의 데이터 소스에 대해 임의의 쿼리를 사용해 채울 수 있습니다. 이 경우 데이터가 일정한 간격으로 원격 저장소에서 가져와지므로 런타임 동작은 능동형입니다. 데이터 포맷. 타사 시스템과 상호작용하려면 최신 분석 데이터베이스는 어떤 포맷의 데이터든 처리할 수 있어야 합니다. ClickHouse는 네이티브 포맷 외에도 CSV, JSON, Parquet, Avro, ORC, Arrow, Protobuf를 포함한 90개 이상의 포맷을 지원합니다. 각 포맷은 입력 형식(ClickHouse가 읽을 수 있는 포맷), 출력 형식(ClickHouse가 내보낼 수 있는 포맷), 또는 둘 다일 수 있습니다. Parquet와 같은 일부 분석 지향 포맷은 쿼리 처리와도 통합되어 있어, 옵티마이저가 내장 통계를 활용할 수 있고 필터는 압축된 데이터에 직접 적용됩니다. 호환 인터페이스. 클라이언트는 네이티브 바이너리 wire 프로토콜과 HTTP 외에도 MySQL 또는 PostgreSQL wire 프로토콜 호환 인터페이스를 통해 ClickHouse와 상호작용할 수 있습니다. 이 호환성 기능은 공급업체가 아직 네이티브 ClickHouse 연결을 구현하지 않은 proprietary 애플리케이션(예: 일부 비즈니스 인텔리전스 도구)에서 접근을 가능하게 하는 데 유용합니다.

6 기능으로서의 성능

이 섹션에서는 성능 분석을 위한 내장 도구를 소개하고, 실제 워크로드와 벤치마크 쿼리를 사용해 성능을 평가합니다.

6.1 내장 성능 분석 도구

개별 쿼리나 백그라운드 작업의 성능 병목을 조사할 수 있는 다양한 도구가 제공됩니다. 모든 도구는 시스템 테이블을 기반으로 한 통일된 인터페이스를 통해 사용할 수 있습니다. 서버 및 쿼리 메트릭. 활성 part 수, 네트워크 처리량, 캐시 적중률과 같은 서버 수준 통계와 함께, 읽은 블록 수나 인덱스 사용 통계와 같은 쿼리별 통계도 제공됩니다. 메트릭은 동기식(요청 시)으로 계산되거나, 구성 가능한 인터벌로 비동기식 계산됩니다. 샘플링 프로파일러. 샘플링 프로파일러를 사용해 서버 스레드의 호출 스택을 수집할 수 있습니다. 결과는 필요에 따라 플레임 그래프 시각화 도구와 같은 외부 도구로 내보낼 수 있습니다. OpenTelemetry 통합. OpenTelemetry는 여러 데이터 처리 시스템에 걸쳐 데이터 행을 추적하기 위한 개방형 표준입니다 [8]. ClickHouse는 모든 쿼리 처리 단계에 대해 구성 가능한 세분화 수준으로 OpenTelemetry 로그 스팬을 생성할 수 있으며, 다른 시스템의 OpenTelemetry 로그 스팬을 수집하고 분석할 수도 있습니다. EXPLAIN 쿼리. 다른 데이터베이스와 마찬가지로 SELECT 쿼리 앞에 EXPLAIN을 붙이면 쿼리의 AST, 논리 및 물리 연산자 계획, 그리고 실행 시 동작을 자세히 확인할 수 있습니다.

6.2 벤치마크

벤치마크는 현실성이 충분하지 않다는 비판을 받아왔지만 [10, 52, 66, [74]](#page-13-24), 데이터베이스의 강점과 약점을 파악하는 데 여전히 유용합니다. 다음에서는 벤치마크를 사용해 ClickHouse의 성능을 어떻게 평가하는지 설명합니다.

6.2.1 비정규화된 테이블

비정규화된 팩트 테이블에 대한 필터 및 집계 쿼리는 역사적으로 ClickHouse의 대표적인 사용 사례였습니다. 여기서는 이러한 유형의 전형적인 워크로드인 ClickBench의 런타임을 보고합니다. ClickBench는 클릭스트림 및 트래픽 분석에 사용되는 애드혹 및 주기적 보고 쿼리를 시뮬레이션합니다. 이 벤치마크는 웹에서 가장 큰 분석 플랫폼 중 하나에서 가져온 익명화된 1억 건의 페이지 조회 기록이 있는 테이블에 대해 실행되는 43개의 쿼리로 구성됩니다. 온라인 대시보드 [17]에는 2024년 6월 기준으로 45개가 넘는 상용 및 연구용 데이터베이스의 측정값(콜드/핫 런타임, 데이터 가져오기 시간, 디스크상 크기)이 표시됩니다. 결과는 공개적으로 이용 가능한 데이터 세트와 쿼리 [16]를 바탕으로 독립적인 기여자들이 제출합니다. 이 쿼리들은 순차 스캔 및 인덱스 스캔 접근 경로를 테스트하며, CPU, IO 또는 메모리 병목이 발생하는 관계형 연산자를 정기적으로 드러냅니다. Figure 10은 분석 워크로드에 자주 사용되는 데이터베이스에서 모든 ClickBench 쿼리를 순차적으로 실행했을 때의 총 상대 콜드 및 핫 런타임을 보여줍니다. 측정은 16 vCPU, 32 GB RAM, 5000 IOPS / 1000 MiB/s 디스크를 갖춘 단일 노드 AWS EC2 c6a.4xlarge 인스턴스에서 수행되었습니다. Redshift(ra3.4xlarge, 12 vCPU, 96 GB RAM) 및 Snowfake(warehouse size S: 2x8 vCPU, 2x16 GB RAM)에도 비슷한 시스템이 사용되었습니다. 물리적 데이터베이스 설계는 최소한으로만 튜닝했으며, 예를 들어 프라이머리 키는 지정했지만 개별 컬럼의 압축을 변경하거나 프로젝션 또는 스키핑 인덱스를 생성하지는 않았습니다. 또한 각 콜드 쿼리 실행 전에 Linux 페이지 캐시를 플러시했지만, 데이터베이스나 운영 체제의 설정은 조정하지 않았습니다. 각 쿼리에서는 데이터베이스 전체에서 가장 빠른 런타임을 기준선으로 사용합니다. 다른 데이터베이스의 상대 쿼리 런타임은 ( + 10)/(_ + 10)으로 계산합니다. 데이터베이스의 총 상대 런타임은 쿼리별 비율의 기하평균입니다. 연구용 데이터베이스 Umbra [54]가 전체적으로 가장 우수한 핫 런타임을 달성했지만, ClickHouse는 핫 및 콜드 런타임 모두에서 다른 모든 프로덕션급 데이터베이스를 능가합니다.
Figure 10: ClickBench의 상대 콜드 및 핫 런타임.
시간이 지남에 따라 더 다양한 워크로드에서 SELECT의 성능을 추적하기 위해, 사용합니다 VersionsBench [19]라고 하는 4개의 벤치마크 조합을. 이 벤치마크는 새 릴리스가 게시될 때마다 성능을 평가하고 [20] 잠재적으로 성능을 저하시킨 코드 변경을 식별하기 위해 매월 한 번 실행됩니다. 개별 벤치마크는 다음과 같습니다. 1. ClickBench(위에서 설명), 2. MgBench [21] 쿼리 15개, 3. 6억 행 규모의 비정규화된 Star Schema Benchmark [57] 팩트 테이블에 대한 쿼리 13개. 4. 34억 행 규모의 NYC Taxi Rides에 대한 쿼리 4개 [70]. Figure 11은 2018년 3월부터 2024년 3월까지 77개 ClickHouse 버전에 대한 VersionsBench 런타임의 변화를 보여줍니다. 개별 쿼리의 상대 런타임 차이를 보정하기 위해, 모든 버전에서의 최소 쿼리 런타임 대비 비율을 가중치로 하는 기하평균을 사용해 런타임을 정규화합니다. 지난 6년 동안 VersionsBench의 성능은 1.72 × 향상되었습니다. 장기 지원(LTS) 릴리스의 날짜는 x축에 표시되어 있습니다. 일부 기간에는 성능이 일시적으로 저하되었지만, 일반적으로 LTS 릴리스는 이전 LTS 버전과 비슷하거나 더 나은 성능을 보입니다. 2022년 8월의 큰 개선은 섹션 4.4.에서 설명한 컬럼별 필터 평가 기법에 의해 발생했습니다.
그림 11: VersionsBench 2018-2024의 상대 핫 런타임.

6.2.2 정규화된 테이블

전통적인 데이터 웨어하우징에서는 데이터를 흔히 스타 스키마 또는 snowfake 스키마로 모델링합니다. 여기서는 TPC-H 쿼리(스케일 팩터 100)의 런타임을 제시하지만, 정규화된 테이블이 ClickHouse의 새로운 사용 사례로 떠오르고 있다는 점도 함께 언급합니다. 그림 124.4절에서 설명한 병렬 해시 조인 알고리즘을 기반으로 한 TPC-H 쿼리의 hot 런타임을 보여줍니다. 측정은 64 vCPU, 128 GB RAM, 5000 IOPS / 1000 MiB/s 디스크를 갖춘 단일 노드 AWS EC2 c6i.16xlarge 인스턴스에서 수행했습니다. 5회 실행 중 가장 빠른 결과를 기록했습니다. 참고를 위해 비슷한 규모의 Snowfake 시스템(warehouse 크기 L, 8x8 vCPU, 8x16 GB RAM)에서도 동일한 측정을 수행했습니다. 11개 쿼리의 결과는 표에서 제외했습니다. 쿼리 Q2, Q4, Q13, Q17, Q20-22에는 ClickHouse v24.6 기준으로 지원되지 않는 상관 서브쿼리가 포함되어 있습니다. 쿼리 Q7-Q9 및 Q19는 조인 재정렬(join reordering)과 조인 프레디케이트 푸시다운(join predicate pushdown) 같은 조인 관련 확장 계획 수준 최적화가 있어야 실용적인 런타임을 달성할 수 있지만, 이 두 기능은 ClickHouse v24.6 기준으로 아직 제공되지 않습니다. 서브쿼리 자동 decorrelation과 조인에 대한 더 나은 옵티마이저 지원은 2024년에 구현될 예정입니다 [18]. 나머지 11개 쿼리 중 5개(6개) 쿼리는 ClickHouse(Snowfake)에서 더 빠르게 실행되었습니다. 앞서 언급한 최적화가 성능에 매우 중요하다는 점은 잘 알려져 있으므로 [27], 이러한 기능이 구현되면 해당 쿼리들의 런타임은 더욱 개선될 것으로 예상합니다.
그림 12: TPC-H 쿼리의 hot 런타임(초).
분석용 데이터베이스는 지난 수십 년간 학계와 산업계 모두에서 큰 관심을 받아 왔습니다 [1]. Sybase IQ [48], Teradata [72], Vertica [42], Greenplum [47] 같은 초기 시스템은 온프레미스 특성상 비용이 많이 드는 배치 ETL 작업과 제한적인 탄력성이 특징이었습니다. 2010년대 초반에는 Snowfake [22], BigQuery [49], Redshift [4]와 같은 클라우드 네이티브 데이터 웨어하우스와 서비스형 데이터베이스(DBaaS)가 등장하면서, 조직이 분석을 수행하는 데 드는 비용과 복잡성이 크게 줄어들었고, 동시에 고가용성과 자동 리소스 스케일링의 이점도 누릴 수 있게 되었습니다. 보다 최근에는 분석 실행 커널(예: Photon [5], Velox [62])이 서로 다른 분석, 스트리밍, 머신 러닝 애플리케이션에서 활용할 수 있는 공통 데이터 처리 기능을 제공합니다. 목표와 설계 원칙 측면에서 ClickHouse와 가장 유사한 데이터베이스는 Druid [78]와 Pinot [34]입니다. 두 시스템 모두 높은 데이터 수집 속도를 바탕으로 실시간 분석을 목표로 합니다. ClickHouse와 마찬가지로 테이블은 세그먼트라고 하는 수평 파트로 분할됩니다. ClickHouse는 더 작은 파트를 지속적으로 머지하고, 선택적으로 3.3절의 기법을 사용해 데이터 볼륨을 줄이지만, Druid와 Pinot에서는 파트가 영구적으로 불변으로 유지됩니다. 또한 Druid와 Pinot는 테이블을 생성, 변경, 검색하기 위해 특수화된 노드가 필요한 반면, ClickHouse는 이러한 작업에 단일 바이너리를 사용합니다. Snowfake [22]는 공유 디스크 아키텍처를 기반으로 하는 널리 사용되는 독점형 클라우드 데이터 웨어하우스입니다. 테이블을 마이크로 파티션으로 나누는 방식은 ClickHouse의 파트 개념과 유사합니다. Snowfake는 영속성을 위해 hybrid PAX 페이지 [3]를 사용하는 반면, ClickHouse의 저장 포맷은 엄격한 열 지향 방식입니다. Snowfake는 또한 자동으로 생성되는 경량 인덱스 [31, [51]](#page-13-14)를 활용한 로컬 캐싱과 데이터 프루닝을 우수한 성능의 원천으로 강조합니다. ClickHouse의 프라이머리 키와 유사하게, 사용자는 동일한 값을 가진 데이터가 함께 배치되도록 클러스터형 인덱스를 선택적으로 생성할 수 있습니다. Photon [5]과 Velox [62]는 복잡한 데이터 관리 시스템의 구성 요소로 사용되도록 설계된 쿼리 실행 엔진입니다. 두 시스템 모두 입력으로 쿼리 계획을 전달받은 뒤, 로컬 노드에서 Parquet(Photon) 또는 Arrow(Velox) 파일 [46]에 대해 이를 실행합니다. ClickHouse는 이러한 범용 포맷의 데이터를 활용하고 생성할 수 있지만, 저장에는 네이티브 파일 포맷을 선호합니다. Velox와 Photon은 쿼리 계획을 최적화하지 않지만(Velox는 기본적인 표현식 최적화를 수행함), 데이터 특성에 따라 컴퓨트 커널을 동적으로 전환하는 것과 같은 런타임 적응 기법을 활용합니다. 이와 비슷하게, ClickHouse의 계획 연산자는 쿼리 메모리 사용량에 따라 주로 외부 집계 또는 조인 연산자로 전환하기 위해 런타임에 다른 연산자를 생성할 수 있습니다. Photon 논문은 코드 생성 설계 [38, 41, [53]](#page-13-0)가 인터프리트 방식의 벡터화 설계 [11]보다 개발과 디버깅이 더 어렵다고 지적합니다. Velox의 실험적 코드 생성 지원은 런타임에 생성된 C++ 코드로부터 만들어진 공유 라이브러리를 빌드하고 링크하는 방식인 반면, ClickHouse는 LLVM의 요청 시 컴파일 API와 직접 상호작용합니다. DuckDB [67] 역시 호스트 프로세스에 내장되도록 설계되었지만, 여기에 더해 쿼리 최적화와 트랜잭션도 제공합니다. 이는 가끔 발생하는 OLTP SQL 문이 섞인 OLAP 쿼리를 처리하도록 설계되었습니다. 이에 따라 DuckDB는 DataBlocks [43] 저장 포맷을 선택했으며, 이 포맷은 하이브리드 워크로드에서 우수한 성능을 내기 위해 순서 보존 딕셔너리나 frame-of-reference [2] 같은 경량 압축 방식을 사용합니다. 반면 ClickHouse는 append-only 사용 사례, 즉 업데이트와 삭제가 없거나 매우 드문 경우에 최적화되어 있습니다. 블록은 LZ4와 같은 고비용 압축 기법으로 압축되며, 사용자가 빈번한 쿼리의 속도를 높이기 위해 데이터 프루닝을 적극적으로 활용하고, 나머지 쿼리에서는 압축 해제 비용보다 I/O 비용이 훨씬 크다고 가정합니다. 또한 DuckDB는 Hyper의 MVCC 방식 [55]에 기반한 직렬화 가능 트랜잭션을 제공하는 반면, ClickHouse는 snapshot isolation만 제공합니다.

8 결론 및 전망

오픈소스 고성능 OLAP 데이터베이스인 ClickHouse의 아키텍처를 소개했습니다. 쓰기에 최적화된 스토리지 계층과 최첨단 벡터화 쿼리 엔진을 기반으로 하는 ClickHouse는 높은 수집 속도로 페타바이트 규모 데이터 세트에 대한 실시간 분석을 지원합니다. ClickHouse는 백그라운드에서 데이터를 비동기적으로 병합하고 변환하여 데이터 유지 관리와 병렬 삽입을 효율적으로 분리합니다. 또한 스토리지 계층은 희소 프라이머리 인덱스, 스키핑 인덱스, 프로젝션 테이블을 활용해 적극적인 데이터 프루닝을 수행할 수 있도록 합니다. 업데이트와 삭제, 멱등 삽입, 그리고 고가용성을 위한 노드 간 데이터 복제에 대한 ClickHouse의 구현도 설명했습니다. 쿼리 처리 레이어는 다양한 기법을 사용해 쿼리를 최적화하고, 모든 서버 및 클러스터 리소스 전반에서 실행을 병렬화합니다. 통합 테이블 엔진과 함수는 다른 데이터 관리 시스템 및 데이터 포맷과 자연스럽게 상호 운용할 수 있는 편리한 방법을 제공합니다. 벤치마크를 통해 ClickHouse가 시장에서 가장 빠른 분석 데이터베이스 중 하나임을 입증했으며, 지난 수년간 실제 ClickHouse 배포 환경에서 일반적인 쿼리의 성능이 크게 향상되었음을 보여주었습니다. 2024년에 계획된 모든 기능과 개선 사항은 공개 로드맵 [18]에서 확인할 수 있습니다. 계획된 개선 사항에는 사용자 트랜잭션 지원, 대체 쿼리 언어로서의 PromQL [69], 반정형 데이터(예: JSON)를 위한 새로운 데이터 타입, 조인에 대한 더 나은 계획 수준 최적화, 그리고 경량 삭제를 보완하는 경량 업데이트 구현이 포함됩니다.

감사의 말

버전 24.6 기준으로 SELECT * FROM system.contributors는 ClickHouse에 기여한 1994명의 개인을 반환합니다. 이 데이터베이스를 함께 만들어 오는 데 큰 노력과 헌신을 기울여 주신 ClickHouse Inc.의 전체 엔지니어링 팀과 ClickHouse의 훌륭한 오픈 소스 커뮤니티에 감사드립니다.

참고

  • 1 Daniel Abadi, Peter Boncz, Stavros Harizopoulos, Stratos Idreaos, 및 Samuel Madden. 2013. The Design and Implementation of Modern Column-Oriented Database Systems. https://doi.org/10.1561/9781601987556
  • 2 Daniel Abadi, Samuel Madden, Miguel Ferreira. 2006. Integrating Compression and Execution in Column-Oriented Database Systems. In Proceedings of the 2006 ACM SIGMOD International Conference on Management of Data (SIGMOD ‘06). 671–682.https://doi.org/10.1145/1142473.1142548
  • 3 Anastassia Ailamaki, David J. DeWitt, Mark D. Hill, and Marios Skounakis. 2001. Weaving Relations for Cache Performance. In Proceedings of the 27th International Conference on Very Large Data Bases (VLDB ‘01). Morgan Kaufmann Publishers Inc., San Francisco, CA, USA, 169–180.
  • 4 Nikos Armenatzoglou, Sanuj Basu, Naga Bhanoori, Mengchu Cai, Naresh Chainani, Kiran Chinta, Venkatraman Govindaraju, Todd J. Green, Monish Gupta, Sebastian Hillig, Eric Hotinger, Yan Leshinksy, Jintian Liang, Michael McCreedy, Fabian Nagel, Ippokratis Pandis, Panos Parchas, Rahul Pathak, Orestis Polychroniou, Foyzur Rahman, Gaurav Saxena, Gokul Soundararajan, Sriram Subramanian, and Doug Terry. 2022. Amazon Redshift Re-Invented. In Proceedings of the 2022 International Conference on Management of Data (Philadelphia, PA, USA) (SIGMOD ‘22). Association for Computing Machinery, New York, NY, USA, 2205–2217. https://doi.org/10.1145/3514221.3526045
  • 5 Alexander Behm, Shoumik Palkar, Utkarsh Agarwal, Timothy Armstrong, David Cashman, Ankur Dave, Todd Greenstein, Shant Hovsepian, Ryan Johnson, Arvind Sai Krishnan, Paul Leventis, Ala Luszczak, Prashanth Menon, Mostafa Mokhtar, Gene Pang, Sameer Paranjpye, Greg Rahn, Bart Samwel, Tom van Bussel, Herman van Hovell, Maryann Xue, Reynold Xin, and Matei Zaharia. 2022. Photon: A Fast Query Engine for Lakehouse Systems (SIGMOD ‘22). Association for Computing Machinery, New York, NY, USA, 2326–2339. https://doi.org/10.1145/3514221. 3526054
  • 6 Philip A. Bernstein, Nathan Goodman. 1981. Concurrency Control in Distributed Database Systems. ACM Computing Survey 13, 2 (1981), 185–221. https://doi.org/10.1145/356842.356846
  • 7 Spyros Blanas, Yinan Li, and Jignesh M. Patel. 2011. 멀티코어 CPU를 위한 주 메모리 해시 조인 알고리즘의 설계 및 평가. In Proceedings of the 2011 ACM SIGMOD International Conference on Management of Data (Athens, Greece) (SIGMOD ‘11). Association for Computing Machinery, New York, NY, USA, 37–48. https://doi.org/10.1145/1989323.1989328
  • 8 Daniel Gomez Blanco. 2023. Practical OpenTelemetry. Springer Nature.
  • 9 Burton H. Bloom. 1970. 허용 가능한 오류가 있는 Hash 코딩의 공간/시간 절충. Commun. ACM 13, 7 (1970), 422–426. https://doi.org/10.1145/362686. 362692
  • 10 Peter Boncz, Thomas Neumann, and Orri Erling. 2014. TPC-H 분석: 영향력 있는 벤치마크에 숨겨진 메시지와 교훈. In Performance Characterization and Benchmarking. 61–76. https://doi.org/10.1007/978-3-319- 04936-6_5
  • 11 Peter Boncz, Marcin Zukowski, and Niels Nes. 2005. MonetDB/X100: 하이퍼 파이프라이닝 쿼리 실행. In CIDR.
  • 12 Martin Burtscher and Paruj Ratanaworabhan. 2007. High Throughput Compression of Double-Precision Floating-Point Data. In Data Compression Conference (DCC). 293–302. https://doi.org/10.1109/DCC.2007.44
  • 13 Jef Carpenter와 Eben Hewitt. 2016. Cassandra: The Defnitive Guide (제2판). O’Reilly Media, Inc.
  • 14 Bernadette Charron-Bost, Fernando Pedone, and André Schiper (편). 2010. 복제: 이론과 실제. Springer-Verlag.
  • 15 chDB. 2024. chDB - 내장 OLAP SQL Engine. 2024-06-20에 https://github.com/chdb-io/chdb에서 확인함
  • 16 ClickHouse. 2024. ClickBench: 분석 데이터베이스를 위한 벤치마크. 2024-06-20에 https://github.com/ClickHouse/ClickBench에서 확인함
  • 17 ClickHouse. 2024. ClickBench: 비교 측정. 2024-06-20에 https://benchmark.clickhouse.com에서 가져옴
  • 18 ClickHouse. 2024. ClickHouse 로드맵 2024 (GitHub). https://github.com/ClickHouse/ClickHouse/issues/58392에서 2024-06-20에 확인함
  • 19 ClickHouse. 2024. ClickHouse 버전 벤치마크. https://github.com/ClickHouse/ClickBench/tree/main/versions에서 2024-06-20에 확인함
  • 20 ClickHouse. 2024. ClickHouse 버전 벤치마크 결과. 2024-06-20에 https://benchmark.clickhouse.com/versions/에서 검색함
  • 21 Andrew Crotty. 2022. MgBench. 2024-06-20에 https://github.com/ andrewcrotty/mgbench에서 확인함
  • 22 Benoit Dageville, Thierry Cruanes, Marcin Zukowski, Vadim Antonov, Artin Avanes, Jon Bock, Jonathan Claybaugh, Daniel Engovatov, Martin Hentschel, Jiansheng Huang, Allison W. Lee, Ashish Motivala, Abdul Q. Munir, Steven Pelley, Peter Povinec, Greg Rahn, Spyridon Triantafyllis, and Philipp Unterbrunner. 2016. The Snowfake Elastic Data Warehouse. In Proceedings of the 2016 International Conference on Management of Data (San Francisco, California, USA) (SIGMOD ‘16). Association for Computing Machinery, New York, NY, USA, 215–226. https: //doi.org/10.1145/2882903.2903741
  • 23 Patrick Damme, Annett Ungethüm, Juliana Hildebrandt, Dirk Habich, Wolfgang Lehner. 2019. From a Comprehensive Experimental Survey to a Cost-Based Selection Strategy for Lightweight Integer Compression Algorithms. ACM Trans. Database Syst. 44, 3, Article 9 (2019), 46쪽. https://doi.org/10.1145/3323991
  • 24 Philippe Dobbelaere and Kyumars Sheykh Esmaili. 2017. Kafka와 RabbitMQ: 업계의 두 가지 참조용 발행/구독 구현에 대한 비교 연구: 산업 논문 (DEBS ‘17). Association for Computing Machinery, 미국 뉴욕주 뉴욕, 227–238. https://doi.org/10.1145/3093742.3093908
  • 25 LLVM 문서. 2024. LLVM의 자동 벡터화. 2024-06-20에 https://llvm.org/docs/Vectorizers.html에서 검색함
  • 26 Siying Dong, Andrew Kryczka, Yanqin Jin, 및 Michael Stumm. 2021. RocksDB: Evolution of Development Priorities in a Key-value Store Serving Large-scale Applications. ACM Transactions on Storage 17, 4, Article 26 (2021), 32쪽. https://doi.org/10.1145/3483840
  • 27 Markus Dreseler, Martin Boissier, Tilmann Rabl, 및 Matthias Ufacker. 2020. TPC-H 병목 지점과 그 최적화의 정량화. Proc. VLDB Endow. 13, 8 (2020), 1206–1220. https://doi.org/10.14778/3389133.3389138
  • 28 Ted Dunning. 2021. The t-digest: 분포에 대한 효율적인 추정. Software Impacts 7 (2021). https://doi.org/10.1016/j.simpa.2020.100049
  • 29 Martin Faust, Martin Boissier, Marvin Keller, David Schwalb, Holger Bischof, Katrin Eisenreich, Franz Färber, and Hasso Plattner. 2016. SAP HANA의 해시 인덱스를 사용한 저장 공간 축소 및 고유성 강제. In Database and Expert Systems Applications. 137–151. https://doi.org/10.1007/978-3-319-44406- 2_11
  • 30 Philippe Flajolet, Eric Fusy, Olivier Gandouet, and Frederic Meunier. 2007. HyperLogLog: the analysis of a near-optimal cardinality estimation algorithm. 수록: AofA: Analysis of Algorithms, Vol. DMTCS Proceedings vol. AH, 2007 Conference on Analysis of Algorithms (AofA 07). Discrete Mathematics and Theoretical Computer Science, 137–156. https://doi.org/10.46298/dmtcs.3545
  • 31 Hector Garcia-Molina, Jefrey D. Ullman, Jennifer Widom. 2009. Database Systems - The Complete Book (2. Ed.).
  • 32 Pawan Goyal, Harrick M. Vin, and Haichen Chen. 1996. Start-time fair queueing: a scheduling algorithm for integrated services packet switching networks. 26, 4 (1996), 157–168. https://doi.org/10.1145/248157.248171
  • 33 Goetz Graefe. 1993. Query Evaluation Techniques for Large Databases. ACM Comput. Surv. 25, 2 (1993), 73–169. https://doi.org/10.1145/152610.152611
  • 34 Jean-François Im, Kishore Gopalakrishna, Subbu Subramaniam, Mayank Shrivastava, Adwait Tumbde, Xiaotian Jiang, Jennifer Dai, Seunghyun Lee, Neha Pawar, Jialiang Li, Ravi Aringunram. 2018. Pinot: 5억 3천만 사용자를 위한 실시간 OLAP. 『2018 데이터 관리 국제 학술대회 논문집』(미국 텍사스주 휴스턴) (SIGMOD ‘18). Association for Computing Machinery, 미국 뉴욕주 뉴욕, 583–594. https://doi.org/10.1145/3183713.3190661
  • 35 ISO/IEC 9075-9:2001 2001. 정보기술 — 데이터베이스 언어 — SQL — 제9부: 외부 데이터 관리(SQL/MED). 표준. 국제표준화기구.
  • 36 Paras Jain, Peter Kraft, Conor Power, Tathagata Das, Ion Stoica, and Matei Zaharia. 2023. 레이크하우스 저장소 시스템 분석 및 비교. CIDR.
  • 37 Project Jupyter. 2024. Jupyter Notebooks. https: //jupyter.org/에서 2024-06-20에 확인함
  • 38 Timo Kersten, Viktor Leis, Alfons Kemper, Thomas Neumann, Andrew Pavlo, and Peter Boncz. 2018. 컴파일 및 벡터화된 쿼리에 대해 늘 알고 싶었지만 차마 묻지 못했던 모든 것. Proc. VLDB Endow. 11, 13 (sep 2018), 2209–2222. https://doi.org/10.14778/3275366.3284966
  • 39 Changkyu Kim, Jatin Chhugani, Nadathur Satish, Eric Sedlar, Anthony D. Nguyen, Tim Kaldewey, Victor W. Lee, Scott A. Brandt, and Pradeep Dubey. 2010. FAST: fast architecture sensitive tree search on modern CPUs and GPUs. In Proceedings of the 2010 ACM SIGMOD International Conference on Management of Data (Indianapolis, Indiana, USA) (SIGMOD ‘10). Association for Computing Machinery, New York, NY, USA, 339–350. https://doi.org/10.1145/1807167.1807206
  • 40 Donald E. Knuth. 1973. The Art of Computer Programming, Volume III: Sorting and Searching. Addison-Wesley.
  • 41 André Kohn, Viktor Leis, and Thomas Neumann. 2018. 컴파일된 쿼리의 적응형 실행. In 2018 IEEE 34th International Conference on Data Engineering (ICDE). 197–208. https://doi.org/10.1109/ICDE.2018.00027
  • 42 Andrew Lamb, Matt Fuller, Ramakrishna Varadarajan, Nga Tran, Ben Vandiver, Lyric Doshi, and Chuck Bear. 2012. Vertica 분석용 데이터베이스: 7년 후의 C-Store. Proc. VLDB Endow. 5, 12 (2012년 8월), 1790–1801. https://doi.org/10. 14778/2367502.2367518
  • 43 Harald Lang, Tobias Mühlbauer, Florian Funke, Peter A. Boncz, Thomas Neumann, and Alfons Kemper. 2016. Data Blocks: Hybrid OLTP and OLAP on Compressed Storage using both Vectorization and Compilation. In 『2016 International Conference on Management of Data』 논문집 (San Francisco, California, USA) (SIGMOD ‘16). Association for Computing Machinery, New York, NY, USA, 311–326. https://doi.org/10.1145/2882903.2882925
  • 44 Viktor Leis, Peter Boncz, Alfons Kemper, and Thomas Neumann. 2014. Morseldriven parallelism: a NUMA-aware query evaluation framework for the manycore age. In Proceedings of the 2014 ACM SIGMOD International Conference on Management of Data (Snowbird, Utah, USA) (SIGMOD ‘14). Association for Computing Machinery, New York, NY, USA, 743–754. https://doi.org/10.1145/2588555. 2610507
  • 45 Viktor Leis, Alfons Kemper, and Thomas Neumann. 2013. 적응형 래딕스 트리: 메인 메모리 데이터베이스를 위한 ARTful 인덱싱. 2013 IEEE 제29회 International Conference on Data Engineering (ICDE)에서. 38–49. https://doi.org/10.1109/ICDE. 2013.6544812
  • 46 Chunwei Liu, Anna Pavlenko, Matteo Interlandi, and Brandon Haynes. 2023. A Deep Dive into Common Open Formats for Analytical DBMSs. 16, 11 (jul 2023), 3044–3056. https://doi.org/10.14778/3611479.3611507
  • 47 Zhenghua Lyu, Huan Hubert Zhang, Gang Xiong, Gang Guo, Haozhou Wang, Jinbao Chen, Asim Praveen, Yu Yang, Xiaoming Gao, Alexandra Wang, Wen Lin, Ashwin Agrawal, Junfeng Yang, Hao Wu, Xiaoliang Li, Feng Guo, Jiang Wu, Jesse Zhang, and Venkatesh Raghavan. 2021. Greenplum: A Hybrid Database for Transactional and Analytical Workloads (SIGMOD ‘21). Association for Computing Machinery, New York, NY, USA, 2530–2542. https: //doi.org/10.1145/3448016.3457562
  • 48 Roger MacNicol and Blaine French. 2004. Sybase IQ Multiplex - 분석용으로 설계됨. In 초대용량 데이터베이스에 관한 제30회 국제 학술대회 논문집 - Volume 30 (Toronto, Canada) (VLDB ‘04). VLDB Endowment, 1227–1230.
  • 49 Sergey Melnik, Andrey Gubarev, Jing Jing Long, Geofrey Romer, Shiva Shivakumar, Matt Tolton, Theo Vassilakis, Hossein Ahmadi, Dan Delorey, Slava Min, Mosha Pasumansky, and Jef Shute. 2020. Dremel: A Decade of Interactive SQL Analysis at Web Scale. Proc. VLDB Endow. 13, 12 (aug 2020), 3461–3472. https://doi.org/10.14778/3415478.3415568
  • 50 Microsoft. 2024. Kusto Query Language. 2024-06-20에 https: //github.com/microsoft/Kusto-Query-Language에서 확인함
  • 51 Guido Moerkotte. 1998. 소규모 구체화된 집계: 데이터 웨어하우징을 위한 경량 인덱스 구조. In Proceedings of the 24rd International Conference on Very Large Data Bases (VLDB ‘98). 476–487.
  • 52 Jalal Mostafa, Sara Wehbi, Suren Chilingaryan, and Andreas Kopmann. 2022. SciTS: A Benchmark for Time-Series Databases in Scientifc Experiments and Industrial Internet of Things. In Proceedings of the 34th International Conference on Scientifc and Statistical Database Management (SSDBM ‘22). 제12번 논문. https: //doi.org/10.1145/3538712.3538723
  • 53 Thomas Neumann. 2011. efficiently Compiling efficient Query Plans for Modern Hardware. Proc. VLDB Endow. 4, 9 (2011년 6월), 539–550. https://doi.org/10.14778/ 2002938.2002940
  • 54 Thomas Neumann and Michael J. Freitag. 2020. Umbra: A Disk-Based System with In-Memory Performance. In 10th Conference on Innovative Data Systems Research, CIDR 2020, Amsterdam, The Netherlands, January 12-15, 2020, Online Proceedings. www.cidrdb.org. http://cidrdb.org/cidr2020/papers/p29-neumanncidr20.pdf
  • 55 Thomas Neumann, Tobias Mühlbauer, and Alfons Kemper. 2015. Fast Serializable Multi-Version Concurrency Control for Main-Memory Database Systems. In Proceedings of the 2015 ACM SIGMOD International Conference on Management of Data (Melbourne, Victoria, Australia) (SIGMOD ‘15). Association for Computing Machinery, New York, NY, USA, 677–689. https://doi.org/10.1145/2723372. 2749436
  • 56 GitHub의 LevelDB. 2024. LevelDB. 2024-06-20에 https://github. com/google/leveldb에서 확인함
  • 57 Patrick O’Neil, Elizabeth O’Neil, Xuedong Chen, and Stephen Revilak. 2009. The Star Schema Benchmark and Augmented Fact Table Indexing. In Performance Evaluation and Benchmarking. Springer Berlin Heidelberg, 237–252. https: //doi.org/10.1007/978-3-642-10424-4_17
  • 58 Patrick E. O’Neil, Edward Y. C. Cheng, Dieter Gawlick, and Elizabeth J. O’Neil. 1996. The log-structured Merge-Tree (LSM-tree). Acta Informatica 33 (1996), 351–385. https://doi.org/10.1007/s002360050048
  • 59 Diego Ongaro와 John Ousterhout. 2014. In Search of an Understandable Consensus Algorithm. In Proceedings of the 2014 USENIX Conference on USENIX Annual Technical Conference (USENIX ATC’14). 305–320. https://doi.org/doi/10. 5555/2643634.2643666
  • 60 Patrick O’Neil, Edward Cheng, Dieter Gawlick, and Elizabeth O’Neil. 1996. The Log-Structured Merge-Tree (LSM-Tree). Acta Inf. 33, 4 (1996), 351–385. https: //doi.org/10.1007/s002360050048
  • 61 Pandas. 2024. Pandas 데이터프레임. 2024-06-20에 https://pandas. pydata.org/에서 확인함
  • 62 Pedro Pedreira, Orri Erling, Masha Basmanova, Kevin Wilfong, Laith Sakka, Krishna Pai, Wei He 및 Biswapesh Chattopadhyay. 2022. Velox: Meta’s Unified Execution Engine. Proc. VLDB Endow. 15, 12 (2022년 8월), 3372–3384. https: //doi.org/10.14778/3554821.3554829
  • 63 Tuomas Pelkonen, Scott Franklin, Justin Teller, Paul Cavallaro, Qi Huang, Justin Meza, 및 Kaushik Veeraraghavan. 2015. Gorilla: A Fast, Scalable, in-Memory Time Series Database. Proceedings of the VLDB Endowment 8, 12 (2015), 1816–1827. https://doi.org/10.14778/2824032.2824078
  • 64 Orestis Polychroniou, Arun Raghavan, and Kenneth A. Ross. 2015. Rethinking SIMD Vectorization for In-Memory Databases. In Proceedings of the 2015 ACM SIGMOD International Conference on Management of Data (SIGMOD ‘15). 1493–1508. https://doi.org/10.1145/2723372.2747645
  • 65 PostgreSQL. 2024. PostgreSQL - Foreign Data Wrappers. 2024-06-20에 https://wiki.postgresql.org/wiki/Foreign&#95;data&#95;wrappers에서 확인함
  • 66 Mark Raasveldt, Pedro Holanda, Tim Gubner, Hannes Mühleisen. 2018. Fair Benchmarking Considered difficult: Common Pitfalls In Database Performance Testing. In Proceedings of the Workshop on Testing Database Systems (Houston, TX, USA) (DBTest’18). Article 2, 6페이지. https://doi.org/10.1145/3209950.3209955
  • 67 Mark Raasveldt 및 Hannes Mühleisen. 2019. DuckDB: An Embeddable Analytical Database (SIGMOD ‘19). Association for Computing Machinery, New York, NY, USA, 1981–1984. https://doi.org/10.1145/3299869.3320212
  • 68 Jun Rao and Kenneth A. Ross. 1999. 주기억장치(main memory)에서 의사결정 지원을 위한 캐시 인식 인덱싱. 『제25회 국제 초대형 데이터베이스 학술대회 논문집』(VLDB ‘99). 미국 캘리포니아주 샌프란시스코, 78–89.
  • 69 Navin C. Sabharwal and Piyush Kant Pandey. 2020. Working with Prometheus Query Language (PromQL). 『Monitoring Microservices and Containerized Applications』에 수록. https://doi.org/10.1007/978-1-4842-6216-0&#95;5
  • 70 Todd W. Schneider. 2022. New York City Taxi and For-Hire Vehicle Data. 2024-06-20에 다음에서 확인함: https://github.com/toddwschneider/nyc-taxi-data
  • 71 Mike Stonebraker, Daniel J. Abadi, Adam Batkin, Xuedong Chen, Mitch Cherniack, Miguel Ferreira, Edmond Lau, Amerson Lin, Sam Madden, Elizabeth O’Neil, Pat O’Neil, Alex Rasin, Nga Tran, and Stan Zdonik. 2005. C-Store: A Column-Oriented DBMS. In Proceedings of the 31st International Conference on Very Large Data Bases (VLDB ‘05). 553–564.
  • 72 Teradata. 2024. Teradata Database. 2024-06-20에 검색함 https://www. teradata.com/resources/datasheets/teradata-database
  • 73 Frederik Transier. 2010. Algorithms and Data Structures for In-Memory Text Search Engines. 박사학위 논문. https://doi.org/10.5445/IR/1000015824
  • 74 Adrian Vogelsgesang, Michael Haubenschild, Jan Finis, Alfons Kemper, Viktor Leis, Tobias Muehlbauer, Thomas Neumann, and Manuel Then. 2018. Get Real: How Benchmarks Fail to Represent the Real World. In Proceedings of the Workshop on Testing Database Systems (Houston, TX, USA) (DBTest’18). 아티클 1, 6페이지. https://doi.org/10.1145/3209950.3209952
  • 75 LZ4 웹사이트. 2024. LZ4. https://lz4.org/에서 2024-06-20에 확인함
  • 76 PRQL 웹사이트. 2024. PRQL. https://prql-lang.org에서 2024-06-20에 확인함 77 Till Westmann, Donald Kossmann, Sven Helmer, and Guido Moerkotte. 2000. The Implementation and Performance of Compressed Databases. SIGMOD Rec.
  • 29, 3 (2000년 9월), 55–67. https://doi.org/10.1145/362084.362137 78 Fangjin Yang, Eric Tschetter, Xavier Léauté, Nelson Ray, Gian Merlino, and Deep Ganguli. 2014. Druid: 실시간 분석 데이터 저장소. In 2014 ACM SIGMOD International Conference on Management of Data (Snowbird, Utah, USA) (SIGMOD ‘14) 프로시딩. Association for Computing Machinery, New York, NY, USA, 157–168. https://doi.org/10.1145/2588555.2595631
  • 79 Tianqi Zheng, Zhibin Zhang, Xueqi Cheng. 2020. SAHA: A String Adaptive Hash Table for Analytical Databases. Applied Sciences 10, 6 (2020). https: //doi.org/10.3390/app10061915
  • 80 Jingren Zhou와 Kenneth A. Ross. 2002. SIMD 명령어를 사용한 데이터베이스 연산 구현. 2002 ACM SIGMOD International Conference on Management of Data (SIGMOD ‘02) 논문집. 145–156. https://doi.org/10. 1145/564691.564709
  • 81 Marcin Zukowski, Sandor Heman, Niels Nes, and Peter Boncz. 2006. Super-Scalar RAM-CPU Cache Compression. In Proceedings of the 22nd International Conference on Data Engineering (ICDE ‘06). 59. https://doi.org/10.1109/ICDE. 2006.150
마지막 수정일 2026년 6월 10일