はじめに
基本的な動作
- 索引名。索引名は、各パーティションに索引ファイルを作成するために使われます。また、索引を削除またはマテリアライズする際のパラメータとしても必要です。
- 索引式。索引式は、索引に格納される値の集合を計算するために使われます。これは、カラム、単純な演算子、または索引タイプによって決まる関数のサブセットを組み合わせたものにできます。
- TYPE。索引のタイプは、各索引ブロックについて、読み取りと評価をスキップできるかどうかを判断する計算を制御します。
- GRANULARITY。各索引付きブロックは、GRANULARITY 個のグラニュールで構成されます。たとえば、テーブルのプライマリ索引の granularity が 8192 行で、索引の granularity が 4 の場合、各索引付き “block” は 32768 行になります。
skp_idx_{index_name}.idx。順序付けられた式の値が格納されますskp_idx_{index_name}.mrk2。関連するデータカラムファイル内の対応するオフセットが格納されます。
my_value
カラム内の 1 億件のエントリすべてがスキャンされます:
my_value が 125 の4096行がどのように読み取られて選択され、その後の行が
ディスクから読み取られることなくどのようにスキップされたかは次のとおりです。
クエリ実行時にトレースを有効にすると、スキップ索引の使用状況に関する詳細情報を確認できます。clickhouse-client
では、send_logs_level を設定します:
スキップ索引の種類
minmax
set
テキスト
hasAnyToken や hasAllTokens などの検索関数に加え、一般的なテキスト検索関数全般も最適化できるため、全文検索クエリにはこれを使用することを推奨します。
詳しくは、テキスト索引のドキュメントをこちらで参照してください。
ブルームフィルタの型
- 基本的な bloom_filter。0 から 1 の範囲で許容される「偽陽性率」を表す、省略可能な 1 つのパラメータを取ります (未指定の場合は .025 が使用されます) 。
-
特化型の tokenbf_v1 (非推奨)。これは 3 つのパラメータを取り、いずれも使用するブルームフィルタの調整に関係します。(1) フィルタのサイズ (バイト単位。大きいほど偽陽性は減りますが、その分ストレージコストが増えます) 、(2) 適用する hash function の数 (これも多いほど偽陽性を減らせます) 、(3) ブルームフィルタの hash function 用の seed です。これらのパラメータがブルームフィルタの動作にどのように影響するかの詳細は、こちら の計算機を参照してください。
この索引は String、FixedString、Map データ型でのみ動作します。入力式は、英数字以外の文字で区切られた文字列に分割されます。たとえば、カラム値が
This is a candidate for a "full text" searchの場合、Thisisacandidateforfulltextsearchという token を含みます。これは、LIKE、EQUALS、IN、hasToken() など、長い文字列内の単語やその他の値を検索する用途を想定しています。たとえば、自由形式のアプリケーションログ行が入ったカラムから、少数のクラス名や行番号を検索するような用途が考えられます。 -
特化型の ngrambf_v1 (非推奨)。この索引は token 索引と同様に機能します。Bloom filter の設定の前に、追加で 1 つのパラメータ、つまり索引付けする ngram のサイズを取ります。ngram とは、任意の文字からなる長さ
nの文字列です。したがって、A short stringという文字列は、ngram サイズを 4 にすると次のように索引付けされます。
全文検索の workload では、非推奨の tokenbf_v1 または ngrambf_v1 索引ではなく、専用の テキスト索引 (Text index for full-text search を参照) の使用を推奨します。 テキスト索引は真の転置索引を提供し、token ベースのブルームフィルタ索引と比べて、検索性能が高く、動作がより予測しやすく、柔軟性にも優れています。
スキップ索引と関数
- データが挿入され、索引が関数式として定義されている場合 (式の結果は索引ファイルに格納されます) 、または
- クエリの処理時に、その式が格納済みの索引値に適用され、ブロックを除外するかどうかが判定される場合。
スキップ索引の設定
- use_skip_indexes (0 または 1。デフォルトは 1)。 すべてのクエリがスキップ索引を効率的に利用できるわけではありません。 特定のフィルタ条件で ほとんどのグラニュールが対象になる可能性が高い場合、データスキッピングインデックスを適用すると不要な、場合によっては大きな、コストが発生します。 どのスキップ索引を使っても 効果が見込めないクエリでは、この値を 0 に設定してください。
- force_data_skipping_indices (索引名のカンマ区切りリスト)。 この設定は、ある種の非効率な クエリを防ぐために使用できます。 スキップ索引を使用しないとテーブルへのクエリのコストが高くなりすぎる場合、この設定に 1 つ以上の索引名を指定すると、 指定した索引を使用しないクエリでは例外が返されます。 これにより、不適切に書かれたクエリが サーバーリソースを消費するのを防げます。
スキップ索引のベストプラクティス
timestamp で、visitor_id に索引があるとします。次のクエリを考えてみましょう。
visitor_id カラム内の 32768 個の値すべてが検査されます。
そのため、ClickHouse クエリを高速化しようとして、キーとなる
カラムに単純に索引を追加すればよいと考えるのは、多くの場合誤りです。この高度な機能は、主キーの変更 (How to Pick a Primary Key を参照) 、projections の使用、または materialized view の使用といった他の選択肢を検討したうえでのみ使うべきです。データスキッピングインデックスが適切な場合であっても、索引とテーブルの
両方を慎重にチューニングする必要があることが少なくありません。
ほとんどの場合、有用なスキップ索引には、主キーと対象となる非主キーカラム/式の間に強い相関が必要です。
相関がない場合 (上の図のように) は、数千個の値から成る
ブロック内で少なくとも 1 行がフィルタ条件を満たす可能性が高く、スキップできるブロックはほとんどありません。これに対して、主キーの値の範囲 (たとえば時刻) が
候補となる索引カラムの値 (たとえばテレビ視聴者の年齢) と強く結び付いている場合は、minmax 型の索引
が有効である可能性が高くなります。なお、データ挿入時にこの相関を高められる場合があります。たとえば、
ソート/ORDER BY キーに追加のカラムを含める、あるいは主キーに関連する値が INSERT 時にまとまるように insert を batch 化する方法です。たとえば、
主キーが多数のサイトのイベントを含む timestamp であっても、特定の site_id のイベントをすべて
取り込みプロセスでまとめてグループ化し、一緒に挿入できます。これにより、少数の site id しか含まないグラニュールが多数できるため、
特定の site_id 値で検索する際に多くのブロックをスキップできるようになります。
スキップ索引のもう 1 つの有力な候補は、個々の値がデータ内では比較的スパースな高カーディナリティの式です。たとえば、
API リクエスト内の error code を追跡するオブザーバビリティプラットフォームが考えられます。特定の error code は、データ内ではまれであっても、検索において
特に重要である場合があります。error_code カラムに対する set スキップ索引を使えば、error を含まない
大多数のブロックを読み飛ばせるため、error に焦点を当てたクエリを大幅に改善できます。
最後に、最も重要なベストプラクティスは、とにかく何度もテストすることです。繰り返しになりますが、ドキュメント検索用の b-tree 二次索引や転置索引とは異なり、
データスキッピングインデックスの挙動は簡単には予測できません。これらを table に追加すると、データの取り込み時にも、またさまざまな理由で索引の恩恵を受けないクエリに対しても、
無視できないコストが発生します。必ず実際のデータに近いデータでテストすべきであり、テストには型、granularity サイズ、その他の parameter の違いも
含める必要があります。テストによって、机上の検討だけでは
見えてこないパターンや落とし穴が明らかになることはよくあります。