- 지속적으로 변경되는 객체 상태를 빠르게 기록할 수 있습니다.
- 이전 객체 상태를 백그라운드에서 삭제합니다. 이를 통해 스토리지 사용량을 크게 줄일 수 있습니다.
VersionedCollapsingMergeTree는 CollapsingMergeTree와 동일한 목적에 사용되지만, 여러 스레드에서 데이터를 임의의 순서로 삽입할 수 있게 해주는 다른 축약 알고리즘을 사용합니다. 특히 Version 컬럼은 행이 잘못된 순서로 삽입되더라도 올바르게 축약되도록 도와줍니다. 반면 CollapsingMergeTree는 엄격히 연속된 순서의 삽입만 허용합니다.
테이블 생성
엔진 매개변수
쿼리 절
VersionedCollapsingMergeTree 테이블을 생성할 때는 MergeTree 테이블을 생성할 때와 동일한 절을 사용해야 합니다.
축약
데이터
Sign 컬럼을 사용합니다. Sign = 1이면 해당 행이 객체의 상태를 나타낸다는 의미입니다(이를 “state” 행이라고 하겠습니다). Sign = -1이면 같은 속성을 가진 객체 상태의 취소를 나타냅니다(이를 “cancel” 행이라고 하겠습니다). 또한 Version 컬럼도 사용해야 하며, 이 컬럼은 객체의 각 상태를 각각의 번호로 식별해야 합니다.
예를 들어, 어떤 사이트에서 사용자가 몇 개의 페이지를 방문했고 얼마나 오래 머물렀는지 계산하려고 합니다. 어느 시점에 사용자 활동 상태를 나타내는 다음 행을 기록합니다:
Sign을 제외하고, 취소되는 상태의 모든 필드를 그대로 복사해야 합니다.
두 번째 행에는 현재 상태가 들어 있습니다.
사용자 활동의 마지막 상태만 필요하므로, 다음 행들은
VersionedCollapsingMergeTree는 데이터 파트를 머지하는 과정에서 이를 수행합니다.
각 변경마다 왜 2개의 행이 필요한지 알아보려면 알고리즘을 참조하십시오.
사용 시 참고 사항
- 데이터를 쓰는 프로그램은 객체를 취소할 수 있도록 객체의 상태를 기억하고 있어야 합니다. “Cancel” 문자열에는 프라이머리 키 필드의 복사본, “state” 문자열의 버전, 그리고 반대 부호의
Sign이 포함되어야 합니다. 이렇게 하면 초기 스토리지 크기는 증가하지만 데이터를 빠르게 쓸 수 있습니다. - 컬럼에서 길이가 계속 늘어나는 배열은 쓰기 부하 때문에 엔진의 효율을 떨어뜨립니다. 데이터 구조가 단순할수록 효율이 높아집니다.
SELECT결과는 객체 변경 이력의 일관성에 크게 좌우됩니다. 삽입할 데이터를 준비할 때는 정확히 처리하십시오. 데이터가 일관되지 않으면 세션 깊이처럼 음수가 될 수 없는 메트릭에 음수 값이 나타나는 등 예측할 수 없는 결과가 발생할 수 있습니다.
알고리즘
Sign 값이 서로 다른 각 행 쌍을 삭제합니다. 행의 순서는 중요하지 않습니다.
ClickHouse가 데이터를 삽입하면 행을 기본 키 순으로 정렬합니다. Version 컬럼이 기본 키에 포함되어 있지 않으면, ClickHouse는 이를 마지막 필드로 기본 키에 암묵적으로 추가한 뒤 정렬에 사용합니다.
데이터 조회
SELECT 쿼리를 처리하므로 결과에 포함된 행의 순서를 예측할 수 없습니다. 즉, VersionedCollapsingMergeTree 테이블에서 완전히 “축약된” 데이터를 얻으려면 집계가 필요합니다.
축약을 완료하려면 GROUP BY 절과 부호를 고려하는 집계 함수를 사용해 쿼리를 작성하십시오. 예를 들어 수량을 계산할 때는 count() 대신 sum(Sign)를 사용합니다. 어떤 값의 합계를 계산할 때는 sum(x) 대신 sum(Sign * x)를 사용하고, HAVING sum(Sign) > 0을 추가합니다.
집계 함수 count, sum, avg는 이런 방식으로 계산할 수 있습니다. 집계 함수 uniq는 객체에 축약되지 않은 상태가 하나 이상 있으면 계산할 수 있습니다. 집계 함수 min과 max는 VersionedCollapsingMergeTree가 축약된 상태의 값 이력을 저장하지 않기 때문에 계산할 수 없습니다.
집계 없이 “축약”된 데이터를 추출해야 하는 경우(예를 들어 최신 값이 특정 조건과 일치하는 행이 있는지 확인하는 경우)에는 FROM 절에 FINAL 수정자를 사용할 수 있습니다. 이 방법은 비효율적이므로 큰 테이블에는 사용하지 않아야 합니다.
사용 예시
INSERT 쿼리 2개를 사용합니다. 데이터를 단일 쿼리로 삽입하면 ClickHouse는 데이터 파트 하나만 생성하므로 머지를 수행하지 않습니다.
데이터 가져오기:
INSERT 쿼리를 사용해 두 개의 데이터 파트를 생성했습니다. SELECT 쿼리는 두 개의 스레드에서 수행되었고, 그 결과 행의 순서는 무작위입니다.
데이터 파트가 아직 머지되지 않았기 때문에 축약은 발생하지 않았습니다. ClickHouse는 예측할 수 없는 시점에 데이터 파트를 머지하므로, 그 시점을 미리 알 수는 없습니다.
이 때문에 집계가 필요합니다:
FROM 절에 FINAL 수정자를 사용할 수 있습니다.