このページでは、ClickHouse のデータスキッピングインデックスの例をまとめており、各タイプの定義方法、使用する場面、実際に適用されているかを確認する方法を紹介します。これらの機能はすべて、MergeTree ファミリーのテーブル で利用できます。
索引の構文:
INDEX name expr TYPE type(...) [GRANULARITY N]ClickHouse は 6 種類のスキップ索引をサポートしています。
| Index Type | Description |
|---|---|
| minmax | 各 granule の最小値と最大値を追跡します |
| set(N) | 各 granule ごとに最大 N 個の異なる値を格納します |
| text | 全文検索のための、トークン化された文字列データに対する転置索引 |
| bloom_filter([false_positive_rate]) | 存在確認のための確率的フィルター |
| ngrambf_v1 | 部分文字列検索のための N-gram Bloom フィルター |
| tokenbf_v1 | 全文検索のためのトークンベースの Bloom フィルター |
各セクションでは、サンプルデータを使った例を示し、クエリ実行時に索引が使用されていることを確認する方法を説明します。
MinMax索引
minmax索引は、大まかにソートされたデータや、ORDER BYと相関のあるカラムに対する範囲条件に最適です。
-- 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;
-- または後から追加してマテリアライズする
ALTER TABLE events ADD INDEX ts_minmax ts TYPE minmax GRANULARITY 1;
ALTER TABLE events MATERIALIZE INDEX ts_minmax;
-- 索引を活用するクエリ
SELECT count() FROM events WHERE ts >= now() - 3600;
-- 使用状況を確認する
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 は、トークン化されたテキストデータに対する転置索引です。
全文検索ワークロード向けに設計されており、トークンや検索語の効率的かつ決定論的なルックアップを可能にします。
自然言語検索や大規模なテキスト検索のユースケースに推奨されます。
詳細と使用例については、テキスト索引を使った全文検索をご覧ください。
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');より詳細なオブザーバビリティの例については、こちらのドキュメントを参照してください。
テキスト索引は完全に決定論的で、トークン化やテキスト処理を全面的に調整できますが、bloom filter ベースの索引と比べると、その分ストレージ消費は増えます。
汎用 Bloom filter (スカラー)
bloom_filter 索引は、"干し草の山から針を探す"ような等価比較や IN による存在確認に適しています。オプションのパラメータとして、偽陽性率 (デフォルトは 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 索引は文字列を N-gram に分割します。LIKE '%...%' クエリに適しており、String/FixedString/Map (mapKeys/mapValues 経由) をサポートするほか、サイズ、hash の数、seed を調整できます。詳細は N-gram bloom filter のドキュメントを参照してください。
-- 部分文字列検索用の索引を作成する
ALTER TABLE logs ADD INDEX msg_ngram msg TYPE ngrambf_v1(3, 10000, 3, 7) GRANULARITY 1;
ALTER TABLE logs MATERIALIZE INDEX msg_ngram;
-- 部分文字列検索
SELECT count() FROM logs WHERE msg LIKE '%timeout%';
EXPLAIN indexes = 1
SELECT count() FROM logs WHERE msg LIKE '%timeout%';このガイドでは、token と ngram の実践的な例と、それぞれをどのような場合に使うべきかを紹介します。
パラメータ最適化用ヘルパー関数:
ngrambf_v1 の 4 つのパラメータ (n-gram サイズ、bitmap サイズ、ハッシュ関数、seed) は、パフォーマンスとメモリ使用量に大きく影響します。想定される n-gram 数と目標の偽陽性率に基づいて最適な bitmap サイズとハッシュ関数の数を計算するには、以下の関数を使用してください。
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))));
-- 4300 N-gramのサイズ計算例、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 は、英数字以外の文字で区切られたトークンに対して索引を作成します。hasToken、LIKE の単語パターン、または等価比較/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;
-- 単語検索(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);
-- オプション: 詳細なプルーニング情報
SET send_logs_level = 'trace';このminmax の実例は、EXPLAIN の出力構造とプルーニング件数を示しています。
スキップ索引を使うべき場合と避けるべき場合
スキップ索引を使うべき場合:
- フィルタ対象の値がデータブロック内でスパースである
ORDER BYカラムと強い相関がある、またはデータの取り込みパターンによって似た値がまとまっている- 大規模なログデータセットに対してテキスト検索を行う (
ngrambf_v1/tokenbf_v1型)
スキップ索引を避けるべき場合:
- ほとんどのブロックに少なくとも 1 つは一致する値が含まれている可能性が高い (いずれにしてもブロックが読み込まれる)
- データの並び順と相関のない、高カーディナリティのカラムでフィルタリングする
一時的に索引を無視または強制する
テストやトラブルシューティングの際には、特定の索引を名前で指定して、クエリごとに無効化できます。必要に応じて索引の使用を強制するための設定も用意されています。詳細は 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/block レベルで行われます。
- ブルームフィルタベースの索引は確率的です (偽陽性により余分な読み取りが発生することはありますが、有効なデータがスキップされることはありません) 。
- ブルームフィルタやその他のスキップ索引は
EXPLAINとトレーシングで検証し、プルーニング効果と索引サイズのバランスが取れるように granularity を調整してください。