[go: up one dir, main page]

メインコンテンツまでスキップ
メインコンテンツまでスキップ

主キーインデックス

高度なインデックスの詳細を探していますか?

このページでは、ClickHouseのスパース主インデックスの構築方法、動作原理、クエリの高速化にどのように役立つかを紹介します。

高度なインデックス戦略とさらに深い技術的詳細については、主インデックスの深掘りをご覧ください。

ClickHouseにおけるスパース主インデックスはどのように機能するのか?


ClickHouseのスパース主インデックスは、クエリの条件に一致するデータを含む可能性のあるグラニュール—行のブロック—を効率的に特定するのに役立ちます。次のセクションでは、このインデックスがそれらのカラムの値からどのように構築されるかを説明します。

スパース主インデックスの作成

スパース主インデックスがどのように構築されるかを示すために、uk_price_paid_simpleテーブルをいくつかのアニメーションとともに使用します。

リマインダーとして、①例示のテーブルには(^^主キー^^(town, street)があり、②挿入されたデータは③ディスクに保存され、^^主キー^^カラムの値に基づいてソートされ、圧縮された状態で、それぞれのカラム用の別々のファイルに格納されます:



処理のために、各カラムのデータは④論理的にグラニュールに分割されます—それぞれが8,192行をカバーします—これはClickHouseのデータ処理メカニズムが操作する最小単位です。

この^^グラニュール^^構造は主インデックスをスパースにする要因でもあります:すべての行をインデックス化するのではなく、ClickHouseは⑤各^^グラニュール^^から1行分の^^主キー^^値、具体的には最初の行の値だけを保存します。これにより、各^^グラニュール^^ごとに1つのインデックスエントリが生成されます:



スパース性のおかげで、主インデックスはメモリに完全に収まるのに十分小さく、^^主キー^^カラムに対する条件を持つクエリのフィルタリングを迅速に行うことを可能にします。次のセクションでは、これがどのようにそのようなクエリの加速に役立つかを示します。

主インデックスの使用

スパース主インデックスがクエリ加速にどのように使用されるか、別のアニメーションを用いて概説します:



①例示のクエリには、両方の^^主キー^^カラムに対する述語が含まれています: town = 'LONDON' AND street = 'OXFORD STREET'

②クエリを加速するために、ClickHouseはテーブルの主インデックスをメモリにロードします。

③その後、インデックスエントリをスキャンして、述語に一致する行を含む可能性のあるグラニュールを特定します—言い換えれば、スキップできないグラニュールです。

④これらの潜在的に関連するグラニュールがメモリにロードされ、クエリに必要な他のカラムの対応するグラニュールとともに処理されます。

主インデックスの監視

テーブル内の各data partには、それぞれの主インデックスがあります。これらのインデックスの内容をmergeTreeIndexテーブル関数を使用して調査できます。

以下のクエリでは、例示のテーブルの各データ^^part^^の主インデックスにおけるエントリの数を一覧表示します:

SELECT
    part_name,
    max(mark_number) AS entries
FROM mergeTreeIndex('uk', 'uk_price_paid_simple')
GROUP BY part_name;
   ┌─part_name─┬─entries─┐
1. │ all_2_2_0 │     914 │
2. │ all_1_1_0 │    1343 │
3. │ all_0_0_0 │    1349 │
   └───────────┴─────────┘

このクエリは、現在のデータ^^parts^^のうち1つの主インデックスからの最初の10エントリを示しています。これらの^^parts^^は、バックグラウンドで継続的にマージされて、より大きな^^parts^^となります:

SELECT 
    mark_number + 1 AS entry,
    town,
    street
FROM mergeTreeIndex('uk', 'uk_price_paid_simple')
WHERE part_name = (SELECT any(part_name) FROM mergeTreeIndex('uk', 'uk_price_paid_simple')) 
ORDER BY mark_number ASC
LIMIT 10;
    ┌─entry─┬─town───────────┬─street───────────┐
 1. │     1 │ ABBOTS LANGLEY │ ABBEY DRIVE      │
 2. │     2 │ ABERDARE       │ RICHARDS TERRACE │
 3. │     3 │ ABERGELE       │ PEN Y CAE        │
 4. │     4 │ ABINGDON       │ CHAMBRAI CLOSE   │
 5. │     5 │ ABINGDON       │ THORNLEY CLOSE   │
 6. │     6 │ ACCRINGTON     │ MAY HILL CLOSE   │
 7. │     7 │ ADDLESTONE     │ HARE HILL        │
 8. │     8 │ ALDEBURGH      │ LINDEN ROAD      │
 9. │     9 │ ALDERSHOT      │ HIGH STREET      │
10. │    10 │ ALFRETON       │ ALMA STREET      │
    └───────┴────────────────┴──────────────────┘

最後に、EXPLAIN句を使用して、すべてのデータ^^parts^^の主インデックスが、例示のクエリの述語に一致する行を含むことができないグラニュールをスキップする方法を確認します。これらのグラニュールは、ロードおよび処理から除外されます:

EXPLAIN indexes = 1
SELECT
    max(price)
FROM
    uk.uk_price_paid_simple
WHERE
    town = 'LONDON' AND street = 'OXFORD STREET';
    ┌─explain────────────────────────────────────────────────────────────────────────────────────────────────────┐
 1. │ Expression ((Project names + Projection))                                                                  │
 2. │   Aggregating                                                                                              │
 3. │     Expression (Before GROUP BY)                                                                           │
 4. │       Expression                                                                                           │
 5. │         ReadFromMergeTree (uk.uk_price_paid_simple)                                                        │
 6. │         Indexes:                                                                                           │
 7. │           PrimaryKey                                                                                       │
 8. │             Keys:                                                                                          │
 9. │               town                                                                                         │
10. │               street                                                                                       │
11. │             Condition: and((street in ['OXFORD STREET', 'OXFORD STREET']), (town in ['LONDON', 'LONDON'])) │
12. │             Parts: 3/3                                                                                     │
13. │             Granules: 3/3609                                                                               │
    └────────────────────────────────────────────────────────────────────────────────────────────────────────────┘

EXPLAIN出力の行13には、すべてのデータ^^parts^^の中で、主インデックス分析によって処理のために選択されたグラニュールが3,609のうちのわずか3つであることが示されています。残りのグラニュールは完全にスキップされました。

また、クエリを実行するだけで、ほとんどのデータがスキップされたことも観察できます:

SELECT max(price)
FROM uk.uk_price_paid_simple
WHERE (town = 'LONDON') AND (street = 'OXFORD STREET');
   ┌─max(price)─┐
1. │  263100000 │ -- 263.10 million
   └────────────┘

1 row in set. Elapsed: 0.010 sec. Processed 24.58 thousand rows, 159.04 KB (2.53 million rows/s., 16.35 MB/s.)
Peak memory usage: 13.00 MiB.

上記のように、例示のテーブル内の約3000万行のうち、約25,000行しか処理されませんでした:

SELECT count() FROM uk.uk_price_paid_simple;
   ┌──count()─┐
1. │ 29556244 │ -- 29.56 million
   └──────────┘

主なポイント

  • スパース主インデックスは、ClickHouseが^^主キー^^カラムのクエリ条件に一致する行を含むグラニュールを特定することで、不必要なデータをスキップするのに役立ちます。

  • 各インデックスは、各^^グラニュール^^の最初の行からの^^主キー^^値のみを保存しており(デフォルトでは^^グラニュール^^は8,192行です)、メモリに収まるコンパクトな構造となっています。

  • ^^MergeTree^^テーブル内の各データ部分は、独立してクエリ実行時に使用される独自の主インデックスを持っています。

  • クエリ中、インデックスによりClickHouseはグラニュールをスキップすることができ、I/Oやメモリ使用量を削減しながらパフォーマンスを向上させます。

  • mergeTreeIndexテーブル関数を使用してインデックスの内容を調査し、EXPLAIN句でインデックスの使用状況を監視できます。

さらなる情報の入手先

ClickHouseにおけるスパース主インデックスの機能の詳細な説明や、従来のデータベースインデックスとの差異、使用に関するベストプラクティスについては、詳細なインデクシングの深掘りをご覧ください。

ClickHouseが主インデックススキャンで選択したデータをどのように高い並列性を持って処理するかに興味がある方は、クエリの並列性ガイドをこちらでご覧ください。