단계별: ClickHouse가 집계 쿼리를 병렬화하는 방법
처리 레인 간 작업 분산
n개의 병렬 처리 레인에 동적으로 분산되며, 각 레인은 데이터를 블록 단위로 스트리밍하고 처리해 최종 결과를 생성합니다:
n개의 병렬 처리 레인 수는 max_threads 설정으로 제어되며, 기본적으로 서버에서 ClickHouse가 사용할 수 있는 단일 CPU의 코어 수(스레드 수)와 일치합니다. 위 예시에서는 4개의 코어를 가정합니다.
8개의 코어가 있는 머신에서는 더 많은 레인이 데이터를 병렬로 처리하므로 쿼리 처리량이 대략 2배로 증가합니다(메모리 사용량도 그에 따라 함께 증가합니다):
효율적인 레인 분산은 CPU 활용도를 극대화하고 전체 쿼리 시간을 줄이는 핵심입니다.
세그먼트된 테이블에서 쿼리 처리하기
처음 쿼리를 받은 서버는 세그먼트에서 나온 모든 하위 결과를 수집해 최종 전역 결과로 결합합니다. 쿼리 부하를 세그먼트 전체에 분산하면 병렬성을 수평으로 확장할 수 있으며, 특히 처리량이 높은 환경에서 효과적입니다.
ClickHouse Cloud는 세그먼트 대신 병렬 레플리카를 사용합니다ClickHouse Cloud에서는 동일한 병렬성을 병렬 레플리카를 통해 구현하며, 이는 shared-nothing 클러스터의 세그먼트와 유사하게 동작합니다. 각 ClickHouse Cloud 레플리카(무상태 컴퓨트 노드)는 데이터의 일부를 병렬로 처리하고, 독립적인 세그먼트와 마찬가지로 최종 결과 생성에 기여합니다.
쿼리 병렬성 모니터링
- ① ClickHouse는 3개의 데이터 범위에 걸쳐 3,609개의 그래뉼(트레이스 로그에서는 마크로 표시됨)을 읽어야 합니다.
- ② 59개의 CPU 코어가 있으므로 이 작업은 59개의 병렬 처리 스트림에 분산되며, 각 레인에 하나씩 할당됩니다.
× 59로 표시된 연산자는 59개의 병렬 처리 레인에서 서로 겹치지 않는 데이터 영역에 대해 동시에 실행됩니다. 이는 max_threads 값을 반영하며, 쿼리의 각 단계가 CPU 코어 전반에서 어떻게 병렬화되는지 보여줍니다.
ClickHouse의 내장 web UI (/play endpoint에서 사용 가능)는 위의 물리적 계획을 그래픽으로 시각화해 표시할 수 있습니다. 이 예시에서는 시각화를 간결하게 유지하기 위해 max_threads를 4로 설정하여 병렬 처리 레인 4개만 표시합니다:
참고: 이 시각화는 왼쪽에서 오른쪽으로 읽으십시오. 각 행은 데이터 블록을 순차적으로 스트리밍하면서 필터링, 집계, 최종 처리 단계와 같은 변환을 적용하는 병렬 처리 레인을 나타냅니다. 이 예시에서는 max_threads = 4 설정에 해당하는 4개의 병렬 레인을 확인할 수 있습니다.
처리 레인 간 부하 분산
Resize 연산자는 데이터 블록 스트림을 재파티셔닝하고 재분배하여 처리 레인이 고르게 활용되도록 합니다. 이러한 재균형은 데이터 범위별로 쿼리 프레디케이트와 일치하는 행 수가 다를 때 특히 중요합니다. 그렇지 않으면 일부 레인에는 부하가 집중되고 다른 레인은 유휴 상태로 남을 수 있습니다. 작업을 재분배하면 더 빠른 레인이 더 느린 레인의 작업을 사실상 나눠 처리하게 되어 전체 쿼리 런타임이 최적화됩니다.
max_threads가 항상 준수되지는 않는 이유
n개의 병렬 처리 레인 수는 max_threads 설정으로 제어되며, 기본적으로 서버에서 ClickHouse가 사용할 수 있는 CPU 코어 수와 같습니다:
max_threads 값이 적용되지 않을 수 있습니다:
max_threads를 59로 설정했더라도 ClickHouse는 데이터를 스캔할 때 동시 스트림을 30개만 사용합니다.
이제 쿼리를 실행해 보겠습니다:
max_threads 값과 관계없이, ClickHouse는 이를 뒷받침할 만큼 데이터가 충분할 때에만 추가 처리 레인을 할당합니다. max_threads의 “max”는 실제 사용되는 스레드 수를 보장한다는 뜻이 아니라 상한을 의미합니다.
여기서 “충분한 데이터”는 주로 두 가지 설정으로 결정되며, 각 처리 레인이 처리해야 하는 최소 행 수(기본값 163,840)와 최소 바이트 수(기본값 2,097,152)를 정의합니다.
shared-nothing 클러스터의 경우:
공유 스토리지를 사용하는 클러스터의 경우(예: ClickHouse Cloud):
- merge_tree_min_rows_for_concurrent_read_for_remote_filesystem
- merge_tree_min_bytes_for_concurrent_read_for_remote_filesystem
max_threads를 완전히 준수하면서 59개의 동시 스트림을 사용해 데이터를 스캔합니다.
이는 작은 데이터셋에 대한 쿼리에서는 ClickHouse가 의도적으로 동시성을 제한한다는 점을 보여줍니다. 설정 재정의는 비효율적인 실행이나 리소스 경합을 초래할 수 있으므로 테스트 용도로만 사용하고, 프로덕션 환경에서는 사용하지 마십시오.
핵심 요약
- ClickHouse는
max_threads에 연결된 처리 레인을 사용해 쿼리를 병렬로 실행합니다. - 실제 레인 수는 처리할 데이터로 선택된 데이터의 크기에 따라 달라집니다.
- 레인 사용 방식을 분석하려면
EXPLAIN PIPELINE과 트레이스 로그를 사용하세요.
더 많은 정보를 확인할 수 있는 곳
- 쿼리 처리 레이어 – VLDB 2024 논문(웹 버전) - 스케줄링, 파이프라이닝, 연산자 설계를 포함해 ClickHouse의 내부 실행 모델을 자세히 설명합니다.
- 부분 집계 상태 설명 - 부분 집계 상태가 처리 레인 전반에서 효율적인 병렬 실행을 어떻게 가능하게 하는지 기술적으로 자세히 설명합니다.
- ClickHouse 쿼리 처리의 전체 단계를 자세히 설명하는 비디오 튜토리얼: