파츠 머지(Part Merges)
파츠 머지(Part Merges)
ClickHouse는 모든 데이터 처리를 백그라운드 파츠 머지에 맡김으로써 가벼운 쓰기와 초고속 삽입을 실현합니다. 이 문서에서는 머지가 무엇인지, 어떻게 동작하는지, 그리고 MergeTree 계열 엔진마다 머지가 어떻게 다른지 설명할게요.
출처: 문서
본문
ClickHouse의 파츠 머지란 무엇인가요?
ClickHouse는 쿼리뿐 아니라 삽입에서도 LSM 트리와 유사하게 동작하는 스토리지 계층 덕분에 빠릅니다: ① (MergeTree 엔진 계열 테이블로의) 삽입은 정렬되고 변경 불가능한 데이터 파츠를 만듭니다. ② 모든 데이터 처리는 백그라운드 파츠 머지로 오프로드됩니다. 이 덕분에 데이터 쓰기는 가볍고 매우 효율적입니다. 테이블당 파츠 수를 제어하고 ②를 구현하기 위해 ClickHouse는 백그라운드에서 (파티션별로) 작은 파츠들을 지속적으로 더 큰 파츠로 병합하여 압축 크기가 약 ~150 GB에 도달할 때까지 진행합니다. 다음 다이어그램이 이 백그라운드 머지 과정을 그려 보여줍니다.
파츠의 머지 레벨(merge level)은 추가 머지가 발생할 때마다 1씩 증가합니다. 레벨 0은 파츠가 새 것이고 아직 머지되지 않았음을 뜻합니다. 더 큰 파츠로 머지된 파츠는 비활성으로 표시되고 설정 가능한 시간(기본 8분) 후에 삭제됩니다. 시간이 지나면 이 과정은 머지된 파츠의 트리(tree) 를 만듭니다. 그래서 머지 트리(merge tree) 테이블이라는 이름이 붙었습니다.
머지 모니터링하기
테이블 파츠란 무엇인가 예제에서 ClickHouse가 모든 테이블 파츠를 parts 시스템 테이블에 추적한다는 것을 보여주었습니다. 다음 쿼리를 사용해 예제 테이블의 각 활성 파츠에 대한 머지 레벨과 저장된 행 수를 가져왔습니다:
SELECT
name,
level,
rows
FROM system.parts
WHERE (database = 'uk') AND (`table` = 'uk_price_paid_simple') AND active
ORDER BY name ASC;
이전에 문서화한 쿼리 결과는 예제 테이블에 네 개의 활성 파츠가 있으며, 각각 초기 삽입된 파츠들의 단일 머지로 생성되었음을 보여줍니다:
┌─name────────┬─level─┬────rows─┐
1. │ all_0_5_1 │ 1 │ 6368414 │
2. │ all_12_17_1 │ 1 │ 6442494 │
3. │ all_18_23_1 │ 1 │ 5977762 │
4. │ all_6_11_1 │ 1 │ 6459763 │
└─────────────┴───────┴─────────┘
쿼리를 다시 실행하면 (테이블에 추가 삽입이 없다면) 네 개의 파츠가 이후 단일 최종 파츠로 머지되었음을 보여줍니다:
┌─name───────┬─level─┬─────rows─┐
1. │ all_0_23_2 │ 2 │ 25248433 │
└────────────┴───────┴──────────┘
ClickHouse 24.10에서는 내장 모니터링 대시보드에 새로운 merges 대시보드가 추가되었습니다. OSS와 Cloud 모두 /merges HTTP 핸들러로 제공되며, 이를 통해 예제 테이블의 모든 파츠 머지를 시각화할 수 있습니다. 위에 기록된 대시보드는 초기 데이터 삽입부터 단일 파츠로의 최종 머지까지 전체 과정을 담습니다: ① 활성 파츠 수. ② 파츠 머지(상자의 크기는 파츠 크기를 반영). ③ 쓰기 증폭(Write amplification).
동시 머지 (Concurrent merges)
단일 ClickHouse 서버는 여러 백그라운드 머지 스레드를 사용해 동시 파츠 머지를 실행합니다. 각 머지 스레드는 루프를 실행합니다:
1 파츠를 메모리에 로드
다음에 머지할 파츠를 결정하고 이 파츠들을 메모리에 로드합니다.
2 파츠 머지
메모리에서 파츠들을 더 큰 파츠로 병합합니다.
3 디스크에 쓰기
머지된 파츠를 디스크에 쓴 다음 1단계로 돌아갑니다.
CPU 코어 수와 RAM 크기를 늘리면 백그라운드 머지 처리량을 높일 수 있다는 점을 기억하세요.
메모리 최적화 머지 (Memory optimized merges)
ClickHouse는 이전 예제처럼 머지할 모든 파츠를 반드시 한 번에 메모리에 로드하지는 않습니다. 메모리 소비를 줄이기 위해(머지 속도를 희생하면서) 여러 요인에 기반해 이른바 수직 머지(vertical merging)는 파츠를 한 번에가 아니라 블록 단위로 청크(chunk)로 로드하고 병합합니다.
머지 메커니즘 (Merge mechanics)
아래 다이어그램은 ClickHouse의 단일 백그라운드 머지 스레드가 (기본적으로 수직 머지 없이) 파츠를 어떻게 병합하는지 보여줍니다. 파츠 머지는 여러 단계로 수행됩니다:
1 압축 해제 및 로드
머지할 파츠들의 압축된 바이너리 컬럼 파일을 압축 해제하고 메모리에 로드합니다.
2 병합
데이터를 더 큰 컬럼 파일로 병합합니다.
3 인덱싱
병합된 컬럼 파일에 대해 새로운 희소 프라이머리 인덱스를 생성합니다.
4 압축 및 저장
새 컬럼 파일과 인덱스를 압축하고 머지된 데이터 파트를 나타내는 새 디렉터리에 저장합니다.
보조 데이터 스킵핑 인덱스, 컬럼 통계, 체크섬, min-max 인덱스 같은 데이터 파츠의 추가 메타데이터도 병합된 컬럼 파일을 기반으로 다시 생성됩니다. 단순화를 위해 이 세부 사항은 생략했습니다. 머지 단계의 메커니즘은 사용된 특정 MergeTree 엔진에 따라 다릅니다. 엔진마다 머지를 다르게 처리하기 때문입니다. 예를 들어 오래된 행은 집계되거나 교체될 수 있습니다. 앞서 언급했듯이 이 접근 방식은 모든 데이터 처리를 백그라운드 머지로 오프로드하여 쓰기 작업을 가볍고 효율적으로 유지함으로써 초고속 삽입을 가능하게 합니다. 이제 MergeTree 계열의 특정 엔진들의 머지 메커니즘을 간략히 살펴보겠습니다.
표준 머지 (Standard merges)
아래 다이어그램은 표준 MergeTree 테이블의 파츠가 어떻게 병합되는지 보여줍니다. 위 다이어그램의 DDL 문은 정렬 키 (town, street)를 가진 MergeTree 테이블을 만듭니다. 이는 디스크상 데이터가 이 컬럼들로 정렬되고 상응하는 희소 프라이머리 인덱스가 생성된다는 뜻입니다. ① 압축 해제되고 미리 정렬된 테이블 컬럼들이 ② 테이블의 정렬 키가 정의하는 전역 정렬 순서를 유지하며 병합되고, ③ 새 희소 프라이머리 인덱스가 생성되며, ④ 병합된 컬럼 파일과 인덱스가 압축되어 디스크에 새 데이터 파트로 저장됩니다.
교체 머지 (Replacing merges)
ReplacingMergeTree 테이블의 파츠 머지는 표준 머지와 유사하게 동작하지만, 각 행의 가장 최신 버전만 유지하고 오래된 버전은 버립니다. 위 다이어그램의 DDL 문은 정렬 키 (town, street, id)를 가진 ReplacingMergeTree 테이블을 만듭니다. 즉 디스크상 데이터가 이 컬럼들로 정렬되고 상응하는 희소 프라이머리 인덱스가 생성됩니다. ② 병합은 표준 MergeTree 테이블과 유사하게, 압축 해제되고 미리 정렬된 컬럼을 전역 정렬 순서를 유지하며 결합합니다. 그러나 ReplacingMergeTree는 정렬 키가 같은 중복 행을 제거하고, 파츠의 생성 타임스탬프를 기준으로 가장 최근 행만 유지합니다.
합산 머지 (Summing merges)
SummingMergeTree 테이블의 파츠 머지 중에는 숫자 데이터가 자동으로 합산됩니다. 위 다이어그램의 DDL 문은 정렬 키로 town을 가진 SummingMergeTree 테이블을 정의합니다. 즉 디스크상 데이터가 이 컬럼으로 정렬되고 상응하는 희소 프라이머리 인덱스가 생성됩니다. ② 병합 단계에서 ClickHouse는 정렬 키가 같은 모든 행을 단일 행으로 교체하고 숫자 컬럼의 값을 합산합니다.
집계 머지 (Aggregating merges)
위의 SummingMergeTree 테이블 예제는 AggregatingMergeTree 테이블의 특수한 변형으로, 파츠 머지 중 90개 이상의 집계 함수 중 아무거나 적용하여 자동 증분 데이터 변환을 가능하게 합니다. 위 다이어그램의 DDL 문은 정렬 키가 town인 AggregatingMergeTree 테이블을 만듭니다. 이 컬럼으로 디스크상 데이터가 정렬되고 상응하는 희소 프라이머리 인덱스가 생성됩니다. ② 병합 중에 ClickHouse는 정렬 키가 같은 모든 행을, 부분 집계 상태(partial aggregation states)(예: avg()를 위한 sum과 count)를 저장하는 단일 행으로 교체합니다. 이 상태들은 증분 백그라운드 머지를 통해 정확한 결과를 보장합니다.