데이터 스킵 인덱스 예제

데이터 스킵 인덱스 예제 (Data skipping index examples)

이 페이지는 ClickHouse 데이터 스킵 인덱스 예제를 한데 모아, 각 타입을 어떻게 선언하는지, 언제 사용하는지, 적용되었는지 어떻게 확인하는지 보여 줍니다. 모든 기능은 MergeTree 계열 테이블에서 동작해요.

출처: 문서

본문

이 페이지는 ClickHouse 데이터 스킵 인덱스 예제를 한데 모아, 각 타입을 어떻게 선언하는지, 언제 사용하는지, 적용되었는지 어떻게 검증하는지 보여 줍니다. 모든 기능은 MergeTree 계열 테이블과 동작합니다.

인덱스 문법:

INDEX name expr TYPE type(...) [GRANULARITY N]

ClickHouse는 여섯 가지 스킵 인덱스 타입을 지원합니다:

인덱스 타입 설명
minmax 각 granule의 최솟값과 최댓값을 추적
set(N) granule당 최대 N개의 서로 다른 값 저장
text 전체 텍스트 검색을 위한 토큰화된 문자열 데이터의 역인덱스
bloom_filter([false_positive_rate]) 존재 확인을 위한 확률적 필터
ngrambf_v1 부분 문자열 검색을 위한 N-gram 블룸 필터
tokenbf_v1 전체 텍스트 검색을 위한 토큰 기반 블룸 필터

각 섹션은 샘플 데이터와 함께 예제를 제공하고 쿼리 실행에서 인덱스 사용을 어떻게 검증하는지 보여 줍니다.

MinMax 인덱스

minmax 인덱스는 느슨하게 정렬된 데이터나 ORDER BY와 상관된 컬럼의 범위 조건에 가장 좋습니다.

-- Define in CREATE TABLE
CREATE TABLE events
(
  ts DateTime,
  user_id UInt64,
  value UInt32,
  INDEX ts_minmax ts TYPE minmax GRANULARITY 1
)
ENGINE=MergeTree
ORDER BY ts;

-- Or add later and materialize
ALTER TABLE events ADD INDEX ts_minmax ts TYPE minmax GRANULARITY 1;
ALTER TABLE events MATERIALIZE INDEX ts_minmax;

-- Query that benefits from the index
SELECT count() FROM events WHERE ts >= now() - 3600;

-- Verify usage
EXPLAIN indexes = 1
SELECT count() FROM events WHERE ts >= now() - 3600;

EXPLAIN과 가지치기가 있는 작업 예제를 보세요.

Set 인덱스

로컬(블록당) 카디널리티가 낮을 때 set 인덱스를 사용하세요. 각 블록에 서로 다른 값이 많으면 도움이 되지 않습니다.

ALTER TABLE events ADD INDEX user_set user_id TYPE set(100) GRANULARITY 1;
ALTER TABLE events MATERIALIZE INDEX user_set;

SELECT * FROM events WHERE user_id IN (101, 202);

EXPLAIN indexes = 1
SELECT * FROM events WHERE user_id IN (101, 202);

생성/구체화 워크플로와 전후 효과는 기본 연산 가이드에 나와 있습니다.

전체 텍스트 검색용 Text 인덱스 (text)

text는 토큰화된 텍스트 데이터에 대한 역인덱스입니다. 전체 텍스트 검색 워크로드용으로 특별히 설계되어 효율적이고 결정적인 토큰과 용어 룩업을 가능하게 합니다. 자연어 또는 대규모 텍스트 검색 사용 사례에 권장됩니다.

자세한 내용과 예제는 "Full-text Search with Text Indexes"를 참고하세요.

ALTER TABLE logs ADD INDEX msg_text msg TYPE text(tokenizer = splitByNonAlpha);
ALTER TABLE logs MATERIALIZE INDEX msg_text;

SELECT count() FROM logs WHERE hasAllTokens(msg, 'exception');

더 완전한 관측 가능성 예제는 여기 문서를 보세요.

text 인덱스는 완전히 결정적이며 토큰화와 텍스트 처리 측면에서 완전히 조정 가능하지만, 블룸 필터 기반 인덱스와 비교해 더 많은 스토리지를 소비합니다.

일반 Bloom filter (스칼라)

bloom_filter 인덱스는 "건초 더미에서 바늘 찾기" equality/IN 멤버십에 좋습니다. 선택적 파라미터인 오탐(false-positive) 비율(기본 0.025)을 받습니다.

ALTER TABLE events ADD INDEX value_bf value TYPE bloom_filter(0.01) GRANULARITY 3;
ALTER TABLE events MATERIALIZE INDEX value_bf;

SELECT * FROM events WHERE value IN (7, 42, 99);

EXPLAIN indexes = 1
SELECT * FROM events WHERE value IN (7, 42, 99);

부분 문자열 검색용 N-gram Bloom filter (ngrambf_v1) (폐기됨)

전체 텍스트 검색을 위한 ngrambf_v1 인덱스 사용은 ClickHouse 버전 >= 26.2에서 text 인덱스(자세한 내용은 여기)를 선호해 폐기되었습니다.

ngrambf_v1 인덱스는 문자열을 n-gram으로 나눕니다. LIKE '%...%' 쿼리에 잘 동작합니다. String/FixedString/Map(mapKeys/mapValues 통해)을 지원하며, 크기, 해시 수, 시드를 조정할 수 있습니다. N-gram bloom filter 문서에서 자세한 내용을 참고하세요.

-- Create index for substring search
ALTER TABLE logs ADD INDEX msg_ngram msg TYPE ngrambf_v1(3, 10000, 3, 7) GRANULARITY 1;
ALTER TABLE logs MATERIALIZE INDEX msg_ngram;

-- Substring search
SELECT count() FROM logs WHERE msg LIKE '%timeout%';

EXPLAIN indexes = 1
SELECT count() FROM logs WHERE msg LIKE '%timeout%';

이 가이드는 token과 ngram을 언제 사용할지 실제 예제를 보여 줍니다.

파라미터 최적화 도우미:

네 개의 ngrambf_v1 파라미터(n-gram 크기, 비트맵 크기, 해시 함수, 시드)는 성능과 메모리 사용에 큰 영향을 줍니다. 기대되는 n-gram 수와 원하는 오탐 비율에 기반해 최적의 비트맵 크기와 해시 함수 수를 계산하려면 다음 함수들을 사용하세요:

CREATE FUNCTION bfEstimateFunctions AS
(total_grams, bits) -> round((bits / total_grams) * log(2));

CREATE FUNCTION bfEstimateBmSize AS
(total_grams, p_false) -> ceil((total_grams * log(p_false)) / log(1 / pow(2, log(2))));

-- Example sizing for 4300 ngrams, p_false = 0.0001
SELECT bfEstimateBmSize(4300, 0.0001) / 8 AS size_bytes;  -- ~10304
SELECT bfEstimateFunctions(4300, bfEstimateBmSize(4300, 0.0001)) AS k; -- ~13

완전한 조정 안내는 파라미터 문서를 참고하세요.

단어 기반 검색용 Token Bloom filter (tokenbf_v1) (폐기됨)

전체 텍스트 검색을 위한 tokenbf_v1 인덱스 사용은 ClickHouse 버전 >= 26.2에서 text 인덱스(자세한 내용은 여기)를 선호해 폐기되었습니다.

tokenbf_v1 인덱스는 영숫자가 아닌 문자로 구분된 토큰을 인덱싱합니다. hasToken, LIKE 단어 패턴 또는 equals/IN과 함께 사용해야 합니다. String / FixedString / Map 타입을 지원합니다.

Token bloom filter와 Bloom filter types 페이지에서 자세한 내용을 참고하세요.

ALTER TABLE logs ADD INDEX msg_token lower(msg) TYPE tokenbf_v1(10000, 7, 7) GRANULARITY 1;
ALTER TABLE logs MATERIALIZE INDEX msg_token;

-- Word search (case-insensitive via lower)
SELECT count() FROM logs WHERE hasToken(lower(msg), 'exception');

EXPLAIN indexes = 1
SELECT count() FROM logs WHERE hasToken(lower(msg), 'exception');

token과 ngram에 대한 관측 가능성 예제와 안내는 여기를 보세요.

CREATE TABLE 중 인덱스 추가 (여러 예제)

스킵 인덱스는 복합 표현식과 Map / Tuple / Nested 타입도 지원합니다. 아래 예제에서 이를 보여 줍니다:

CREATE TABLE t
(
  u64 UInt64,
  s String,
  m Map(String, String),

  INDEX idx_bf u64 TYPE bloom_filter(0.01) GRANULARITY 3,
  INDEX idx_minmax u64 TYPE minmax GRANULARITY 1,
  INDEX idx_set u64 * length(s) TYPE set(1000) GRANULARITY 4,
  INDEX idx_ngram s TYPE ngrambf_v1(3, 10000, 3, 7) GRANULARITY 1,
  INDEX idx_token mapKeys(m) TYPE tokenbf_v1(10000, 7, 7) GRANULARITY 1
)
ENGINE = MergeTree
ORDER BY u64;

기존 데이터 구체화와 검증

MATERIALIZE로 기존 데이터 파트에 인덱스를 추가할 수 있고, 아래처럼 EXPLAIN 또는 트레이스 로그로 가지치기를 검사할 수 있습니다:

ALTER TABLE t MATERIALIZE INDEX idx_bf;

EXPLAIN indexes = 1
SELECT count() FROM t WHERE u64 IN (123, 456);

-- Optional: detailed pruning info
SET send_logs_level = 'trace';

이 작업 minmax 예제는 EXPLAIN 출력 구조와 가지치기 수를 보여 줍니다.

스킵 인덱스를 언제 사용하고 피할지

스킵 인덱스는 다음 경우에 사용하세요:

  • 필터 값이 데이터 블록 내에서 희소함
  • ORDER BY 컬럼과 강한 상관이 있거나, 데이터 수집 패턴이 비슷한 값을 함께 그룹핑함
  • 대규모 로그 데이터셋에서 텍스트 검색(ngrambf_v1/tokenbf_v1 타입)

스킵 인덱스는 다음 경우에 피하세요:

  • 대부분의 블록이 일치하는 값을 하나 이상 포함할 가능성이 높음(블록이 어차피 읽힘)
  • 데이터 순서와 상관 없는 고카디널리티 컬럼에서 필터링

중요 고려사항 값이 데이터 블록에 단 한 번만 나타나도 ClickHouse는 블록 전체를 읽어야 합니다. 현실적인 데이터셋으로 인덱스를 테스트하고, 실제 성능 측정에 기반해 입자(granularity)와 타입별 파라미터를 조정하세요.

일시적으로 인덱스 무시 또는 강제

테스트와 문제 해결 중 개별 쿼리에서 이름으로 특정 인덱스를 비활성화할 수 있습니다. 필요할 때 인덱스 사용을 강제하는 설정도 있습니다. ignore_data_skipping_indices를 참고하세요.

-- Ignore an index by name
SELECT * FROM logs
WHERE hasToken(lower(msg), 'exception')
SETTINGS ignore_data_skipping_indices = 'msg_token';

참고사항과 주의사항

  • 스킵 인덱스는 MergeTree 계열 테이블에서만 지원됩니다. 가지치기는 granule/블록 수준에서 발생합니다.
  • 블룸 필터 기반 인덱스는 확률적입니다(오탐은 추가 읽기를 일으키지만 유효한 데이터를 건너뛰지는 않음).
  • 블룸 필터와 다른 스킵 인덱스는 EXPLAIN과 트레이싱으로 검증해야 합니다. 가지치기와 인덱스 크기의 균형을 맞추기 위해 입자를 조정하세요.

관련 문서

더 알아보기 (Learn more)