파트 병합

파트 병합 (Part merges)

MergeTree 엔진 계열의 테이블은 삽입 시 정렬된 불변 데이터 파트를 만들고, 모든 데이터 처리를 백그라운드 파트 병합에 맡겨요. 덕분에 쓰기가 가벼워지고 효율이 높아지죠. 이 저장 방식은 LSM 트리와 비슷하게 동작합니다. 병합이 반복되며 병합된 파트들의 트리(tree) 구조가 생기는데, 그래서 이름이 MergeTree 엔진이에요.

파트 병합이 뭐예요?

클릭하우스는 테이블당 파트 수를 조절하기 위해 백그라운드에서 작은 파트들을 큰 파트로 계속 합칩니다. 파티션별로 병합이 일어나고, 파트가 약 150GB 압축 크기에 이를 때까지 계속되죠.

파트의 병합 레벨(merge level)은 병합이 한 번 추가될 때마다 1씩 올라가요. 레벨 0은 아직 병합되지 않은 새 파트라는 뜻이고, 더 큰 파트로 병합된 기존 파트는 비활성으로 표시된 뒤 설정 가능한 시간(기본 8분) 후 삭제됩니다.

병합 모니터링

system.parts 시스템 테이블로 각 활성 파트의 병합 레벨과 저장된 행 수를 볼 수 있어요.

SELECT
    name,
    level,
    rows
FROM system.parts
WHERE (database = 'uk') AND (`table` = 'uk_price_paid_simple') AND active
ORDER BY name ASC;

처음엔 4개의 활성 파트가 각각 1번의 병합을 거쳐 있었는데, 시간이 지나면(더 이상의 삽입이 없다면) 하나의 최종 파트로 합쳐져요.

   ┌─name───────┬─level─┬─────rows─┐
1. │ all_0_23_2 │     2 │ 25248433 │
   └────────────┴───────┴──────────┘

클릭하우스 24.10부터는 내장 모니터링 대시보드에 merges 대시보드가 추가됐어요. OSS와 클라우드 모두 /merges HTTP 핸들러로 접근할 수 있고, 활성 파트 수, 파트 병합(박스 크기가 파트 크기), 쓰기 증폭을 시각화해 주죠.

병합은 어떻게 실행될까요?

클릭하우스 서버 하나는 여러 백그라운드 병합 스레드를 두고 파트 병합을 동시에 실행합니다. 각 병합 스레드는 루프를 돌아요.

  1. 파트를 메모리로 로드 — 다음에 병합할 파트를 정하고 메모리로 로드.
  2. 파트 병합 — 메모리에서 파트들을 더 큰 파트로 병합.
  3. 디스크 기록 — 병합된 파트를 디스크에 쓰고 1번으로 복귀.

CPU 코어 수와 RAM을 늘리면 백그라운드 병합 처리량을 높일 수 있어요.

병합할 모든 파트를 한 번에 메모리로 로드하지는 않을 수도 있어요. 메모리 최적화 병합(vertical merging) 은 블록 단위 청크로 파트를 로드·병합해서 메모리 사용을 줄이는 대신 병합 속도를 희생합니다.

병합 메커니즘

단일 백그라운드 병합 스레드가 파트를 병합하는 과정은 이렇게 진행돼요.

  1. 압축 해제·로드 — 병합할 파트의 압축된 바이너리 컬럼 파일을 압축 해제해 메모리로 로드.
  2. 병합 — 데이터를 더 큰 컬럼 파일로 병합.
  3. 인덱싱 — 병합된 컬럼 파일에 대한 새 스파스 프라이머리 인덱스 생성.
  4. 압축·저장 — 새 컬럼 파일과 인덱스를 압축해 병합된 데이터 파트를 나타내는 새 디렉토리에 저장.

병합 단계의 구체 동작은 사용하는 MergeTree 엔진에 따라 달라져요. 데이터가 집계되거나 오래된 버전이 대체되기도 하죠. 병합으로 파트가 합쳐질 때 보조 데이터 스킵 인덱스·컬럼 통계·체크섬·미니맥스 인덱스 같은 메타데이터도 병합된 컬럼 파일 기준으로 다시 만들어져요.

표준 병합 (Standard merges)

MergeTree 테이블의 파트 병합은 정렬 키에 정의된 테이블 전체 정렬 순서를 유지하며 컬럼을 병합하고, 새 스파스 프라이머리 인덱스를 생성한 뒤 압축·저장합니다. 예시 DDL은 정렬 키가 (town, street)MergeTree 테이블을 만들어요.

대체 병합 (Replacing merges)

ReplacingMergeTree 테이블의 병합은 표준 병합과 비슷하지만, 정렬 키가 같은 중복 행을 제거하고 가장 최신 행만 남겨요. 최신 여부는 그 행이 들어 있는 파트의 생성 타임스탬프 기준으로 판단합니다. 예시 DDL은 정렬 키가 (town, street, id)ReplacingMergeTree 테이블을 만들어요.

합산 병합 (Summing merges)

SummingMergeTree 테이블의 파트 병합 중에는 숫자 데이터가 자동으로 합산돼요. 정렬 키가 같은 모든 행을 하나의 행으로 줄이면서 숫자 컬럼 값을 합칩니다. 예시 DDL은 town을 정렬 키로 쓰는 SummingMergeTree 테이블을 만들어요.

집계 병합 (Aggregating merges)

SummingMergeTreeAggregatingMergeTree의 특수한 변형이에요. AggregatingMergeTree는 파트 병합 중 90개 이상의 집계 함수를 적용해 자동 증분 데이터 변환을 수행합니다. 정렬 키가 같은 모든 행을 하나로 줄여 부분 집계 상태(예: avg()를 위한 sumcount)를 저장하죠. 이런 상태 덕분에 백그라운드 병합을 거쳐도 정확한 결과를 얻을 수 있어요.

출처: Part merges

더 알아보기

  • 테이블 데이터 파트 — 병합의 대상이 되는 데이터 파트가 무엇인지
  • 스파스 프라이머리 인덱스 — 병합 시 새로 생성되는 인덱스
  • 쿼리 병렬 실행 — 병합 데이터를 읽는 쿼리의 병렬 처리