MergeTree 테이블 엔진
MergeTree 테이블 엔진
MergeTree 엔진과 MergeTree 패밀리의 다른 엔진들(예: ReplacingMergeTree, AggregatingMergeTree)은 ClickHouse에서 가장 흔히 사용되고 가장 견고한 테이블 엔진이에요. MergeTree-패밀리 테이블 엔진은 높은 데이터 수집 속도와 거대한 데이터 볼륨을 위해 설계됐어요. 삽입 연산은 테이블 파트를 만들고, 이 파트는 백그라운드 프로세스에 의해 다른 테이블 파트와 병합돼요.
MergeTree-패밀리 테이블 엔진의 주요 기능:
- 테이블의 기본 키는 각 테이블 파트 내의 정렬 순서(클러스터형 인덱스)를 결정해요. 기본 키는 개별 행을 참조하지 않고 granule이라 불리는 8192행 블록을 참조해요. 이는 거대한 데이터셋의 기본 키가 주 메모리에 유지될 만큼 작게 유지되면서도 디스크 데이터에 대한 빠른 접근을 제공하게 해요
- 테이블은 임의의 파티션 표현식으로 파티셔닝될 수 있어요. 파티션 가지치기(pruning)는 쿼리가 허용할 때 파티션이 읽기에서 제외되도록 보장해요
- 고가용성, 장애 조치, 제로 다운타임 업그레이드를 위해 데이터가 여러 클러스터 노드에 복제될 수 있어요. Data replication 참고
MergeTree테이블 엔진은 쿼리 최적화를 돕기 위해 다양한 통계 종류와 샘플링 방법을 지원해요
비슷한 이름에도 불구하고 Merge 엔진은 *MergeTree 엔진과 달라요.
출처: 문서
본문
테이블 생성하기
CREATE TABLE [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster]
(
name1 [type1] [[NOT] NULL] [DEFAULT|MATERIALIZED|ALIAS|EPHEMERAL expr1] [COMMENT ...] [CODEC(codec1)] [STATISTICS(stat1)] [TTL expr1] [PRIMARY KEY] [SETTINGS (name = value, ...)],
name2 [type2] [[NOT] NULL] [DEFAULT|MATERIALIZED|ALIAS|EPHEMERAL expr2] [COMMENT ...] [CODEC(codec2)] [STATISTICS(stat2)] [TTL expr2] [PRIMARY KEY] [SETTINGS (name = value, ...)],
...
INDEX index_name1 expr1 TYPE type1(...) [GRANULARITY value1],
INDEX index_name2 expr2 TYPE type2(...) [GRANULARITY value2],
...
PROJECTION projection_name_1 (SELECT <COLUMN LIST EXPR> [GROUP BY] [ORDER BY]),
PROJECTION projection_name_2 (SELECT <COLUMN LIST EXPR> [GROUP BY] [ORDER BY])
) ENGINE = MergeTree()
ORDER BY expr
[PARTITION BY expr]
[PRIMARY KEY expr]
[SAMPLE BY expr]
[TTL expr
[DELETE|TO DISK 'xxx'|TO VOLUME 'xxx' [, ...] ]
[WHERE conditions]
[GROUP BY key_expr [SET v1 = aggr_func(v1) [, v2 = aggr_func(v2) ...]] ] ]
[SETTINGS name = value, ...]
매개변수에 대한 자세한 설명은 CREATE TABLE 문을 참고해요.
쿼리 절
ENGINE
ENGINE — 엔진 이름과 매개변수. ENGINE = MergeTree(). MergeTree 엔진에는 매개변수가 없어요.
ORDER BY
ORDER BY — 정렬 키. 컬럼 이름 또는 임의 표현식의 튜플. 예: ORDER BY (CounterID + 1, EventDate). 기본 키가 정의되지 않으면 (즉 PRIMARY KEY가 지정되지 않으면) ClickHouse는 정렬 키를 기본 키로 사용해요. 정렬이 필요 없으면 ORDER BY tuple() 문법을 사용할 수 있어요. 또는 create_table_empty_primary_key_by_default 설정이 활성화되면 ORDER BY ()가 CREATE TABLE 문에 암시적으로 추가돼요. Selecting a Primary Key 참고.
PARTITION BY
PARTITION BY — 파티셔닝 키. 선택. 대부분의 경우 파티션 키가 필요하지 않고, 파티셔닝이 필요하더라도 일반적으로 월 단위보다 더 세밀한 파티션 키는 필요하지 않아요. 파티셔닝은 (ORDER BY 표현식과 대조적으로) 쿼리를 빠르게 하지 않아요. 너무 세밀한 파티셔닝을 사용하면 안 돼요. 클라이언트 식별자나 이름으로 데이터를 파티셔닝하지 마세요 (대신 클라이언트 식별자나 이름을 ORDER BY 표현식의 첫 컬럼으로 만들어요).
월 단위 파티셔닝에는 toYYYYMM(date_column) 표현식을 사용해요. 여기서 date_column은 Date 타입의 날짜 컬럼이에요. 여기서 파티션 이름은 "YYYYMM" 형식이에요.
PRIMARY KEY
PRIMARY KEY — 정렬 키와 다른 경우의 기본 키. 선택. 정렬 키 지정(ORDER BY 절 사용)은 암시적으로 기본 키를 지정해요. 보통은 정렬 키 외에 기본 키를 추가로 지정할 필요가 없어요.
SAMPLE BY
SAMPLE BY — 샘플링 표현식. 선택. 지정되면 기본 키에 포함되어 있어야 해요. 샘플링 표현식은 부호 없는 정수를 결과로 내야 해요. 예: SAMPLE BY intHash32(UserID) ORDER BY (CounterID, EventDate, intHash32(UserID)).
TTL
TTL — 행의 저장 기간과 디스크와 볼륨 사이 파트의 자동 이동 논리를 지정하는 규칙 목록. 선택. 표현식은 Date 또는 DateTime을 결과로 내야 해요. 예: TTL date + INTERVAL 1 DAY. 규칙 유형 DELETE|TO DISK 'xxx'|TO VOLUME 'xxx'|GROUP BY은 표현식이 만족되면(현재 시간에 도달) 파트에 대해 수행할 작업을 지정해요: 만료된 행 제거, 파트 이동(파트의 모든 행에 대해 표현식이 만족되면) 지정된 디스크(TO DISK 'xxx') 또는 볼륨(TO VOLUME 'xxx')으로, 또는 만료된 행의 값 집계. 규칙의 기본 유형은 제거(DELETE)예요. 여러 규칙 목록을 지정할 수 있지만 DELETE 규칙은 하나 이상 있으면 안 돼요. 자세한 내용은 TTL for columns and tables 참고.
SETTINGS
MergeTree 설정 참고.
설정 섹션 예시
ENGINE MergeTree() PARTITION BY toYYYYMM(EventDate) ORDER BY (CounterID, EventDate, intHash32(UserID)) SAMPLE BY intHash32(UserID) SETTINGS index_granularity=8192
예시에서 월 단위 파티셔닝을 설정했어요. 또한 사용자 ID의 해시로 샘플링 표현식을 설정했어요. 이는 각 CounterID와 EventDate에 대해 테이블의 데이터를 의사 무작위화할 수 있게 해줘요. 데이터 선택 시 SAMPLE 절을 정의하면 ClickHouse가 사용자 부분 집합에 대해 고르게 의사 무작위 데이터 샘플을 반환해요.
index_granularity 설정은 8192가 기본값이므로 생략할 수 있어요.
데이터 저장
테이블은 기본 키로 정렬된 데이터 파트로 구성돼요. 데이터가 테이블에 삽입되면 별도의 데이터 파트가 생성되고 각각 기본 키로 사전순 정렬돼요. 예를 들어 기본 키가 (CounterID, Date)라면 파트의 데이터는 CounterID로 정렬되고, 각 CounterID 안에서 Date 순으로 정렬돼요.
서로 다른 파티션에 속한 데이터는 서로 다른 파트로 분리돼요. 백그라운드에서 ClickHouse는 더 효율적인 저장을 위해 데이터 파트를 병합해요. 서로 다른 파티션에 속한 파트는 병합되지 않아요. 병합 메커니즘은 같은 기본 키를 가진 모든 행이 같은 데이터 파트에 있을 것도 보장하지 않아요.
데이터 파트는 Wide 또는 Compact 형식으로 저장될 수 있어요. Wide 형식에서는 각 컬럼이 파일시스템의 별도 파일에 저장되고, Compact 형식에서는 모든 컬럼이 하나의 파일에 저장돼요. Compact 형식은 작고 빈번한 삽입의 성능을 높이는 데 사용할 수 있어요.
데이터 저장 형식은 테이블 엔진의 min_bytes_for_wide_part와 min_rows_for_wide_part 설정에 의해 제어돼요. 데이터 파트의 바이트 수나 행 수가 해당 설정 값보다 작으면 파트는 Compact 형식으로 저장돼요. 그렇지 않으면 Wide 형식으로 저장돼요. 이 설정 중 어느 것도 설정되지 않으면 데이터 파트는 Wide 형식으로 저장돼요.
각 데이터 파트는 논리적으로 granule로 나뉘어요. granule은 ClickHouse가 데이터를 선택할 때 읽는 가장 작은 분할 불가능한 데이터 집합이에요. ClickHouse는 행이나 값을 분할하지 않으므로 각 granule은 항상 정수 개의 행을 포함해요. granule의 첫 행은 그 행의 기본 키 값으로 표시돼요. 각 데이터 파트에 대해 ClickHouse는 marks를 저장하는 인덱스 파일을 만들어요. 각 컬럼에 대해 기본 키에 있든 없든 ClickHouse는 같은 marks도 저장해요. 이 marks는 컬럼 파일에서 데이터를 직접 찾을 수 있게 해줘요.
granule 크기는 테이블 엔진의 index_granularity와 index_granularity_bytes 설정에 의해 제한돼요. granule의 행 수는 행 크기에 따라 [1, index_granularity] 범위에 있어요. 단일 행 크기가 설정 값보다 크면 granule 크기가 index_granularity_bytes를 초과할 수 있어요. 이 경우 granule 크기는 행 크기와 같아져요.
쿼리의 기본 키와 인덱스
(CounterID, Date) 기본 키를 예로 들어요. 이 경우 정렬과 인덱스는 다음과 같이 설명할 수 있어요.
Whole data: [---------------------------------------------]
CounterID: [aaaaaaaaaaaaaaaaaabbbbcdeeeeeeeeeeeeefgggggggghhhhhhhhhiiiiiiiiikllllllll]
Date: [1111111222222233331233211111222222333211111112122222223111112223311122333]
Marks: | | | | | | | | | | |
a,1 a,2 a,3 b,3 e,2 e,3 g,1 h,2 i,1 i,3 l,3
Marks numbers: 0 1 2 3 4 5 6 7 8 9 10
데이터 쿼리가 지정한다면:
CounterID in ('a', 'h')— 서버는 marks 범위[0, 3)과[6, 8)의 데이터를 읽어요CounterID IN ('a', 'h') AND Date = 3— 서버는 marks 범위[1, 3)과[7, 8)의 데이터를 읽어요Date = 3— 서버는 marks 범위[1, 10]의 데이터를 읽어요
위 예시들은 인덱스를 사용하는 것이 항상 전체 스캔보다 더 효과적이라는 것을 보여줘요. 희소(sparse) 인덱스는 추가 데이터가 읽히게 해요. 기본 키의 단일 범위를 읽을 때 각 데이터 블록에서 최대 index_granularity * 2개의 추가 행을 읽을 수 있어요. 희소 인덱스는 매우 많은 수의 테이블 행과 함께 작업할 수 있게 해주는데, 대부분의 경우 그런 인덱스가 컴퓨터 RAM에 들어맞기 때문이에요.
ClickHouse는 고유 기본 키를 요구하지 않아요. 같은 기본 키를 가진 여러 행을 삽입할 수 있어요. PRIMARY KEY와 ORDER BY 절에서 Nullable-타입 표현식을 사용할 수 있지만 강력히 권장되지는 않아요. 이 기능을 허용하려면 allow_nullable_key 설정을 켜요. ORDER BY 절의 NULL 값에는 NULLS_LAST 원칙이 적용돼요.
기본 키 선택하기
기본 키의 컬럼 수는 명시적으로 제한되지 않아요. 데이터 구조에 따라 기본 키에 더 많거나 적은 컬럼을 포함할 수 있어요. 이는 다음을 할 수 있어요.
- 인덱스 성능을 향상시켜요. 기본 키가
(a, b)라면 다음 조건이 충족될 때 컬럼c를 추가하면 성능이 향상돼요. 컬럼c에 대한 조건이 있는 쿼리가 있는 경우.(a, b)에 대해 같은 값을 가진 긴 데이터 범위(index_granularity보다 몇 배 긴)가 흔한 경우. 다시 말해, 다른 컬럼을 추가하면 꽤 긴 데이터 범위를 건너뛸 수 있는 경우 - 데이터 압축을 향상시켜요. ClickHouse는 기본 키로 데이터를 정렬하므로 일관성이 높을수록 압축이 더 좋아요
- CollapsingMergeTree와 SummingMergeTree 엔진에서 데이터 파트 병합 시 추가 논리를 제공해요. 이 경우 기본 키와 다른 정렬 키를 지정하는 것이 합리적이에요
긴 기본 키는 삽입 성능과 메모리 소비에 부정적인 영향을 주지만, 기본 키의 추가 컬럼은 SELECT 쿼리 중에 ClickHouse 성능에 영향을 주지 않아요. ORDER BY tuple() 문법으로 기본 키 없이 테이블을 만들 수 있어요. 이 경우 ClickHouse는 삽입 순서로 데이터를 저장해요. INSERT ... SELECT 쿼리로 데이터를 삽입할 때 데이터 순서를 유지하려면 max_insert_threads = 1을 설정해요. 초기 순서로 데이터를 선택하려면 single-threaded SELECT 쿼리를 사용해요.
정렬 키와 다른 기본 키 선택하기
정렬 키(데이터 파트의 행 정렬 표현식)와 다른 기본 키(각 mark에 대해 인덱스 파일에 기록되는 값을 가진 표현식)를 지정할 수 있어요. 이 경우 기본 키 표현식 튜플은 정렬 키 표현식 튜플의 접두사여야 해요.
이 기능은 SummingMergeTree와 AggregatingMergeTree 테이블 엔진을 사용할 때 유용해요. 이 엔진을 사용하는 일반적인 경우 테이블에는 dimensions와 measures 두 가지 유형의 컬럼이 있어요. 일반적인 쿼리는 임의의 GROUP BY와 dimensions로 필터링하여 measure 컬럼의 값을 집계해요. SummingMergeTree와 AggregatingMergeTree는 정렬 키의 같은 값을 가진 행을 집계하므로 모든 dimensions를 그것에 추가하는 것이 자연스러워요. 결과적으로 키 표현식은 긴 컬럼 목록으로 구성되고 이 목록은 새로 추가되는 dimensions로 빈번히 업데이트되어야 해요.
이 경우 효율적인 범위 스캔을 제공할 몇 개의 컬럼만 기본 키에 남기고 나머지 dimensions 컬럼은 정렬 키 튜플에 추가하는 것이 합리적이에요. ALTER 정렬 키는 새 컬럼이 테이블과 정렬 키에 동시에 추가될 때 기존 데이터 파트를 변경할 필요가 없으므로 가벼운 연산이에요. 기존 정렬 키는 새 정렬 키의 접두사이고 새로 추가된 컬럼에 데이터가 없으므로 테이블 수정 시점에 데이터는 기존과 새 정렬 키 둘 다로 정렬돼요.
쿼리의 인덱스와 파티션 사용
SELECT 쿼리의 경우 ClickHouse는 인덱스를 사용할 수 있는지 분석해요. WHERE/PREWHERE 절에 (결합 요소 중 하나로, 또는 완전히) 등식 또는 부등식 비교 연산을 나타내는 표현식이 있거나, 기본 키 또는 파티셔닝 키에 있는 컬럼이나 표현식에 IN 또는 고정 접두사가 있는 LIKE가 있거나, 그 컬럼의 특정 부분 반복 함수나 그 표현식의 논리적 관계가 있으면 인덱스를 사용할 수 있어요.
따라서 기본 키의 하나 또는 많은 범위에 대해 빠르게 쿼리를 실행할 수 있어요. 이 예시에서 특정 추적 태그, 특정 태그와 날짜 범위, 특정 태그와 날짜, 날짜 범위가 있는 여러 태그 등에 대해 쿼리를 실행하면 빠를 거예요.
다음과 같이 구성된 엔진을 살펴봐요.
ENGINE MergeTree()
PARTITION BY toYYYYMM(EventDate)
ORDER BY (CounterID, EventDate)
SETTINGS index_granularity=8192
이 경우 쿼리에서:
SELECT count() FROM table
WHERE EventDate = toDate(now())
AND CounterID = 34
SELECT count() FROM table
WHERE EventDate = toDate(now())
AND (CounterID = 34 OR CounterID = 42)
SELECT count() FROM table
WHERE ((EventDate >= toDate('2014-01-01')
AND EventDate <= toDate('2014-01-31')) OR EventDate = toDate('2014-05-01'))
AND CounterID IN (101500, 731962, 160656)
AND (CounterID = 101500 OR EventDate != toDate('2014-05-01'))
ClickHouse는 잘못된 데이터를 제거하기 위해 기본 키 인덱스를, 부적절한 날짜 범위에 있는 파티션을 제거하기 위해 월 파티셔닝 키를 사용해요. 위 쿼리들은 복잡한 표현식에도 인덱스가 사용됨을 보여줘요. 테이블에서 읽는 것은 인덱스를 사용하는 것이 전체 스캔보다 느릴 수 없도록 구성돼요.
아래 예시에서는 인덱스를 사용할 수 없어요.
SELECT count() FROM table WHERE CounterID = 34 OR URL LIKE '%upyachka%'
쿼리를 실행할 때 ClickHouse가 인덱스를 사용할 수 있는지 확인하려면 force_index_by_date와 force_primary_key 설정을 사용해요.
월 단위 파티셔닝 키는 적절한 범위의 날짜를 포함하는 데이터 블록만 읽을 수 있게 해줘요. 이 경우 데이터 블록은 여러 날짜(최대 한 달)의 데이터를 포함할 수 있어요. 블록 내에서 데이터는 기본 키로 정렬되며, 날짜가 첫 컬럼이 아닐 수 있어요. 때문에 기본 키 접두사를 지정하지 않는 날짜 조건만 있는 쿼리를 사용하면 단일 날짜보다 더 많은 데이터가 읽히게 돼요.
기본 키의 결정적 표현식에 대한 인덱스 사용
기본 키는 컬럼 이름뿐 아니라 표현식을 포함할 수 있어요. 이 표현식은 단순 함수 체인으로 제한되지 않아요: 결정적(deterministic)이기만 하면 임의의 표현식 트리(예: 중첩 함수와 복합 표현식)일 수 있어요.
표현식은 같은 입력 값에 대해 항상 같은 결과를 반환하면 결정적이에요 (예: length(), toDate(), lower(), left(), cityHash64(), toUUID(); now()나 rand()와 달리). 기본 키에 결정적 표현식이 포함되면 ClickHouse는 쿼리의 상수 값에 그것을 적용하고 그 결과를 사용해 기본 키 인덱스에 대한 조건을 만들 수 있어요. 이는 =, IN, has 같은 조건에 대한 데이터 스킵을 가능하게 해요.
일반적인 사용 사례는 기본 키를 컴팩트하게 유지하면서(예: 긴 String 대신 해시 저장) 원래 컬럼에 대한 조건이 여전히 인덱스를 사용하게 하는 것이에요.
결정적(하지만 비-단사) 기본 키의 예시:
ENGINE = MergeTree()
ORDER BY length(user_id)
인덱스를 사용할 수 있는 예시 조건:
SELECT * FROM table WHERE user_id = 'alice';
SELECT * FROM table WHERE user_id IN ('alice', 'bob');
SELECT * FROM table WHERE has(['alice', 'bob'], user_id);
이 경우 ClickHouse는 length('alice')(및 다른 상수)를 한 번 계산하고 길이 값을 사용해 기본 키 인덱스의 범위를 좁혀요. 문자열 길이는 단사(injective)가 아니므로 서로 다른 user_id 문자열이 같은 길이를 공유할 수 있어 인덱스가 추가 그레인(거짓 양성)을 읽을 수 있어요. 읽기 후 원래 조건(user_id = ..., IN 등)이 여전히 적용되므로 결과는 정확하게 유지돼요.
결정적 표현식이 또한 단사(사용된 인자 타입에 대해 다른 입력이 같은 출력을 만들 수 없는)라면 ClickHouse는 부정 형태 !=, NOT IN, NOT has(...)에 대해서도 인덱스를 효과적으로 사용할 수 있어요. 예를 들어 String에 대해 reverse(p)와 hex(p)는 단사적이에요.
단사 기본 키의 예시:
ENGINE = MergeTree()
ORDER BY hex(p)
더 복잡한 단사 표현식도 지원돼요. 예:
ENGINE = MergeTree()
ORDER BY reverse(tuple(reverse(p), hex(p)))
인덱스를 사용할 수 있는 예시 조건:
SELECT * FROM table WHERE p != 'abc';
SELECT * FROM table WHERE p NOT IN ('abc', '12345');
SELECT * FROM table WHERE NOT has(['abc', '12345'], p);
부분-단조 기본 키에 대한 인덱스 사용
예를 들어 한 달의 날짜들을 고려해요. 그것들은 한 달에 대해서는 단조 수열(monotonic sequence)을 형성하지만 더 긴 기간에 대해서는 단조적이지 않아요. 이것은 부분-단조 수열이에요. 사용자가 부분-단조 기본 키로 테이블을 만들면 ClickHouse는 평소처럼 희소 인덱스를 만들어요. 사용자가 이 종류의 테이블에서 데이터를 선택하면 ClickHouse는 쿼리 조건을 분석해요. 사용자가 인덱스의 두 mark 사이의 데이터를 얻으려 하고 두 mark가 모두 한 달 안에 속하면 ClickHouse는 이 특정 경우에 인덱스를 사용할 수 있어요. 쿼리 매개변수와 인덱스 mark 사이의 거리를 계산할 수 있기 때문이에요.
쿼리 매개변수 범위의 기본 키 값이 단조 수열을 나타내지 않으면 ClickHouse는 인덱스를 사용할 수 없어요. 이 경우 ClickHouse는 전체 스캔 방법을 사용해요. ClickHouse는 이 논리를 한 달의 날짜 수열뿐 아니라 부분-단조 수열을 나타내는 어떤 기본 키에 대해서도 사용해요.
데이터 스킵 인덱스
인덱스 선언은 CREATE 쿼리의 컬럼 섹션에 있어요.
INDEX index_name expr TYPE type(...) [GRANULARITY granularity_value]
*MergeTree 패밀리의 테이블에는 데이터 스킵 인덱스를 지정할 수 있어요. 이 인덱스들은 granularity_value-개의 granule(그래뉼 크기는 테이블 엔진의 index_granularity 설정으로 지정)로 구성된 블록에 대해 지정된 표현식에 대한 일부 정보를 집계해요. 그런 다음 이 집계들은 SELECT 쿼리에서 where 쿼리가 충족될 수 없는 큰 데이터 블록을 건너뛰어 디스크에서 읽을 데이터 양을 줄이는 데 사용돼요.
GRANULARITY 절은 생략할 수 있고 granularity_value의 기본값은 1이에요.
예시
CREATE TABLE table_name
(
u64 UInt64,
i32 Int32,
s String,
...
INDEX idx1 u64 TYPE bloom_filter GRANULARITY 3,
INDEX idx2 u64 * i32 TYPE minmax GRANULARITY 3,
INDEX idx3 u64 * length(s) TYPE set(1000) GRANULARITY 4
) ENGINE = MergeTree()
...
예시의 인덱스들은 다음 쿼리에서 디스크에서 읽을 데이터 양을 줄이는 데 ClickHouse가 사용할 수 있어요.
SELECT count() FROM table WHERE u64 == 10;
SELECT count() FROM table WHERE u64 * i32 >= 1234
SELECT count() FROM table WHERE u64 * length(s) == 1234
데이터 스킵 인덱스는 복합 컬럼에도 만들 수 있어요.
-- on columns of type Map:
INDEX map_key_index mapKeys(map_column) TYPE bloom_filter
INDEX map_value_index mapValues(map_column) TYPE bloom_filter
-- on columns of type JSON:
INDEX json_paths_index JSONAllPaths(json_column) TYPE bloom_filter
-- on columns of type Tuple:
INDEX tuple_1_index tuple_column.1 TYPE bloom_filter
INDEX tuple_2_index tuple_column.2 TYPE bloom_filter
-- on columns of type Nested:
INDEX nested_1_index col.nested_col1 TYPE bloom_filter
INDEX nested_2_index col.nested_col2 TYPE bloom_filter
스킵 인덱스 유형
MergeTree 테이블 엔진은 다음 유형의 스킵 인덱스를 지원해요. 스킵 인덱스가 성능 최적화에 어떻게 사용될 수 있는지에 대한 자세한 내용은 "Understanding ClickHouse data skipping indexes"을 참고해요.
- MinMax 인덱스
- Set 인덱스
- bloom_filter 인덱스
- ngrambf_v1 인덱스 (사용 중단)
- tokenbf_v1 인덱스 (사용 중단)
- text 인덱스
- vector_similarity 인덱스
MinMax 스킵 인덱스
각 인덱스 granule에 대해 표현식의 최소값과 최대값이 저장돼요. (표현식이 tuple 타입이면 각 튜플 요소에 대한 최소값과 최대값을 저장해요.)
문법
minmax
Set
각 인덱스 granule에 대해 지정된 표현식의 고유 값이 기껏해야 max_rows개 저장돼요. max_rows = 0은 "모든 고유 값 저장"을 의미해요.
문법
set(max_rows)
Bloom filter
각 인덱스 granule에 대해 지정된 컬럼의 bloom filter를 저장해요.
문법
bloom_filter([false_positive_rate])
false_positive_rate 매개변수는 0과 1 사이의 값을 가질 수 있고 (기본: 0.025) 양성 결과를 생성할 확률(읽을 데이터 양을 증가시킴)을 지정해요. 다음 데이터 타입이 지원돼요.
(U)Int*Float*EnumDateDateTimeStringFixedStringArrayLowCardinalityNullableUUIDMap
Map 데이터 타입의 경우 클라이언트는 mapKeys 또는 mapValues 함수를 사용해 인덱스를 키용으로 만들지 값용으로 만들지 지정할 수 있어요. JSON 데이터 타입의 경우 JSONAllPaths 함수를 사용해 경로 집합에 bloom filter 인덱스를 만들 수 있어요. 이는 조회된 JSON 경로가 없는 granule을 건너뛸 수 있게 해줘요. 자세한 내용은 JSON용 데이터 스킵 인덱스 참고.
N-gram bloom filter (사용 중단)
text 인덱스가 ClickHouse 버전 26.2부터 GA(일반 공급) 되면서 ngrambf_v1 인덱스는 전문 검색에 더 이상 권장되지 않아요. 자세한 내용은 페이지 "Full-text search with text indexes"를 참고해요.
각 인덱스 granule에 대해 지정된 컬럼의 n-grams에 대한 bloom filter를 저장해요.
문법
ngrambf_v1(n, size_of_bloom_filter_in_bytes, number_of_hash_functions, random_seed)
| Parameter | Description |
|---|---|
n |
ngram 크기 |
size_of_bloom_filter_in_bytes |
Bloom filter 크기(바이트). 여기서 256이나 512 같은 큰 값을 사용할 수 있어요 (잘 압축될 수 있으므로) |
number_of_hash_functions |
bloom filter에서 사용되는 해시 함수 수 |
random_seed |
bloom filter 해시 함수를 위한 시드 |
이 인덱스는 다음 데이터 타입에서만 동작해요.
ngrambf_v1의 매개변수를 추정하려면 다음 User Defined Functions (UDFs)를 사용할 수 있어요.
ngrambf_v1용 UDFs
CREATE FUNCTION bfEstimateFunctions [ON CLUSTER cluster]
AS
(total_number_of_all_grams, size_of_bloom_filter_in_bits) -> round((size_of_bloom_filter_in_bits / total_number_of_all_grams) * log(2));
CREATE FUNCTION bfEstimateBmSize [ON CLUSTER cluster]
AS
(total_number_of_all_grams, probability_of_false_positives) -> ceil((total_number_of_all_grams * log(probability_of_false_positives)) / log(1 / pow(2, log(2))));
CREATE FUNCTION bfEstimateFalsePositive [ON CLUSTER cluster]
AS
(total_number_of_all_grams, number_of_hash_functions, size_of_bloom_filter_in_bytes) -> pow(1 - exp(-number_of_hash_functions/ (size_of_bloom_filter_in_bytes / total_number_of_all_grams)), number_of_hash_functions);
CREATE FUNCTION bfEstimateGramNumber [ON CLUSTER cluster]
AS
(number_of_hash_functions, probability_of_false_positives, size_of_bloom_filter_in_bytes) -> ceil(size_of_bloom_filter_in_bytes / (-number_of_hash_functions / log(1 - exp(log(probability_of_false_positives) / number_of_hash_functions))))
이 함수들을 사용하려면 최소 두 매개변수를 지정해야 해요.
total_number_of_all_gramsprobability_of_false_positives
예를 들어 granule에 4300개의 ngram이 있고 거짓 양성이 0.0001보다 작기를 기대한다고 해요. 그러면 다른 매개변수들은 다음 쿼리를 실행해 추정할 수 있어요.
--- estimate number of bits in the filter
SELECT bfEstimateBmSize(4300, 0.0001) / 8 AS size_of_bloom_filter_in_bytes;
┌─size_of_bloom_filter_in_bytes─┐
│ 10304 │
└───────────────────────────────┘
--- estimate number of hash functions
SELECT bfEstimateFunctions(4300, bfEstimateBmSize(4300, 0.0001)) as number_of_hash_functions
┌─number_of_hash_functions─┐
│ 13 │
└──────────────────────────┘
물론 다른 조건에 대한 매개변수를 추정하는 데도 이 함수들을 사용할 수 있어요. 위 함수들은 여기 bloom filter 계산기를 참조해요.
Token bloom filter
text 인덱스가 ClickHouse 버전 26.2부터 GA 되면서 tokenbf_v1 인덱스는 전문 검색에 더 이상 권장되지 않아요. 자세한 내용은 페이지 "Full-text search with text indexes"를 참고해요.
문법
tokenbf_v1(size_of_bloom_filter_in_bytes, number_of_hash_functions, random_seed)
Sparse grams bloom filter
sparse grams bloom filter는 ngrambf_v1과 비슷하지만 ngrams 대신 sparse grams tokens를 사용해요.
문법
sparse_grams(min_ngram_length, max_ngram_length, min_cutoff_length, size_of_bloom_filter_in_bytes, number_of_hash_functions, random_seed)
Text 인덱스
토큰화된 문자열 데이터에 대한 역 인덱스(inverted index)를 구축해 효율적이고 결정적인 전문 검색을 가능하게 해요. 자세한 내용은 여기 참고.
Vector similarity
근사 최근접 이웃 검색을 지원해요. 자세한 내용은 여기 참고.
함수 지원
WHERE 절의 조건은 컬럼으로 동작하는 함수 호출을 포함해요. 컬럼이 인덱스의 일부라면 ClickHouse는 함수를 수행할 때 이 인덱스를 사용하려 시도해요. ClickHouse는 인덱스 사용을 위해 서로 다른 함수 부분집합을 지원해요.
set 유형의 인덱스는 모든 함수가 활용할 수 있어요. 다른 인덱스 유형은 다음과 같이 지원돼요.
| Function (operator) / Index | primary key | minmax | ngrambf_v1 | tokenbf_v1 | bloom_filter | sparse_grams | text |
|---|---|---|---|---|---|---|---|
| equals (=, ==) | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
| notEquals(!=, <>) | ✔ | ✔ | ✔ | ✔ | ✗ | ✔ | ✗ |
| like | ✔ | ✔ | ✔ | ✔ | ✗ | ✔ | ✔ |
| notLike | ✔ | ✔ | ✔ | ✔ | ✗ | ✔ | ✗ |
| match | ✗ | ✗ | ✔ | ✔ | ✗ | ✔ | ✔ |
| startsWith | ✔ | ✔ | ✔ | ✔ | ✗ | ✔ | ✔ |
| endsWith | ✗ | ✗ | ✔ | ✔ | ✗ | ✔ | ✔ |
| multiSearchAny | ✗ | ✗ | ✔ | ✗ | ✗ | ✗ | ✔ |
| multiSearchAnyUTF8 | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✔ |
| multiMatchAny | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✔ |
| in | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
| notIn | ✔ | ✔ | ✔ | ✔ | ✗ | ✔ | ✗ |
| less ( < ) | ✔ | ✔ | ✗ | ✗ | ✗ | ✗ | ✗ |
| greater ( > ) | ✔ | ✔ | ✗ | ✗ | ✗ | ✗ | ✗ |
| lessOrEquals ( <= ) | ✔ | ✔ | ✗ | ✗ | ✗ | ✗ | ✗ |
| greaterOrEquals ( >= ) | ✔ | ✔ | ✗ | ✗ | ✗ | ✗ | ✗ |
| empty | ✔ | ✔ | ✗ | ✗ | ✗ | ✗ | ✗ |
| notEmpty | ✗ | ✔ | ✗ | ✗ | ✗ | ✔ | ✗ |
| has | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
| hasAny | ✗ | ✗ | ✔ | ✔ | ✔ | ✔ | ✗ |
| hasAll | ✗ | ✗ | ✔ | ✔ | ✔ | ✔ | ✗ |
| hasToken | ✗ | ✗ | ✗ | ✔ | ✗ | ✗ | ✔ |
| hasTokenOrNull | ✗ | ✗ | ✗ | ✔ | ✗ | ✗ | ✔ |
| hasTokenCaseInsensitive ( * ) | ✗ | ✗ | ✗ | ✔ | ✗ | ✗ | ✗ |
| hasTokenCaseInsensitiveOrNull ( * ) | ✗ | ✗ | ✗ | ✔ | ✗ | ✗ | ✗ |
| hasAnyTokens | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✔ |
| hasAllTokens | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✔ |
| pointInPolygon | ✔ | ✔ | ✗ | ✗ | ✗ | ✗ | ✗ |
| mapContains (mapContainsKey) | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✔ |
| mapContainsKeyLike | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✔ |
| mapContainsValue | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✔ |
| mapContainsValueLike | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✔ |
ngram 크기보다 작은 상수 인자를 가진 함수는 ngrambf_v1이 쿼리 최적화에 사용할 수 없어요.
(*) hasTokenCaseInsensitive와 hasTokenCaseInsensitiveOrNull이 효과적이려면 tokenbf_v1 인덱스가 소문자 데이터에 만들어져야 해요. 예: INDEX idx (lower(str_col)) TYPE tokenbf_v1(512, 3, 0).
Bloom filter는 거짓 양성 일치를 가질 수 있으므로 ngrambf_v1, tokenbf_v1, sparse_grams, bloom_filter 인덱스는 함수 결과가 거짓일 것으로 예상되는 쿼리를 최적화하는 데 사용할 수 없어요.
예를 들어:
- 최적화할 수 있음:
s LIKE '%test%'NOT s NOT LIKE '%test%'s = 1NOT s != 1startsWith(s, 'test')
- 최적화할 수 없음:
NOT s LIKE '%test%'s NOT LIKE '%test%'NOT s = 1s != 1NOT startsWith(s, 'test')
Projections
Projections는 materialized views와 같지만 파트 레벨로 정의돼요. 일관성 보장과 쿼리에서의 자동 사용을 제공해요. Projections를 구현할 때 force_optimize_projection 설정도 고려해야 해요. Projections는 FINAL 수정자가 있는 SELECT 문에서는 지원되지 않아요.
Projection query
projection query는 projection을 정의하는 것이에요. 부모 테이블에서 암시적으로 데이터를 선택해요.
문법
SELECT <column list expr> [GROUP BY] <group keys expr> [ORDER BY] <expr>
Projections는 ALTER 문으로 수정하거나 버릴 수 있어요.
Projection 인덱스
Projection 인덱스는 projection 레벨 인덱스를 정의하는 가볍고 명시적인 방법을 제공해 projection 서브시스템을 확장해요. 외부적으로 projection 인덱스는 여전히 projection이지만 단순화된 문법과 더 명확한 의도를 가져요: materialized 데이터를 제공하는 대신 필터링 전용으로 헌신된 표현식을 정의해요.
내부적으로 projection 인덱스는 일반 projection처럼 순열(permuted) 행 순서로 원래 테이블을 materialize하지 않아요. 대신 순열은 숫자 순열 컬럼 _part_offset, 즉 SELECT _part_offset ORDER BY <index_expr>의 형태로 저장돼요.
문법
PROJECTION <name> INDEX <index_expr> TYPE <index_type>
예시:
CREATE TABLE example
(
id UInt64,
region String,
user_id UInt32,
PROJECTION region_proj INDEX region TYPE basic,
PROJECTION uid_proj INDEX user_id TYPE basic
)
ENGINE = MergeTree
ORDER BY id;
인덱스 유형
현재 지원됨:
- basic: 표현식에 대한 일반 MergeTree 인덱스와 동일
프레임워크는 향후 더 많은 인덱스 유형을 추가할 수 있게 해요.
Projection 저장
Projections는 파트 디렉터리 안에 저장돼요. 인덱스와 비슷하지만 익명 MergeTree 테이블의 파트를 저장하는 하위 디렉터리를 포함해요. 테이블은 projection의 정의 쿼리에 의해 유도돼요. GROUP BY 절이 있으면 기본 저장 엔진은 AggregatingMergeTree가 되고 모든 집계 함수는 AggregateFunction으로 변환돼요. ORDER BY 절이 있으면 MergeTree 테이블은 그것을 기본 키 표현식으로 사용해요. 병합 과정 중 projection 파트는 자신의 저장소의 병합 루틴에 의해 병합돼요. 부모 테이블 파트의 체크섬은 projection의 파트와 결합돼요. 다른 유지보수 작업은 스킵 인덱스와 비슷해요.
쿼리 분석
- projection이 주어진 쿼리에 답하는 데 사용할 수 있는지, 즉 기본 테이블을 쿼리하는 것과 같은 답을 생성하는지 확인해요
- 읽을 granule이 가장 적은 최상의 적합한 일치를 선택해요
- projection을 사용하는 쿼리 파이프라인은 원래 파트를 사용하는 것과 다를 거예요. 어떤 파트에 projection이 없으면 파이프라인을 추가해 그 자리에서 "project"할 수 있어요
동시 데이터 접근
동시 테이블 접근을 위해 다중 버전(multi-versioning)을 사용해요. 즉, 테이블이 동시에 읽히고 업데이트되면 데이터는 쿼리 시점에 현재인 파트 집합에서 읽혀져요. 긴 잠금이 없어요. 삽입은 읽기 연산의 방해가 되지 않아요. 테이블에서 읽는 것은 자동으로 병렬화돼요.
컬럼과 테이블의 TTL
값의 수명을 결정해요. TTL 절은 전체 테이블과 각 개별 컬럼에 설정할 수 있어요. 테이블 레벨 TTL은 디스크와 볼륨 사이의 데이터 자동 이동 논리, 또는 모든 데이터가 만료된 파트의 재압축을 지정할 수도 있어요.
표현식은 Date, Date32, DateTime 또는 DateTime64 데이터 타입으로 평가해야 해요.
TTL은 삽입 시점이 아니라 백그라운드 병합 중에 평가돼요. rand(), now(), now64() 같은 함수는 매 병합마다 다시 평가되어 예측할 수 없는 삭제 동작을 만들어요. ClickHouse는 컬럼 의존성이 전혀 없는 표현식을 차단하지만, 컬럼 참조와 섞인 비결정적 함수(예: ts + rand())는 현재 거부하지 않아요. TTL 표현식은 예측 가능한 결과를 위해 결정적이고 컬럼 파생된 값에만 기반해야 해요.
문법
컬럼의 수명(time-to-live) 설정:
TTL time_column
TTL time_column + interval
interval을 정의하려면 time interval 연산자를 사용해요. 예:
TTL date_time + INTERVAL 1 MONTH
TTL date_time + INTERVAL 15 HOUR
컬럼 TTL
컬럼의 값이 만료되면 ClickHouse는 컬럼 데이터 타입의 기본값으로 대체해요. 데이터 파트의 모든 컬럼 값이 만료되면 ClickHouse는 파일시스템에서 데이터 파트의 이 컬럼을 삭제해요. TTL 절은 키 컬럼에 사용할 수 없어요.
예시
TTL로 테이블 생성:
CREATE TABLE tab
(
d DateTime,
a Int TTL d + INTERVAL 1 MONTH,
b Int TTL d + INTERVAL 1 MONTH,
c String
)
ENGINE = MergeTree
PARTITION BY toYYYYMM(d)
ORDER BY d;
기존 테이블의 컬럼에 TTL 추가:
ALTER TABLE tab
MODIFY COLUMN
c String TTL d + INTERVAL 1 DAY;
컬럼의 TTL 변경:
ALTER TABLE tab
MODIFY COLUMN
c String TTL d + INTERVAL 1 MONTH;
테이블 TTL
테이블은 만료된 행 제거를 위한 표현식과 디스크 또는 볼륨 사이 파트의 자동 이동을 위한 여러 표현식을 가질 수 있어요. 테이블의 행이 만료되면 ClickHouse는 해당 행을 모두 삭제해요. 파트 이동이나 재압축의 경우 파트의 모든 행이 TTL 표현식 기준을 만족해야 해요.
TTL expr
[DELETE|RECOMPRESS codec_name1|TO DISK 'xxx'|TO VOLUME 'xxx'][, DELETE|RECOMPRESS codec_name2|TO DISK 'aaa'|TO VOLUME 'bbb'] ...
[WHERE conditions]
[GROUP BY key_expr [SET v1 = aggr_func(v1) [, v2 = aggr_func(v2) ...]] ]
TTL 규칙의 유형은 각 TTL 표현식 다음에 올 수 있어요. 표현식이 만족되면(현재 시간에 도달) 수행할 작업에 영향을 줘요.
DELETE- 만료된 행 삭제 (기본 동작)RECOMPRESS codec_name-codec_name으로 데이터 파트 재압축TO DISK 'aaa'- 파트를 디스크aaa로 이동TO VOLUME 'bbb'- 파트를 디스크bbb로 이동GROUP BY- 만료된 행 집계
DELETE 동작은 WHERE 절과 함께 사용해 필터링 조건에 기반해 만료된 행 중 일부만 삭제할 수 있어요.
TTL time_column + INTERVAL 1 MONTH DELETE WHERE column = 'value'
GROUP BY 표현식은 테이블 기본 키의 접두사여야 해요. 컬럼이 GROUP BY 표현식의 일부가 아니고 SET 절에 명시적으로 설정되지 않으면 결과 행에서 그룹화된 행의 임의 값(집계 함수 any가 적용된 것처럼)을 포함해요.
예시
TTL로 테이블 생성:
CREATE TABLE tab
(
d DateTime,
a Int
)
ENGINE = MergeTree
PARTITION BY toYYYYMM(d)
ORDER BY d
TTL d + INTERVAL 1 MONTH DELETE,
d + INTERVAL 1 WEEK TO VOLUME 'aaa',
d + INTERVAL 2 WEEK TO DISK 'bbb';
테이블의 TTL 변경:
ALTER TABLE tab
MODIFY TTL d + INTERVAL 1 DAY;
행이 한 달 후 만료되는 테이블 생성. 날짜가 월요일인 만료된 행이 삭제돼요.
CREATE TABLE table_with_where
(
d DateTime,
a Int
)
ENGINE = MergeTree
PARTITION BY toYYYYMM(d)
ORDER BY d
TTL d + INTERVAL 1 MONTH DELETE WHERE toDayOfWeek(d) = 1;
만료된 행이 재압축되는 테이블 생성:
CREATE TABLE table_for_recompression
(
d DateTime,
key UInt64,
value String
) ENGINE MergeTree()
ORDER BY tuple()
PARTITION BY key
TTL d + INTERVAL 1 MONTH RECOMPRESS CODEC(ZSTD(17)), d + INTERVAL 1 YEAR RECOMPRESS CODEC(LZ4HC(10))
SETTINGS min_rows_for_wide_part = 0, min_bytes_for_wide_part = 0;
만료된 행이 집계되는 테이블 생성. 결과 행에서 x는 그룹화된 행들에 걸친 최대값, y는 최소값, d는 그룹화된 행들의 임의 값을 포함해요.
CREATE TABLE table_for_aggregation
(
d DateTime,
k1 Int,
k2 Int,
x Int,
y Int
)
ENGINE = MergeTree
ORDER BY (k1, k2)
TTL d + INTERVAL 1 MONTH GROUP BY k1, k2 SET x = max(x), y = min(y);
만료된 데이터 제거
만료된 TTL이 있는 데이터는 ClickHouse가 데이터 파트를 병합할 때 제거돼요. ClickHouse가 데이터가 만료된 것을 감지하면 예정되지 않은(off-schedule) 병합을 수행해요. 그런 병합의 빈도를 제어하려면 merge_with_ttl_timeout을 설정할 수 있어요. 값이 너무 낮으면 많은 자원을 소비할 수 있는 많은 예정되지 않은 병합을 수행해요.
병합 사이에 SELECT 쿼리를 수행하면 만료된 데이터를 얻을 수 있어요. 그것을 피하려면 SELECT 전에 OPTIMIZE 쿼리를 사용해요.
함께 보기
디스크 유형
로컬 블록 장치 외에 ClickHouse는 다음 저장 유형을 지원해요.
- S3 및 MinIO용 s3
- GCS용 gcs
- Azure Blob Storage용 blob_storage_disk
- HDFS용 hdfs
- 웹에서 읽기 전용용 web
- 로컬 캐싱용 cache
- S3로 백업용 s3_plain
- S3의 불변·비복제 테이블용 s3_plain_rewritable
데이터 저장을 위한 여러 블록 장치 사용
소개
MergeTree 패밀리 테이블 엔진은 여러 블록 장치에 데이터를 저장할 수 있어요. 예를 들어 특정 테이블의 데이터가 암묵적으로 "핫(hot)"과 "콜드(cold)"로 나뉠 때 유용할 수 있어요. 가장 최근 데이터는 정기적으로 요청되지만 적은 공간만 필요해요. 반대로 fat-tailed 과거 데이터는 드물게 요청돼요. 여러 디스크를 사용할 수 있다면 "핫" 데이터는 빠른 디스크(예: NVMe SSD나 메모리)에, "콜드" 데이터는 상대적으로 느린 디스크(예: HDD)에 두면 돼요.
이것은 S3 및 다른 객체 스토리지 디스크를 포함한 모든 디스크 유형에 적용돼요. 예를 들어 단일 볼륨 내 여러 S3 버킷에 데이터를 분산하거나, 로컬 디스크에서 S3로 데이터를 이동하는 계층 정책을 만들 수 있어요. 자세한 내용은 Multiple volumes와 함께 S3 디스크 사용 참고.
데이터 파트는 MergeTree-엔진 테이블의 최소 이동 가능 단위예요. 한 파트에 속한 데이터는 한 디스크에 저장돼요. 데이터 파트는 백그라운드에서(사용자 설정에 따라) 그리고 ALTER 쿼리를 통해서도 디스크 사이에서 이동할 수 있어요.
용어
- 디스크(Disk) — 파일시스템에 마운트된 블록 장치
- 기본 디스크(Default disk) — path 서버 설정에 지정된 경로를 저장하는 디스크
- 볼륨(Volume) — 같은 디스크들의 정렬된 집합 (JBOD와 유사)
- 저장 정책(Storage policy) — 볼륨 집합과 그 사이에서 데이터를 이동하는 규칙
설명된 엔티티에 주어진 이름은 시스템 테이블 system.storage_policies와 system.disks에서 찾을 수 있어요. 구성된 저장 정책 중 하나를 테이블에 적용하려면 MergeTree-엔진 패밀리 테이블의 storage_policy 설정을 사용해요.
구성
디스크, 볼륨, 저장 정책은 <storage_configuration> 태그 안에 config.d 디렉터리의 파일에 선언해야 해요. 디스크는 쿼리의 SETTINGS 섹션에도 선언할 수 있어요. 이것은 예를 들어 URL에 호스팅된 디스크를 임시로 attach하는 임시(ad-hoc) 분석에 유용해요. 자세한 내용은 dynamic storage 참고.
구성 구조:
<storage_configuration>
<disks>
<disk_name_1> <!-- disk name -->
<path>/mnt/fast_ssd/clickhouse/</path>
</disk_name_1>
<disk_name_2>
<path>/mnt/hdd1/clickhouse/</path>
<keep_free_space_bytes>10485760</keep_free_space_bytes>
</disk_name_2>
<disk_name_3>
<path>/mnt/hdd2/clickhouse/</path>
<keep_free_space_bytes>10485760</keep_free_space_bytes>
</disk_name_3>
...
</disks>
...
</storage_configuration>
태그:
<disk_name_N>— 디스크 이름. 모든 디스크에 대해 이름은 달라야 해요path— 서버가 데이터(data와shadow폴더)를 저장하는 경로. '/'로 끝나야 해요keep_free_space_bytes— 예약할 여유 디스크 공간의 양
디스크 정의의 순서는 중요하지 않아요.
저장 정책 구성 마크업:
<storage_configuration>
...
<policies>
<policy_name_1>
<volumes>
<volume_name_1>
<disk>disk_name_from_disks_configuration</disk>
<max_data_part_size_bytes>1073741824</max_data_part_size_bytes>
<load_balancing>round_robin</load_balancing>
</volume_name_1>
<volume_name_2>
<!-- configuration -->
</volume_name_2>
<!-- more volumes -->
</volumes>
<move_factor>0.2</move_factor>
</policy_name_1>
<policy_name_2>
<!-- configuration -->
</policy_name_2>
<!-- more policies -->
</policies>
...
</storage_configuration>
태그:
policy_name_N— 정책 이름. 정책 이름은 고유해야 해요volume_name_N— 볼륨 이름. 볼륨 이름은 고유해야 해요disk— 볼륨 내 디스크max_data_part_size_bytes— 볼륨의 디스크 중 어떤 것에 저장할 수 있는 파트의 최대 크기. 병합된 파트의 크기가max_data_part_size_bytes보다 크다고 추정되면 그 파트는 다음 볼륨에 기록돼요. 기본적으로 이 기능은 새/작은 파트를 핫(SSD) 볼륨에 유지하고 크기가 커지면 콜드(HDD) 볼륨으로 이동하게 해줘요. 정책에 볼륨이 하나만 있으면 이 설정을 사용하지 마세요move_factor— 사용 가능한 공간이 이 요소보다 낮아지면 데이터가 (있으면) 다음 볼륨으로 자동으로 이동하기 시작해요 (기본 0.1). ClickHouse는 기존 파트를 크기순(내림차순)으로 정렬하고move_factor조건을 충족하기에 충분한 총 크기의 파트를 선택해요. 모든 파트의 총 크기가 불충분하면 모든 파트가 이동돼요perform_ttl_move_on_insert— 데이터 파트 INSERT 시 TTL 이동을 비활성화해요. 기본적으로 (활성화되면) TTL 이동 규칙에 의해 이미 만료된 데이터 파트를 삽입하면 즉시 이동 규칙에 선언된 볼륨/디스크로 이동돼요. 이것은 대상 볼륨/디스크가 느리면(예: S3) 삽입을 크게 느리게 할 수 있어요. 비활성화하면 이미 만료된 데이터 파트는 기본 볼륨에 기록된 다음 TTL 볼륨으로 이동돼요load_balancing- 디스크 균형 정책,round_robin또는least_usedleast_used_ttl_ms- 모든 디스크의 사용 가능 공간 갱신 타임아웃(밀리초) 구성 (0- 항상 갱신,-1- 절대 갱신하지 않음, 기본60000). 디스크가 ClickHouse만 사용하고 온라인 파일시스템 리사이즈/슈링크 대상이 아니면-1을 사용할 수 있지만, 다른 모든 경우에는 부적절한 공간 분배로 이어질 수 있으므로 권장하지 않아요prefer_not_to_merge— 이 설정을 사용하지 마세요. 이 볼륨에서 데이터 파트 병합을 비활성화해요 (이것은 해롭고 성능 저하를 일으켜요). 이 설정이 활성화되면(하지 마세요) 이 볼륨에서 데이터 병합이 허용되지 않아요 (좋지 않아요). 이것은 ClickHouse가 느린 디스크로 작업하는 방식을 제어할 수 있게 해줘요 (하지만 ClickHouse가 더 잘 알므로, 제발 이 설정을 사용하지 마세요)volume_priority— 볼륨이 채워지는 우선순위(순서)를 정의해요. 값이 낮을수록 우선순위가 높아요. 매개변수 값은 자연수여야 하고 숫자를 건너뛰지 않고 1에서 N(가장 낮은 우선순위)까지의 범위를 집합적으로 덮어야 해요
모든 볼륨이 태그되면 주어진 순서로 우선순위가 매겨져요. 일부 볼륨만 태그되면 태그가 없는 것들은 가장 낮은 우선순위를 갖고 구성에 정의된 순서로 우선순위가 매겨져요. 태그된 볼륨이 없으면 구성에 선언된 순서에 따라 우선순위가 설정돼요. 두 볼륨은 같은 우선순위 값을 가질 수 없어요.
구성 예시:
<storage_configuration>
...
<policies>
<hdd_in_order> <!-- policy name -->
<volumes>
<single> <!-- volume name -->
<disk>disk1</disk>
<disk>disk2</disk>
</single>
</volumes>
</hdd_in_order>
<moving_from_ssd_to_hdd>
<volumes>
<hot>
<disk>fast_ssd</disk>
<max_data_part_size_bytes>1073741824</max_data_part_size_bytes>
</hot>
<cold>
<disk>disk1</disk>
</cold>
</volumes>
<move_factor>0.2</move_factor>
</moving_from_ssd_to_hdd>
<small_jbod_with_external_no_merges>
<volumes>
<main>
<disk>jbod1</disk>
</main>
<external>
<disk>external</disk>
</external>
</volumes>
</small_jbod_with_external_no_merges>
</policies>
...
</storage_configuration>
주어진 예시에서 hdd_in_order 정책은 round-robin 접근 방식을 구현해요. 따라서 이 정책은 하나의 볼륨(single)만 정의하고 데이터 파트는 모든 디스크에 순환 순서로 저장돼요. 이 정책은 여러 비슷한 디스크가 시스템에 마운트되어 있지만 RAID가 구성되지 않은 경우에 꽤 유용할 수 있어요. 각 개별 디스크 드라이브는 신뢰할 수 없고 복제 계수 3 이상으로 보상하고 싶을 수 있다는 점을 명심해요.
시스템에 서로 다른 종류의 디스크가 있으면 moving_from_ssd_to_hdd 정책을 대신 사용할 수 있어요. hot 볼륨은 SSD 디스크(fast_ssd)로 구성되고, 이 볼륨에 저장할 수 있는 파트의 최대 크기는 1GB예요. 1GB보다 큰 모든 파트는 HDD 디스크 disk1을 포함하는 cold 볼륨에 직접 저장돼요. 또한 fast_ssd 디스크가 80% 이상 채워지면 데이터가 백그라운드 프로세스에 의해 disk1로 전송돼요.
저장 정책 내 볼륨 열거 순서는 나열된 볼륨 중 적어도 하나에 명시적 volume_priority 매개변수가 없으면 중요해요. 볼륨이 초과 채워지면 데이터가 다음 것으로 이동돼요. 디스크 열거 순서도 중요해요. 데이터가 번갈아 그들에 저장되기 때문이에요.
테이블을 만들 때 구성된 저장 정책 중 하나를 적용할 수 있어요.
CREATE TABLE table_with_non_default_policy (
EventDate Date,
OrderID UInt64,
BannerID UInt64,
SearchPhrase String
) ENGINE = MergeTree
ORDER BY (OrderID, BannerID)
PARTITION BY toYYYYMM(EventDate)
SETTINGS storage_policy = 'moving_from_ssd_to_hdd'
default 저장 정책은 <path>에 주어진 하나의 디스크만으로 구성된 하나의 볼륨만 사용하는 것을 의미해요. 테이블 생성 후 [ALTER TABLE … MODIFY SETTING] 쿼리로 저장 정책을 변경할 수 있고, 새 정책은 같은 이름의 모든 이전 디스크와 볼륨을 포함해야 해요.
데이터 파트의 백그라운드 이동을 수행하는 스레드 수는 background_move_pool_size 설정으로 변경할 수 있어요.
세부 사항
MergeTree 테이블의 경우 데이터가 여러 방식으로 디스크에 도달해요:
- 삽입 결과로 (
INSERT쿼리) - 백그라운드 병합과 mutations 중
- 다른 복제본에서 다운로드할 때
- 파티션 동결 ALTER TABLE … FREEZE PARTITION의 결과로
mutations와 파티션 동결을 제외한 모든 경우에 파트는 주어진 저장 정책에 따라 볼륨과 디스크에 저장돼요.
- 파트 저장에 충분한 디스크 공간(
unreserved_space > current_part_size)이 있고 주어진 크기의 파트를 저장할 수 있는(max_data_part_size_bytes > current_part_size) 첫 번째 볼륨(정의 순서대로)이 선택돼요 - 이 볼륨 내에서, 이전 데이터 청크를 저장하는 데 사용된 디스크 다음에 오고 파트 크기보다 많은 여유 공간(
unreserved_space - keep_free_space_bytes > current_part_size)이 있는 디스크가 선택돼요
내부적으로 mutations와 파티션 동결은 하드 링크를 사용해요. 서로 다른 디스크 사이의 하드 링크는 지원되지 않으므로 그런 경우 결과 파트는 초기 것들과 같은 디스크에 저장돼요.
백그라운드에서 파트는 볼륨 사이에서 구성 파일에 볼륨이 선언된 순서에 따라 여유 공간의 양(move_factor 매개변수)을 기준으로 이동돼요. 데이터는 마지막 것에서 첫 번째 것으로는 결코 전송되지 않아요. 백그라운드 이동을 모니터링하려면 시스템 테이블 system.part_log(필드 type = MOVE_PART)와 system.parts(필드 path와 disk)를 사용할 수 있어요. 또한 상세 정보는 서버 로그에서 찾을 수 있어요.
사용자는 쿼리 ALTER TABLE … MOVE PART|PARTITION … TO VOLUME|DISK …로 파트 또는 파티션을 한 볼륨에서 다른 볼륨으로 강제로 이동할 수 있어요. 모든 백그라운드 연산 제한이 고려돼요. 쿼리는 자체적으로 이동을 시작하고 백그라운드 연산이 완료되기를 기다리지 않아요. 사용자는 충분한 여유 공간이 없거나 필요한 조건 중 하나가 충족되지 않으면 오류 메시지를 받아요.
데이터 이동은 데이터 복제를 방해하지 않아요. 따라서 다른 복제본의 같은 테이블에 대해 다른 저장 정책을 지정할 수 있어요.
백그라운드 병합과 mutations가 완료된 후 이전 파트는 일정 시간(old_parts_lifetime) 후에만 제거돼요. 이 시간 동안 그들은 다른 볼륨이나 디스크로 이동되지 않아요. 따라서 파트가 마침내 제거될 때까지 그들은 점유된 디스크 공간 평가에 여전히 고려돼요.
사용자는 min_bytes_to_rebalance_partition_over_jbod 설정으로 JBOD 볼륨의 다른 디스크에 새 큰 파트를 균형 있게 할당할 수 있어요.
데이터 저장을 위한 외부 스토리지 사용
MergeTree 패밀리 테이블 엔진은 s3, azure_blob_storage, hdfs 유형의 디스크를 사용해 S3, AzureBlobStorage, HDFS에 데이터를 저장할 수 있어요. 자세한 내용은 외부 스토리지 옵션 구성 참고.
S3를 s3 유형 디스크로 사용한 외부 저장 예시.
구성 마크업:
<storage_configuration>
...
<disks>
<s3>
<type>s3</type>
<support_batch_delete>true</support_batch_delete>
<endpoint>https://clickhouse-public-datasets.s3.amazonaws.com/my-bucket/root-path/</endpoint>
<access_key_id>your_access_key_id</access_key_id>
<secret_access_key>your_secret_access_key</secret_access_key>
<region></region>
<header>Authorization: Bearer SOME-T...der>
<server_side_encryption_customer_key_base64>your_base64_encoded_customer_key</server_side_encryption_customer_key_base64>
<server_side_encryption_kms_key_id>your_kms_key_id</server_side_encryption_kms_key_id>
<server_side_encryption_kms_encryption_context>your_kms_encryption_context</server_side_encryption_kms_encryption_context>
<server_side_encryption_kms_bucket_key_enabled>true</server_side_encryption_kms_bucket_key_enabled>
<proxy>
<uri>http://proxy1</uri>
<uri>http://proxy2</uri>
</proxy>
<connect_timeout_ms>10000</connect_timeout_ms>
<request_timeout_ms>5000</request_timeout_ms>
<s3_retry_attempts>10</s3_retry_attempts>
<s3_max_single_read_retries>4</s3_max_single_read_retries>
<min_bytes_for_seek>1000</min_bytes_for_seek>
<metadata_path>/var/lib/clickhouse/disks/s3/</metadata_path>
<skip_access_check>false</skip_access_check>
</s3>
<s3_cache>
<type>cache</type>
<disk>s3</disk>
<path>/var/lib/clickhouse/disks/s3_cache/</path>
<max_size>10Gi</max_size>
</s3_cache>
</disks>
...
</storage_configuration>
또한 외부 스토리지 옵션 구성을 참고해요.
Multiple volumes와 함께 S3 디스크 사용
S3(및 다른 객체 스토리지) 디스크는 로컬 디스크와 같은 방식으로 다중 디스크 및 다중 볼륨 저장 정책에서 사용할 수 있어요. 이를 통해 단일 볼륨(JBOD 스타일) 내 여러 S3 버킷에 데이터를 분산하거나 S3 볼륨으로 계층 저장 정책을 설정할 수 있어요.
예를 들어 round-robin 방식으로 두 S3 버킷에 데이터를 분산하려면:
<storage_configuration>
<disks>
<s3_bucket1>
<type>s3</type>
<endpoint>https://s3.amazonaws.com/bucket-1/data/</endpoint>
<access_key_id>your_access_key_id</access_key_id>
<secret_access_key>your_secret_access_key</secret_access_key>
</s3_bucket1>
<s3_bucket2>
<type>s3</type>
<endpoint>https://s3.amazonaws.com/bucket-2/data/</endpoint>
<access_key_id>your_access_key_id</access_key_id>
<secret_access_key>your_secret_access_key</secret_access_key>
</s3_bucket2>
</disks>
<policies>
<s3_multi_bucket>
<volumes>
<main>
<disk>s3_bucket1</disk>
<disk>s3_bucket2</disk>
</main>
</volumes>
</s3_multi_bucket>
</policies>
</storage_configuration>
데이터가 오래될수록 로컬 SSD에서 S3로 이동하는 것처럼 로컬 볼륨과 S3 볼륨을 계층 정책으로 결합할 수도 있어요.
<storage_configuration>
<disks>
<local_ssd>
<path>/mnt/fast_ssd/clickhouse/</path>
</local_ssd>
<s3_cold>
<type>s3</type>
<endpoint>https://s3.amazonaws.com/cold-storage/data/</endpoint>
<access_key_id>your_access_key_id</access_key_id>
<secret_access_key>your_secret_access_key</secret_access_key>
</s3_cold>
</disks>
<policies>
<local_to_s3>
<volumes>
<hot>
<disk>local_ssd</disk>
<max_data_part_size_bytes>1073741824</max_data_part_size_bytes>
</hot>
<cold>
<disk>s3_cold</disk>
</cold>
</volumes>
<move_factor>0.2</move_factor>
</local_to_s3>
</policies>
</storage_configuration>
S3 인증에 use_environment_credentials를 사용할 때 환경 자격 증명(AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN)은 모든 S3 디스크에서 공유돼요. 다른 디스크에 다른 환경 자격 증명을 사용하는 것은 불가능해요. 각 S3 디스크에 다른 자격 증명이 필요하면 디스크마다 명시적 access_key_id와 secret_access_key 설정을 사용해요.
공유 스토리지에서 one-writer, many-readers 시나리오로 비복제 MergeTree 테이블을 설정할 수 있어요. 이것은 Reader에 설정할 수 있는 파츠 목록의 자동 새로고침에 의해 제공돼요. 이는 복제본 전체에 공유 파일시스템 메타데이터(또는 table_disk = true인 테이블 로컬 디스크)가 필요하다는 점을 유의해요. refresh_parts_interval and table_disk 참고.
ClickHouse 버전 22.3~22.7은 다른 캐시 구성을 사용해요. 그 버전 중 하나를 사용한다면 로컬 캐시 사용을 참고해요.
가상 컬럼
_part— 파트의 이름_part_index— 쿼리 결과에서 파트의 순차 인덱스_part_starting_offset— 쿼리 결과에서 파트의 누적 시작 행_part_offset— 파트의 행 번호_part_granule_offset— 파트의 granule 번호_partition_id— 파티션의 이름_part_uuid— 고유 파트 식별자 (MergeTree 설정assign_part_uuids가 활성화된 경우)_part_data_version— 파트의 데이터 버전 (최소 블록 번호 또는 mutation 버전)_partition_value—partition by표현식의 값(튜플)_sample_factor— 샘플 계수 (쿼리에서)_block_number— 삽입 시 할당된 행의 원래 블록 번호.enable_block_number_column설정이 활성화되면 병합에서 유지됨_block_offset— 삽입 시 할당된 블록의 원래 행 번호.enable_block_offset_column설정이 활성화되면 병합에서 유지됨_disk_name— 저장에 사용되는 디스크 이름
컬럼 통계
통계 선언은 *MergeTree* 패밀리 테이블의 CREATE 쿼리 컬럼 섹션에 있어요.
CREATE TABLE tab
(
a Int64 STATISTICS(tdigest, uniq),
b Float64
)
ENGINE = MergeTree
ORDER BY a
통계는 물리적으로 저장된 컬럼을 요구해요. ALIAS 또는 EPHEMERAL 컬럼은 어떤 파트에도 기록되지 않으므로, 그들에 대한 통계 선언은 거부돼요.
ALTER 문으로 통계를 조작할 수도 있어요.
ALTER TABLE tab ADD STATISTICS b TYPE tdigest, uniq;
ALTER TABLE tab DROP STATISTICS a;
이러한 가벼운 통계는 컬럼의 값 분포에 대한 정보를 집계해요. 통계는 모든 파트에 저장되고 매 삽입마다 갱신돼요. set use_statistics = 1을 활성화할 때만 prewhere 최적화에 사용될 수 있어요.
통계로 파트 가지치기
use_statistics_for_part_pruning이 활성화되면 통계를 파트 가지치기에 사용할 수 있어요. 현재 basic 통계(그리고 사용 중단된 minmax 통계)만 파트 가지치기를 지원해요. 숫자와 시간 컬럼에서 basic(및 명시적 minmax)은 각 파트의 최소·최대값을 추적하므로 범위 조건이 경계가 일치할 수 없는 파트를 건너뛸 수 있어요. 어떤 타입의 Nullable 컬럼에도 basic은 각 파트의 NULL 값 수를 추적해요. 그것은 IS NULL / IS NOT NULL 조건에 기반한 가지치기를 가능하게 해요. 숫자와 시간 컬럼에서는 NULL 값이 없는 파트에 대해 범위 경계도 좁혀줘요.
파트 가지치기는 쿼리 필터 조건이 그 파트의 어떤 행과도 일치할 수 없을 때 전체 데이터 파트 읽기를 건너뛸 수 있게 해줘요.
예시:
-- Create a table with basic statistics on the 'value' column
CREATE TABLE test_stats
(
id UInt64,
value Int64 STATISTICS(basic)
)
ENGINE = MergeTree
ORDER BY id;
SYSTEM STOP MERGES test_stats;
-- Insert data in separate inserts to create multiple parts
INSERT INTO test_stats SELECT number, number FROM numbers(1000); -- Part 1: value range [0, 999]
INSERT INTO test_stats SELECT number, number + 10000 FROM numbers(1000); -- Part 2: value range [10000, 10999]
SET use_statistics_for_part_pruning = 1;
-- This query will skip Part 1 entirely because its max value (999) < 5000
SELECT count() FROM test_stats WHERE value > 5000;
-- Use EXPLAIN to see the pruning effect
EXPLAIN indexes = 1 SELECT count() FROM test_stats WHERE value > 5000;
-- The output will show "Parts: 1/2" indicating one part was pruned
사용 가능한 컬럼 통계 유형
basic컬럼에서 파생된 단일 값 요약의 컴팩트 번들. 컬럼 타입에 따라 다음 조각이 채워져요:- 모든 컬럼에 대해: 타입의 기본값과 같은 행 수 (정수와 부동소수점은
0,String은'',Array는[],Nullable은NULL등), 이는 최적화 프로그램이col = <default>조건과IS NULL필터를 추정하게 해줌 - 값이 숫자로 표현되는 모든 컬럼(정수, 부동소수점,
Decimal*,Date*,DateTime*,Enum*,IPv4, …)에 대해: 최소·최대값, 이는 범위 필터의 선택도를 추정하고 파트 가지치기를 가능하게 함 String과FixedString컬럼에 대해: 비-NULL값의 총 바이트 길이 (이로부터 평균 문자열 길이를 파생할 수 있음)- 단일
basic통계는 한 번에 여러 것을 채울 수 있어요 — 예를 들어Nullable(UInt32)컬럼에서 숫자 최소·최대와NULL개수를 둘 다 추적해요. 모든 컬럼 타입에 대해 기본값 개수가 정의되므로basic은Array,Tuple,Map같은 복합 타입을 포함해 어떤 컬럼에도 선언할 수 있어요
- 모든 컬럼에 대해: 타입의 기본값과 같은 행 수 (정수와 부동소수점은
minmax(사용 중단)minmax통계는 사용이 중단됐어요.minmax의 상위 집합인basic통계를 대신 사용해요tdigesttdigest유형의 통계는 생성 비용이 높고 데이터 수집을 잠재적으로 느리게 할 수 있어요. 숫자 컬럼에 대한 근사 백분위수(예: 90번째 백분위수)를 계산할 수 있는 TDigest 스케치uniq컬럼이 얼마나 많은 고유 값을 포함하는지 추정을 제공하는 BJKST 스케치. 내부적으로 uniq 사용uniq_v2uniq와 비슷하지만 내부적으로 uniqCombined(12)(HyperLogLog의 변형) 사용.uniq보다 메모리를 적게 소비하고 더 빠르게 구축할 수 있어요countmincountmin유형의 통계는 생성 비용이 높고 데이터 수집을 잠재적으로 느리게 할 수 있어요. 컬럼의 각 값 빈도의 근사 개수를 제공하는 CountMin 스케치
지원되는 데이터 타입
| (U)Int*, Float*, Decimal(), Date, Boolean, Enum* | IPv4 | String or FixedString | Any other type | |
|---|---|---|---|---|
| basic | ✔ | ✔ | ✔ | ✔ (default count only) |
| countmin | ✔ | ✔ | ✔ | ✗ |
| minmax | ✔ | ✔ | ✗ | ✗ |
| tdigest | ✔ | ✗ | ✗ | ✗ |
| uniq | ✔ | ✔ | ✔ | ✗ |
| uniq_v2 | ✔ | ✔ | ✔ | ✗ |
위의 모든 것은 나열된 타입의 Nullable과 LowCardinality(Nullable) 래퍼도 받아들여요. basic은 추가로 다른 어떤 타입(Array, Tuple, Map 같은 복합 타입 포함)에도 선언될 수 있으며, 그 경우 기본값 개수만 기록해요.
지원되는 연산
| Equality filters (==) | Range filters (>, >=, <, <=) |
|
|---|---|---|
| basic | ✔ (default value only) | ✔ (numeric columns only) |
| countmin | ✔ | ✗ |
| minmax | ✗ | ✔ (numeric columns only) |
| tdigest | ✗ | ✔ (numeric columns only) |
| uniq | ✔ | ✗ |
| uniq_v2 | ✔ | ✗ |
basic은 비교 값이 컬럼의 내부 저장 기본값(숫자 타입의 원시 정수 0, String/FixedString의 '' / N개의 0 바이트, Nullable 컬럼의 NULL 등)과 일치할 때만 등식 필터에 정확히 답해요. 특히 Enum 타입의 경우 빠른 경로는 열거자 리터럴이 원시 정수 0에 매핑될 때만 작동해요. 원시 값 0을 가진 열거자가 없으면 통계는 등식 추정에 아무 기여도 하지 않아요. 다른 값에 대해서는 최적화 프로그램이 다른 통계로 빠져요.
String/FixedString 컬럼의 basic의 경우 통계는 총 비-NULL 바이트 길이(평균 문자열 길이 추정에 사용)와 기본값/NULL 개수를 기록해요. 범위 필터와 파트 가지치기는 그것에 의해 구동되지 않아요.
컬럼 레벨 설정
특정 MergeTree 설정은 컬럼 레벨에서 재정의될 수 있어요.
max_compress_block_size— 테이블에 쓰기 위한 압축 전 압축되지 않은 데이터 블록의 최대 크기min_compress_block_size— 다음 mark를 쓸 때 압축에 필요한 압축되지 않은 데이터 블록의 최소 크기
예시:
CREATE TABLE tab
(
id Int64,
document String SETTINGS (min_compress_block_size = 16777216, max_compress_block_size = 16777216)
)
ENGINE = MergeTree
ORDER BY id
컬럼 레벨 설정은 ALTER MODIFY COLUMN로 수정하거나 제거할 수 있어요. 예:
- 컬럼 선언에서
SETTINGS제거:
ALTER TABLE tab MODIFY COLUMN document REMOVE SETTINGS;
- 설정 수정:
ALTER TABLE tab MODIFY COLUMN document MODIFY SETTING min_compress_block_size = 8192;
- 하나 이상의 설정 재설정 (테이블의 CREATE 쿼리 컬럼 표현식의 설정 선언도 제거):
ALTER TABLE tab MODIFY COLUMN document RESET SETTING min_compress_block_size;