Skip to main content
이 엔진의 특징은 다음과 같습니다.
  • 지속적으로 변경되는 객체 상태를 빠르게 기록할 수 있습니다.
  • 이전 객체 상태를 백그라운드에서 삭제합니다. 이를 통해 스토리지 사용량을 크게 줄일 수 있습니다.
자세한 내용은 축약 섹션을 참조하십시오. 이 엔진은 MergeTree를 상속하며, 데이터 파트를 머지하는 알고리즘에 행 축약 로직을 추가합니다. VersionedCollapsingMergeTreeCollapsingMergeTree와 동일한 목적에 사용되지만, 여러 스레드에서 데이터를 임의의 순서로 삽입할 수 있게 해주는 다른 축약 알고리즘을 사용합니다. 특히 Version 컬럼은 행이 잘못된 순서로 삽입되더라도 올바르게 축약되도록 도와줍니다. 반면 CollapsingMergeTree는 엄격히 연속된 순서의 삽입만 허용합니다.

테이블 생성

쿼리 매개변수에 대한 설명은 쿼리 설명을 참조하십시오.

엔진 매개변수

쿼리 절

VersionedCollapsingMergeTree 테이블을 생성할 때는 MergeTree 테이블을 생성할 때와 동일한 을 사용해야 합니다.

축약

데이터

어떤 객체에 대해 계속 변경되는 데이터를 저장해야 하는 상황을 가정해 보겠습니다. 객체마다 하나의 행을 두고 변경이 있을 때마다 해당 행을 업데이트하는 방식이 합리적일 수 있습니다. 그러나 업데이트 작업은 저장소의 데이터를 다시 써야 하므로 DBMS에서는 비용이 많이 들고 속도도 느립니다. 데이터를 빠르게 써야 한다면 업데이트는 적합하지 않지만, 다음과 같이 객체의 변경 사항을 순차적으로 기록할 수 있습니다. 행을 기록할 때 Sign 컬럼을 사용합니다. Sign = 1이면 해당 행이 객체의 상태를 나타낸다는 의미입니다(이를 “state” 행이라고 하겠습니다). Sign = -1이면 같은 속성을 가진 객체 상태의 취소를 나타냅니다(이를 “cancel” 행이라고 하겠습니다). 또한 Version 컬럼도 사용해야 하며, 이 컬럼은 객체의 각 상태를 각각의 번호로 식별해야 합니다. 예를 들어, 어떤 사이트에서 사용자가 몇 개의 페이지를 방문했고 얼마나 오래 머물렀는지 계산하려고 합니다. 어느 시점에 사용자 활동 상태를 나타내는 다음 행을 기록합니다:
이후 어느 시점에 사용자 활동 변경을 기록하고, 이를 다음 두 개의 행으로 저장합니다.
첫 번째 행은 객체(사용자)의 이전 상태를 취소합니다. Sign을 제외하고, 취소되는 상태의 모든 필드를 그대로 복사해야 합니다. 두 번째 행에는 현재 상태가 들어 있습니다. 사용자 활동의 마지막 상태만 필요하므로, 다음 행들은
삭제할 수 있으며, 객체의 유효하지 않은(오래된) 상태를 축약합니다. VersionedCollapsingMergeTree는 데이터 파트를 머지하는 과정에서 이를 수행합니다. 각 변경마다 왜 2개의 행이 필요한지 알아보려면 알고리즘을 참조하십시오. 사용 시 참고 사항
  1. 데이터를 쓰는 프로그램은 객체를 취소할 수 있도록 객체의 상태를 기억하고 있어야 합니다. “Cancel” 문자열에는 프라이머리 키 필드의 복사본, “state” 문자열의 버전, 그리고 반대 부호의 Sign이 포함되어야 합니다. 이렇게 하면 초기 스토리지 크기는 증가하지만 데이터를 빠르게 쓸 수 있습니다.
  2. 컬럼에서 길이가 계속 늘어나는 배열은 쓰기 부하 때문에 엔진의 효율을 떨어뜨립니다. 데이터 구조가 단순할수록 효율이 높아집니다.
  3. SELECT 결과는 객체 변경 이력의 일관성에 크게 좌우됩니다. 삽입할 데이터를 준비할 때는 정확히 처리하십시오. 데이터가 일관되지 않으면 세션 깊이처럼 음수가 될 수 없는 메트릭에 음수 값이 나타나는 등 예측할 수 없는 결과가 발생할 수 있습니다.

알고리즘

ClickHouse가 데이터 파트를 머지할 때, 기본 키(primary key)와 버전이 같고 Sign 값이 서로 다른 각 행 쌍을 삭제합니다. 행의 순서는 중요하지 않습니다. ClickHouse가 데이터를 삽입하면 행을 기본 키 순으로 정렬합니다. Version 컬럼이 기본 키에 포함되어 있지 않으면, ClickHouse는 이를 마지막 필드로 기본 키에 암묵적으로 추가한 뒤 정렬에 사용합니다.

데이터 조회

ClickHouse는 동일한 기본 키(primary key)를 가진 모든 행이 동일한 결과 데이터 파트(data part)에 있거나, 심지어 동일한 물리 서버에 있을 것이라고 보장하지 않습니다. 이는 데이터를 쓸 때뿐 아니라 이후 데이터 파트를 머지할 때도 마찬가지입니다. 또한 ClickHouse는 여러 스레드로 SELECT 쿼리를 처리하므로 결과에 포함된 행의 순서를 예측할 수 없습니다. 즉, VersionedCollapsingMergeTree 테이블에서 완전히 “축약된” 데이터를 얻으려면 집계가 필요합니다. 축약을 완료하려면 GROUP BY 절과 부호를 고려하는 집계 함수를 사용해 쿼리를 작성하십시오. 예를 들어 수량을 계산할 때는 count() 대신 sum(Sign)를 사용합니다. 어떤 값의 합계를 계산할 때는 sum(x) 대신 sum(Sign * x)를 사용하고, HAVING sum(Sign) > 0을 추가합니다. 집계 함수 count, sum, avg는 이런 방식으로 계산할 수 있습니다. 집계 함수 uniq는 객체에 축약되지 않은 상태가 하나 이상 있으면 계산할 수 있습니다. 집계 함수 minmaxVersionedCollapsingMergeTree가 축약된 상태의 값 이력을 저장하지 않기 때문에 계산할 수 없습니다. 집계 없이 “축약”된 데이터를 추출해야 하는 경우(예를 들어 최신 값이 특정 조건과 일치하는 행이 있는지 확인하는 경우)에는 FROM 절에 FINAL 수정자를 사용할 수 있습니다. 이 방법은 비효율적이므로 큰 테이블에는 사용하지 않아야 합니다.

사용 예시

예시 데이터:
테이블 생성:
데이터 삽입:
두 개의 서로 다른 데이터 파트를 생성하기 위해 INSERT 쿼리 2개를 사용합니다. 데이터를 단일 쿼리로 삽입하면 ClickHouse는 데이터 파트 하나만 생성하므로 머지를 수행하지 않습니다. 데이터 가져오기:
여기서 무엇을 볼 수 있으며, 축약된 파트는 어디에 있을까요? 두 개의 INSERT 쿼리를 사용해 두 개의 데이터 파트를 생성했습니다. SELECT 쿼리는 두 개의 스레드에서 수행되었고, 그 결과 행의 순서는 무작위입니다. 데이터 파트가 아직 머지되지 않았기 때문에 축약은 발생하지 않았습니다. ClickHouse는 예측할 수 없는 시점에 데이터 파트를 머지하므로, 그 시점을 미리 알 수는 없습니다. 이 때문에 집계가 필요합니다:
집계가 필요 없고 축약을 강제로 적용하려면 FROM 절에 FINAL 수정자를 사용할 수 있습니다.
이 방법은 데이터를 조회하는 데 매우 비효율적입니다. 대규모 테이블에는 사용하지 마십시오.
마지막 수정일 2026년 7월 23일