MergeTree 엔진과 MergeTree 계열의 다른 엔진(예: ReplacingMergeTree, AggregatingMergeTree)은 ClickHouse에서 가장 널리 사용되며 가장 안정적인 테이블 엔진입니다.
MergeTree 계열 테이블 엔진은 높은 데이터 수집 속도와 방대한 데이터 볼륨을 처리하도록 설계되었습니다.
삽입 작업이 수행되면 테이블 파트가 생성되며, 이 파트는 백그라운드 프로세스에 의해 다른 테이블 파트와 머지됩니다.
MergeTree 계열 테이블 엔진의 주요 기능은 다음과 같습니다.
- 테이블의 프라이머리 키(primary key)는 각 테이블 파트 내 정렬 순서(클러스터형 인덱스)를 결정합니다. 또한 프라이머리 키는 개별 행을 참조하지 않고, 그래뉼이라고 하는 8192개 행 단위의 블록을 참조합니다. 따라서 대규모 데이터 세트의 프라이머리 키도 main memory에 유지할 수 있을 만큼 작게 유지되면서, 디스크에 있는 데이터에도 빠르게 접근할 수 있습니다.
- 테이블은 임의의 파티션 표현식을 사용해 파티셔닝할 수 있습니다. 파티션 프루닝을 통해 쿼리 조건상 가능한 경우 읽기 대상에서 해당 파티션을 제외할 수 있습니다.
- 데이터는 고가용성, failover, 무중단 업그레이드를 위해 여러 클러스터 노드에 걸쳐 복제할 수 있습니다. 자세한 내용은 Data replication을 참조하십시오.
-
MergeTree테이블 엔진은 쿼리 최적화에 도움이 되도록 다양한 통계 유형과 샘플링 메서드를 지원합니다.
이름이 비슷하더라도 Merge 엔진은
*MergeTree 엔진과는 다릅니다.테이블 생성하기
쿼리 절
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가 활성화되어 있으면 CREATE TABLE SQL 문에 ORDER BY ()가 암묵적으로 추가됩니다. 프라이머리 키 선택을 참조하십시오.
PARTITION BY
PARTITION BY — 파티셔닝 키입니다. 선택 사항입니다. 대부분의 경우 파티션 키는 필요하지 않으며, 파티셔닝이 필요하더라도 일반적으로 월 단위보다 더 세분화된 파티션 키는 필요하지 않습니다. 파티셔닝은 쿼리 속도를 높여 주지 않습니다(ORDER BY 표현식과는 다릅니다). 지나치게 세분화된 파티셔닝은 절대 사용하지 마십시오. 데이터를 클라이언트 식별자나 이름을 기준으로 파티셔닝하지 마십시오(대신 클라이언트 식별자 또는 이름을 ORDER BY 표현식의 첫 번째 컬럼으로 지정하십시오).
월 단위로 파티셔닝하려면 toYYYYMM(date_column) 표현식을 사용하십시오. 여기서 date_column은 Date 타입의 날짜 컬럼입니다. 이 경우 파티션 이름은 "YYYYMM" 포맷을 사용합니다.
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을 참조하십시오.
설정
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 포맷으로 저장됩니다.
각 데이터 파트는 논리적으로 그래뉼로 나뉩니다. 그래뉼은 ClickHouse가 데이터를 조회할 때 읽는 가장 작은, 더 이상 나눌 수 없는 데이터 집합입니다. ClickHouse는 행이나 값을 분할하지 않으므로 각 그래뉼에는 항상 정수 개수의 행이 포함됩니다. 각 그래뉼의 첫 번째 행에는 해당 행의 프라이머리 키 값이 마크됩니다. ClickHouse는 각 데이터 파트마다 마크를 저장하는 인덱스 파일을 생성합니다. 또한 프라이머리 키 포함 여부와 관계없이 각 컬럼에 대해서도 동일한 마크를 저장합니다. 이러한 마크를 사용하면 컬럼 파일에서 데이터를 직접 찾을 수 있습니다.
그래뉼 크기는 테이블 엔진의 index_granularity 및 index_granularity_bytes 설정에 의해 제한됩니다. 그래뉼의 행 수는 행 크기에 따라 [1, index_granularity] 범위에 있습니다. 단일 행의 크기가 설정값보다 크면 그래뉼의 크기는 index_granularity_bytes를 초과할 수 있습니다. 이 경우 그래뉼의 크기는 해당 행의 크기와 같습니다.
쿼리의 프라이머리 키와 인덱스
(CounterID, Date) 프라이머리 키를 예시로 들어 보겠습니다. 이 경우 정렬과 인덱스는 다음과 같이 나타낼 수 있습니다.
CounterID in ('a', 'h')이면 서버는 마크 범위[0, 3)및[6, 8)에서 데이터를 읽습니다.CounterID IN ('a', 'h') AND Date = 3이면 서버는 마크 범위[1, 3)및[7, 8)에서 데이터를 읽습니다.Date = 3이면 서버는 마크 범위[1, 10]에서 데이터를 읽습니다.
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 엔진에서 데이터 파트를 머지할 때 추가 로직을 제공할 수 있습니다. 이런 경우에는 프라이머리 키와 다른 *정렬 키(sorting key)*를 지정하는 것이 적절합니다.
SELECT 쿼리 수행 시 ClickHouse 성능에 영향을 주지 않습니다.
ORDER BY tuple() 구문을 사용하면 프라이머리 키 없이 테이블을 생성할 수 있습니다. 이 경우 ClickHouse는 데이터를 삽입된 순서대로 저장합니다. INSERT ... SELECT 쿼리로 데이터를 삽입할 때 데이터 순서를 유지하려면 max_insert_threads = 1을 설정하십시오.
초기 순서대로 데이터를 선택하려면 single-threaded SELECT 쿼리를 사용하십시오.
정렬 키와 다른 프라이머리 키 선택하기
GROUP BY와 차원 기준 필터링을 사용해 측정값 컬럼의 값을 집계합니다. SummingMergeTree와 AggregatingMergeTree는 정렬 키 값이 동일한 행을 집계하므로, 정렬 키에 모든 차원을 추가하는 것이 자연스럽습니다. 그 결과 키 표현식은 긴 컬럼 목록으로 이루어지며, 새 차원이 추가될 때마다 이 목록도 자주 업데이트해야 합니다.
이 경우 효율적인 범위 스캔에 필요한 몇 개의 컬럼만 프라이머리 키에 남겨 두고, 나머지 차원 컬럼은 정렬 키 튜플에 추가하는 것이 좋습니다.
정렬 키에 대한 ALTER는 가벼운 작업입니다. 새 컬럼을 테이블과 정렬 키에 동시에 추가하더라도 기존 데이터 파트는 변경할 필요가 없기 때문입니다. 이전 정렬 키는 새 정렬 키의 접두사이고, 새로 추가된 컬럼에는 데이터가 없으므로, 테이블 수정 시점의 데이터는 이전 정렬 키와 새 정렬 키를 모두 기준으로 정렬된 상태가 됩니다.
쿼리에서 인덱스와 파티션 활용
SELECT 쿼리의 경우 ClickHouse는 인덱스를 사용할 수 있는지 분석합니다. WHERE/PREWHERE 절에 등호 또는 부등호 비교 연산을 나타내는 표현식이 있거나(결합 조건의 일부이거나 전체인 경우), 프라이머리 키 또는 파티셔닝 키에 포함된 컬럼이나 표현식, 이러한 컬럼의 일부 반복 함수, 또는 이러한 표현식의 논리적 관계에 대해 고정 접두사가 있는 IN 또는 LIKE 조건이 있으면 인덱스를 사용할 수 있습니다.
따라서 프라이머리 키의 하나 이상의 범위에 대해 쿼리를 빠르게 실행할 수 있습니다. 이 예시에서는 특정 추적 태그, 특정 태그와 날짜 범위, 특정 태그와 특정 날짜, 날짜 범위를 포함한 여러 태그 등에 대해 실행하는 쿼리가 빠르게 동작합니다.
다음과 같이 구성된 엔진을 살펴보겠습니다:
프라이머리 키의 결정적 표현식에 대한 인덱스 사용
length(), toDate(), lower(), left(), cityHash64(), toUUID()이며, now()나 rand()는 이에 해당하지 않습니다). 프라이머리 키에 결정적 표현식이 포함되어 있으면 ClickHouse는 이를 쿼리의 상수 값에 적용한 뒤, 그 결과를 사용해 프라이머리 키 인덱스에 대한 조건을 구성할 수 있습니다. 이를 통해 =, IN, has와 같은 프레디케이트에서 데이터 스키핑이 가능해집니다.
일반적인 사용 사례는 프라이머리 키를 간결하게 유지하는 것입니다(예: 긴 String 대신 해시를 저장). 그러면서도 원래 컬럼에 대한 프레디케이트에서 인덱스를 계속 사용할 수 있습니다.
결정적이지만 단사적이지는 않은 프라이머리 키의 예시:
length('alice')(및 다른 상수)를 한 번만 계산하고, 그 길이 값을 사용해 프라이머리 키 인덱스의 범위를 좁힙니다. 문자열의 길이는 단사 함수가 아니므로, 서로 다른 user_id 문자열이 같은 길이를 가질 수 있어 인덱스가 추가 그래뉼(거짓 양성)을 읽을 수 있습니다. 원래 프레디케이트(user_id = ..., IN 등)는 읽은 뒤에도 계속 적용되므로 결과의 정확성은 유지됩니다.
결정적 표현식이 또한 단사적인 경우(사용된 인수 타입에서 서로 다른 입력이 같은 출력을 만들 수 없는 경우), ClickHouse는 부정형인 !=, NOT IN, NOT has(...)에도 인덱스를 효과적으로 사용할 수 있습니다. 예를 들어 reverse(p)와 hex(p)는 String에 대해 단사적입니다.
단사적인 프라이머리 키의 예시:
부분적으로 단조인 프라이머리 키에 대한 인덱스 사용
데이터 스키핑 인덱스
CREATE 쿼리의 컬럼 섹션에서 지정합니다.
*MergeTree 계열의 테이블에서는 데이터 스키핑 인덱스를 지정할 수 있습니다.
이 인덱스는 granularity_value개의 그래뉼로 구성된 블록에 대해 지정된 표현식의 일부 정보를 집계합니다(그래뉼의 크기는 테이블 엔진의 index_granularity 설정으로 지정합니다). 그런 다음 이러한 집계 정보는 SELECT 쿼리에서 사용되어, where 쿼리 조건을 만족할 수 없는 큰 데이터 블록을 건너뛰고 디스크에서 읽어야 하는 데이터 양을 줄입니다.
GRANULARITY 절은 생략할 수 있으며, granularity_value의 기본값은 1입니다.
예시
스킵 인덱스 유형
MergeTree 테이블 엔진은 다음과 같은 스킵 인덱스 유형을 지원합니다.
스킵 인덱스를 성능 최적화에 활용하는 방법에 대한 자세한 내용은
“ClickHouse 데이터 스키핑 인덱스 이해하기”를 참조하십시오.
MinMax인덱스Set인덱스bloom_filter인덱스ngrambf_v1인덱스 (사용 중단 예정)tokenbf_v1인덱스 (사용 중단 예정)text인덱스vector_similarity인덱스
MinMax 스킵 인덱스
tuple이면 tuple의 각 요소에 대한 최솟값과 최댓값이 저장됩니다.)
구문
Set
max_rows개까지 저장합니다.
max_rows = 0은 “모든 고유 값을 저장”함을 의미합니다.
구문
블룸 필터
구문
false_positive_rate 매개변수는 0과 1 사이의 값을 가질 수 있으며(기본값: 0.025), 양성 결과가 생성될 확률을 지정합니다(이 값이 높을수록 읽어야 하는 데이터 양이 증가합니다).
다음 데이터 타입이 지원됩니다.
(U)Int*Float*EnumDateDateTimeStringFixedStringArrayLowCardinalityNullableUUIDMap
JSON 데이터 타입: JSON 경로 인덱싱
JSON 데이터 타입의 경우, JSONAllPaths 함수를 사용해 경로 집합에 블룸 필터 인덱스를 생성할 수 있습니다. 이를 통해 쿼리한 JSON 경로가 없는 그래뉼은 스키핑할 수 있습니다. 자세한 내용은 JSON용 데이터 스키핑 인덱스를 참조하십시오.N-그램 블룸 필터 (사용 중단 예정)
ClickHouse 버전 26.2부터
text 인덱스가 일반 제공(GA)됨에 따라 ngrambf_v1 인덱스는 더 이상 전문 검색에 권장되지 않습니다.자세한 내용은 “text 인덱스를 사용한 전문 검색” 페이지를 참조하십시오.구문
이 인덱스는 다음 데이터 타입에서만 사용할 수 있습니다:
ngrambf_v1의 매개변수를 추정하려면 다음 사용자 정의 함수(UDF)를 사용할 수 있습니다.
UDFs for ngrambf_v1
total_number_of_all_gramsprobability_of_false_positives
4300개의 ngrams가 있고, 거짓 양성(false positive)이 0.0001보다 작기를 기대하는 경우를 가정합니다.
그러면 다음 쿼리를 실행하여 나머지 매개변수를 추정할 수 있습니다:
토큰 블룸 필터
ClickHouse 버전 26.2부터
text 인덱스가 일반 제공(GA)되므로, tokenbf_v1 인덱스는 전문 검색에는 더 이상 권장되지 않습니다.자세한 내용은 “텍스트 인덱스를 사용한 전문 검색” 페이지를 참조하십시오.구문
희소 grams 블룸 필터
ngrambf_v1와 비슷하지만, ngrams 대신 희소 grams 토큰을 사용합니다.
구문
텍스트 인덱스
벡터 유사도
함수 지원
WHERE 절의 조건에는 컬럼에 대해 동작하는 함수 호출이 포함됩니다. 해당 컬럼이 인덱스에 포함되어 있으면, ClickHouse는 그 함수를 처리할 때 이 인덱스를 사용하려고 시도합니다. ClickHouse는 인덱스 사용을 위해 서로 다른 함수 부분 집합을 지원합니다.
set 유형의 인덱스는 모든 함수와 함께 사용할 수 있습니다. 다른 인덱스 타입은 다음과 같이 지원됩니다:
상수 인수가 ngram 크기보다 작은 함수에는 쿼리 최적화를 위해
ngrambf_v1를 사용할 수 없습니다.
(*) hasTokenCaseInsensitive 및 hasTokenCaseInsensitiveOrNull가 효과적으로 동작하려면 tokenbf_v1 인덱스를 소문자로 변환된 데이터에 생성해야 합니다. 예를 들어 INDEX idx (lower(str_col)) TYPE tokenbf_v1(512, 3, 0)와 같습니다.
블룸 필터는 거짓 양성(false positive)이 발생할 수 있으므로,
ngrambf_v1, tokenbf_v1, sparse_grams, bloom_filter 인덱스는 함수 결과가 false로 예상되는 쿼리를 최적화하는 데 사용할 수 없습니다.예시:- 최적화 가능:
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')
PROJECTION
PROJECTION을 구현할 때는 force_optimize_projection 설정도 함께 고려해야 합니다.
SELECT 문에서는 지원되지 않습니다.
PROJECTION 쿼리
PROJECTION 인덱스
_part_offset의 형태로 저장됩니다. 즉, SELECT _part_offset ORDER BY <index_expr>와 같습니다.
구문
인덱스 유형
- basic: 표현식에 대한 일반 MergeTree 인덱스와 같습니다.
프로젝션 저장소
MergeTree 테이블의 파트를 저장하는 하위 디렉터리를 포함한다는 점이 다릅니다. 이 테이블은 프로젝션 정의 쿼리로부터 생성됩니다. GROUP BY 절이 있으면 하위 저장 엔진은 AggregatingMergeTree가 되며, 모든 집계 함수는 AggregateFunction으로 변환됩니다. ORDER BY 절이 있으면 MergeTree 테이블은 이를 프라이머리 키 표현식으로 사용합니다. 머지 과정에서는 프로젝션 파트도 해당 저장소의 머지 루틴을 통해 머지됩니다. 부모 테이블 파트의 체크섬은 프로젝션 파트의 체크섬과 결합됩니다. 그 밖의 유지 관리 작업은 스킵 인덱스와 비슷합니다.
쿼리 분석
- 프로젝션을 사용해 주어진 쿼리를 처리할 수 있는지, 즉 기반 테이블(base table)에 쿼리했을 때와 동일한 결과를 생성하는지 확인합니다.
- 읽어야 하는 그래뉼 수가 가장 적은, 사용 가능한 최적의 대상을 선택합니다.
- 프로젝션을 사용하는 쿼리 파이프라인은 원본 파트를 사용하는 파이프라인과 다릅니다. 일부 파트에 프로젝션이 없으면, 즉석에서 이를 “프로젝션”하기 위한 파이프라인을 추가할 수 있습니다.
동시 데이터 액세스
컬럼 및 테이블의 TTL
TTL 절은 전체 테이블과 각 개별 컬럼에 설정할 수 있습니다. 테이블 수준의 TTL에서는 디스크와 볼륨 간 데이터 자동 이동 로직이나, 모든 데이터가 만료된 파트의 재압축 로직도 지정할 수 있습니다.
표현식은 Date, Date32, DateTime 또는 DateTime64 데이터 타입으로 평가되어야 합니다.
구문
컬럼의 TTL 설정:
interval을 정의하려면 시간 인터벌 연산자를 사용하십시오. 예를 들면 다음과 같습니다:
컬럼 TTL
TTL 절은 키 컬럼에는 사용할 수 없습니다.
예시
TTL이 적용된 테이블 생성:
기존 테이블 컬럼에 TTL 추가하기
컬럼 TTL 변경
테이블 TTL
TTL 표현식 조건을 충족해야 합니다.
TTL 표현식 뒤에는 TTL 규칙 유형을 지정할 수 있습니다. 이 유형은 표현식이 만족될 때(현재 시점에 도달할 때) 수행할 작업을 결정합니다.
DELETE- 만료된 행을 삭제합니다(기본 동작).RECOMPRESS codec_name- 데이터 파트를codec_name으로 다시 압축합니다.TO DISK 'aaa'- 파트를 디스크aaa로 이동합니다.TO VOLUME 'bbb'- 파트를 디스크bbb로 이동합니다.GROUP BY- 만료된 행을 집계합니다.
DELETE 동작은 WHERE 절과 함께 사용하여 필터링 조건에 따라 만료된 행 일부만 삭제할 수 있습니다:
GROUP BY 표현식은 테이블 프라이머리 키의 접두사여야 합니다.
컬럼이 GROUP BY 표현식에 포함되지 않고 SET 절에서도 명시적으로 설정되지 않은 경우, 결과 행에는 그룹화된 행들 중 하나의 임의 값이 들어갑니다(마치 해당 컬럼에 집계 함수 any가 적용된 것과 같습니다).
예시
TTL이 있는 테이블 생성하기:
테이블의 TTL 변경:
만료된 행이 재압축되도록 테이블 생성:
x에는 그룹화된 행들 전체의 최댓값이, y에는 최솟값이, d에는 그룹화된 행들 중 하나의 임의 값이 들어갑니다.
만료된 데이터 삭제
TTL이 만료된 데이터는 ClickHouse가 데이터 파트를 머지할 때 삭제됩니다.
ClickHouse가 데이터 만료를 감지하면 예정되지 않은 머지를 수행합니다. 이러한 머지의 빈도를 제어하려면 merge_with_ttl_timeout을 설정할 수 있습니다. 이 값이 너무 낮으면 예정되지 않은 머지가 자주 수행되어 많은 리소스를 소비할 수 있습니다.
머지 사이에 SELECT 쿼리를 수행하면 만료된 데이터가 반환될 수 있습니다. 이를 방지하려면 SELECT 전에 OPTIMIZE 쿼리를 사용하십시오.
관련 항목
디스크 유형
s3for S3 및 MinIOgcsfor GCSblob_storage_diskfor Azure Blob Storagehdfsfor HDFSwebfor 웹의 읽기 전용 스토리지cachefor 로컬 캐싱s3_plainfor S3 백업용s3_plain_rewritablefor S3의 변경 불가능한 비복제 테이블용
데이터 저장에 여러 블록 디바이스 사용
소개
MergeTree 계열 테이블 엔진은 여러 블록 디바이스에 데이터를 저장할 수 있습니다. 예를 들어, 특정 테이블의 데이터가 자연스럽게 “hot” 데이터와 “cold” 데이터로 나뉘는 경우에 유용합니다. 최신 데이터는 자주 조회되지만 필요한 공간은 적습니다. 반대로 장기간 누적된 과거 데이터는 드물게 조회됩니다. 여러 디스크를 사용할 수 있다면 “hot” 데이터는 빠른 디스크(예: NVMe SSD 또는 메모리)에 두고, “cold” 데이터는 상대적으로 느린 디스크(예: HDD)에 둘 수 있습니다.
이는 S3 및 기타 객체 스토리지 디스크를 포함한 모든 디스크 유형에 적용됩니다. 예를 들어, 단일 볼륨 내의 여러 S3 버킷에 데이터를 분산할 수 있으며, 로컬 디스크에서 S3로 데이터를 이동하는 계층형 정책을 만들 수도 있습니다. 자세한 내용은 여러 볼륨에서 S3 디스크 사용하기를 참조하십시오.
데이터 파트는 MergeTree 엔진 테이블에서 이동할 수 있는 최소 단위입니다. 하나의 파트에 속한 데이터는 하나의 디스크에 저장됩니다. 데이터 파트는 백그라운드에서(사용자 설정에 따라) 디스크 간에 이동할 수 있으며, ALTER 쿼리를 통해서도 이동할 수 있습니다.
용어
- 디스크 — 파일 시스템에 마운트된 블록 디바이스입니다.
- 기본 디스크 — path 서버 설정에 지정된 경로를 저장하는 디스크입니다.
- 볼륨 — 동일한 디스크로 이루어진 순서 있는 집합입니다(JBOD와 유사).
- 스토리지 정책 — 볼륨 집합과 볼륨 간 데이터 이동 규칙입니다.
MergeTree 엔진 계열 테이블의 storage_policy 설정을 사용하십시오.
구성
<storage_configuration> 태그 안이나 config.d 디렉터리의 파일에서 선언해야 합니다.
구성 구조:
<disk_name_N>— 디스크 이름입니다. 모든 디스크의 이름은 서로 달라야 합니다.path— 서버가 데이터(data및shadow폴더)를 저장할 경로이며, 끝에 ’/‘가 있어야 합니다.keep_free_space_bytes— 예약해 둘 여유 디스크 공간의 크기입니다.
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_used를 사용합니다.least_used_ttl_ms- 모든 디스크의 사용 가능 공간을 갱신하는 timeout(밀리초 단위)을 구성합니다(0- 항상 갱신,-1- 갱신 안 함, 기본값은60000). 참고로 디스크를 ClickHouse만 사용하고 온라인 파일 시스템 크기 확장/축소 대상이 아닌 경우에는-1을 사용할 수 있습니다. 그 외의 경우에는 권장되지 않으며, 결국 잘못된 공간 분배로 이어집니다.prefer_not_to_merge— 이 설정은 사용하지 마십시오. 이 볼륨에서 데이터 파트 머지를 비활성화합니다(이는 해로우며 성능 저하를 초래합니다). 이 설정을 활성화하면(사용하지 마십시오) 이 볼륨에서는 데이터 머지가 허용되지 않습니다(좋지 않습니다). 이를 통해 느린 디스크에서 ClickHouse의 동작을 제어할 수는 있지만(실제로는 필요하지 않습니다), ClickHouse가 더 잘 판단하므로 이 설정은 사용하지 마십시오.volume_priority— 볼륨이 채워지는 우선순위(순서)를 정의합니다. 값이 낮을수록 우선순위가 높습니다. 매개변수 값은 자연수여야 하며, 숫자를 건너뛰지 않고 1부터 N까지의 범위를 모두 포함해야 합니다(N이 가장 낮은 우선순위).- 모든 볼륨에 태그가 지정된 경우, 지정된 순서대로 우선순위가 부여됩니다.
- 일부 볼륨에만 태그가 지정된 경우, 태그가 없는 볼륨이 가장 낮은 우선순위를 가지며 구성에 정의된 순서대로 우선순위가 부여됩니다.
- 어떤 볼륨에도 태그가 지정되지 않은 경우, 우선순위는 구성에 선언된 순서에 따라 설정됩니다.
- 두 볼륨이 같은 우선순위 값을 가질 수는 없습니다.
hdd_in_order 정책은 라운드 로빈 방식을 구현합니다. 따라서 이 정책은 하나의 볼륨(single)만 정의하며, 데이터 파트는 해당 볼륨의 모든 디스크에 순환 방식으로 저장됩니다. 이러한 정책은 시스템에 비슷한 디스크 여러 개가 마운트되어 있지만 RAID가 구성되지 않은 경우 매우 유용할 수 있습니다. 각 개별 디스크 드라이브는 신뢰성이 높지 않으므로, 이를 보완하기 위해 복제 계수를 3 이상으로 설정하는 것이 좋다는 점을 유의하십시오.
시스템에서 서로 다른 종류의 디스크를 사용할 수 있다면 대신 moving_from_ssd_to_hdd 정책을 사용할 수 있습니다. hot 볼륨은 SSD 디스크(fast_ssd)로 구성되며, 이 볼륨에 저장할 수 있는 파트의 최대 크기는 1GB입니다. 크기가 1GB를 초과하는 모든 파트는 HDD 디스크 disk1이 포함된 cold 볼륨에 직접 저장됩니다.
또한 fast_ssd 디스크 사용량이 80%를 초과하면 백그라운드 프로세스가 데이터를 disk1으로 이동합니다.
스토리지 정책 내에서 볼륨 나열 순서는, 나열된 볼륨 중 적어도 하나에 volume_priority 매개변수가 명시되어 있지 않은 경우 중요합니다.
볼륨이 가득 차면 데이터는 다음 볼륨으로 이동합니다. 디스크 나열 순서도 중요합니다. 데이터가 각 디스크에 번갈아 저장되기 때문입니다.
테이블을 생성할 때는 구성된 스토리지 정책 중 하나를 적용할 수 있습니다:
default 스토리지 정책은 <path>에 지정된 단일 디스크로만 이루어진 하나의 볼륨만 사용한다는 뜻입니다.
[ALTER TABLE … MODIFY SETTING] 쿼리를 사용하면 테이블 생성 후에도 스토리지 정책을 변경할 수 있으며, 새 정책에는 기존의 모든 디스크와 볼륨이 동일한 이름으로 포함되어야 합니다.
데이터 파트의 백그라운드 이동 작업을 수행하는 스레드 수는 background_move_pool_size 설정에서 변경할 수 있습니다.
세부 사항
MergeTree 테이블에서는 데이터가 여러 방식으로 디스크에 기록됩니다.
- 삽입(
INSERT쿼리)의 결과로 기록됩니다. - 백그라운드 머지 및 뮤테이션 중에 기록됩니다.
- 다른 레플리카에서 다운로드할 때 기록됩니다.
- 파티션 동결 ALTER TABLE … FREEZE PARTITION의 결과로 기록됩니다.
- 파트를 저장하기에 충분한 디스크 공간이 있고 (
unreserved_space > current_part_size), 해당 크기의 파트 저장이 허용되는 (max_data_part_size_bytes > current_part_size) 첫 번째 볼륨(정의된 순서 기준)을 선택합니다. - 이 볼륨 내에서는 이전 데이터 청크를 저장하는 데 사용한 디스크의 다음 디스크이면서, 파트 크기보다 많은 여유 공간이 있는 (
unreserved_space - keep_free_space_bytes > current_part_size) 디스크를 선택합니다.
move_factor 매개변수)을 기준으로 파트가 볼륨 간에 이동됩니다.
데이터는 마지막 볼륨에서 다른 볼륨으로 이동되지 않으며, 첫 번째 볼륨으로도 이동되지 않습니다. 백그라운드 이동 작업을 모니터링하려면 시스템 테이블 system.part_log (type = MOVE_PART 필드)와 system.parts (path 및 disk 필드)를 사용할 수 있습니다. 또한 자세한 정보는 서버 로그에서 확인할 수 있습니다.
사용자는 ALTER TABLE … MOVE PART|PARTITION … TO VOLUME|DISK … 쿼리를 사용해 파트 또는 파티션을 한 볼륨에서 다른 볼륨으로 강제로 이동할 수 있으며, 이때 백그라운드 작업에 대한 모든 제한 사항이 적용됩니다. 이 쿼리는 자체적으로 이동을 시작하며 백그라운드 작업이 완료될 때까지 기다리지 않습니다. 여유 공간이 충분하지 않거나 필요한 조건 중 하나라도 충족되지 않으면 오류 메시지가 반환됩니다.
데이터 이동은 데이터 복제를 방해하지 않습니다. 따라서 서로 다른 레플리카의 동일한 테이블에 서로 다른 스토리지 정책을 지정할 수 있습니다.
백그라운드 머지와 뮤테이션이 완료된 후에도 오래된 파트는 일정 시간이 지난 뒤에만 제거됩니다(old_parts_lifetime).
이 시간 동안에는 다른 볼륨이나 디스크로 이동되지 않습니다. 따라서 파트가 최종적으로 제거될 때까지는 점유한 디스크 공간을 평가할 때 계속 반영됩니다.
사용자는 min_bytes_to_rebalance_partition_over_jbod 설정을 사용해 JBOD 볼륨의 서로 다른 디스크에 새 대형 파트를 균형 있게 할당할 수 있습니다.
데이터 저장에 외부 스토리지 사용하기
s3, azure_blob_storage, hdfs 타입의 디스크를 사용해 데이터를 S3, AzureBlobStorage, HDFS에 저장할 수 있습니다. 자세한 내용은 외부 스토리지 옵션 구성을 참조하십시오.
S3 타입의 디스크를 사용하여 S3를 외부 스토리지로 사용하는 예시입니다.
구성 예시:
여러 볼륨에서 S3 디스크 사용하기
S3 인증에
use_environment_credentials를 사용하면 환경 자격 증명(AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN)이 모든 S3 디스크에서 공유됩니다. 디스크마다 서로 다른 환경 자격 증명을 사용하는 것은 불가능합니다. 각 S3 디스크마다 다른 자격 증명이 필요하면 디스크별로 명시적인 access_key_id 및 secret_access_key 설정을 대신 사용하십시오.table_disk = true를 사용해야 합니다). 자세한 내용은 refresh_parts_interval and table_disk를 참조하십시오.
cache 구성ClickHouse 22.3~22.7 버전은 다른 cache 구성을 사용합니다. 해당 버전 중 하나를 사용 중이라면 using local cache를 참조하십시오.
가상 컬럼
_part— 파트 이름입니다._part_index— 쿼리 결과에서 파트의 순차 인덱스입니다._part_starting_offset— 쿼리 결과에서 파트의 누적 시작 행입니다._part_offset— 파트 내 행 번호입니다._part_granule_offset— 파트 내 그래뉼 번호입니다._partition_id— 파티션 이름입니다._part_uuid— 고유한 파트 식별자입니다(MergeTree 설정assign_part_uuids가 활성화된 경우)._part_data_version— 파트의 데이터 버전입니다(최소 block 번호 또는 mutation 버전)._partition_value—partition by표현식의 값들(튜플)입니다._sample_factor— 샘플 계수입니다(쿼리 기준)._block_number— 삽입 시 할당된 행의 원래 block 번호이며, 설정enable_block_number_column이 활성화된 경우 머지 후에도 유지됩니다._block_offset— 삽입 시 할당된 block 내 원래 행 번호이며, 설정enable_block_offset_column이 활성화된 경우 머지 후에도 유지됩니다._disk_name— 저장소에 사용된 디스크 이름입니다.
컬럼 통계
*MergeTree* 엔진 계열 테이블의 CREATE 쿼리에서 컬럼 섹션에 위치합니다:
ALTER SQL 문으로 통계를 변경할 수도 있습니다:
set use_statistics = 1을 활성화한 경우에만 PREWHERE 최적화에 사용할 수 있습니다.
통계를 활용한 파트 프루닝
use_statistics_for_part_pruning이 활성화되면 통계를 파트 프루닝에 사용할 수 있습니다.
현재 basic 통계(및 지원 중단된 minmax 통계)만 파트 프루닝을 지원합니다. 이러한 통계가 컬럼에 정의되면 ClickHouse는 각 파트에서 해당 컬럼의 최솟값과 최댓값을 추적합니다.
파트 프루닝을 사용하면 쿼리 필터 조건과 일치하는 행이 전혀 없는 파트는 전체를 읽지 않고 건너뛸 수 있습니다.
예시:
사용 가능한 컬럼 통계 유형
-
basic컬럼에서 파생되는 단일 값 요약을 묶어 놓은 간결한 통계 묶음입니다. 컬럼 타입에 따라 다음 항목이 채워집니다:-
값이 숫자로 표현되는 모든 컬럼(정수, 부동소수점,
Decimal*,Date*,DateTime*,Enum*,IPv4, …): 범위 필터의 선택도를 추정하고 파트 프루닝을 가능하게 하는 최솟값과 최댓값 -
String및FixedString컬럼:NULL이 아닌 값의 전체 바이트 길이(여기서 평균 문자열 길이를 도출할 수 있음) -
Nullable및LowCardinality(Nullable)컬럼:NULL값의 개수이며, 최적화기는 이를 사용해 선택도 추정에서NULL행을 제외합니다. 하나의basic통계는 이들 중 여러 항목을 한 번에 채울 수 있습니다. 예를 들어Nullable(UInt32)컬럼에서는 숫자 최솟값/최댓값과NULL값 개수를 모두 추적합니다.minmax와 비교하면basic은String/FixedString컬럼에서도 추가로 동작하며,UUID나IPv6같은 타입의Nullable래퍼에 선언해NULL값 개수만 추적할 수도 있습니다.
-
값이 숫자로 표현되는 모든 컬럼(정수, 부동소수점,
-
minmax(지원 중단됨)
minmax 통계는 지원 중단되었으며 더 이상 생성할 수 없습니다(CREATE TABLE ... STATISTICS(minmax) 및 ALTER TABLE ... ADD/MODIFY STATISTICS ... TYPE minmax는 오류를 반환합니다). 기존 minmax 통계를 사용하는 테이블과 파트는 계속 동작합니다. 대신 basic 통계를 사용하십시오.tdigest
-
uniq컬럼에 포함된 고유값의 개수를 추정해 주는 BJKST 스케치입니다. 내부적으로uniq를 사용합니다. -
uniq_v2uniq와 유사하지만 내부적으로uniqCombined(12)(HyperLogLog의 변형)를 사용합니다.uniq보다 메모리를 덜 사용하며 더 빠르게 빌드할 수 있습니다. -
countmin
지원되는 데이터 타입
위에 나열된 타입의
Nullable 및 LowCardinality(Nullable) 래퍼도 모두 허용됩니다. Basic은 null 개수만 추적하기 위한 용도로 UUID 또는 IPv6 같은 타입의 Nullable 래퍼에도 추가로 선언할 수 있습니다.
지원되는 연산
String / FixedString 컬럼에서 basic은 전체
non-NULL 바이트 길이(평균 문자열 길이 추정에 사용)와 NULL 개수만 기록합니다.
범위 필터와 파트 프루닝에는 사용되지 않습니다.
컬럼 수준 설정
max_compress_block_size— 테이블에 쓰기 전에 압축하는 비압축 데이터 블록의 최대 크기입니다.min_compress_block_size— 다음 mark를 쓸 때 압축하기 위해 필요한 비압축 데이터 블록의 최소 크기입니다.
- 컬럼 선언에서
SETTINGS를 제거합니다:
- 설정을 수정하세요:
- 하나 이상의 설정을 재설정하고, 테이블의 CREATE 쿼리에서 해당 컬럼 표현식에 있는 설정 선언도 제거합니다.