- クエリのパフォーマンスを向上させる。特に
JOINと組み合わせて使用する場合 - インジェスト処理を遅くすることなく、取り込みデータをその場でエンリッチする
Dictionary を使用した JOIN の高速化
JOIN を高速化するために使用できます。具体的には、結合キーが基盤となるキー・バリュー ストレージのキー属性と一致している必要がある LEFT ANY 型 です。
この条件に該当する場合、ClickHouse は Dictionary を利用して Direct Join を実行できます。これは ClickHouse で最も高速な JOIN アルゴリズムで、右側のテーブルの基盤となるテーブルエンジンが低レイテンシのキー・バリュー リクエストをサポートしている場合に適用できます。ClickHouse には、これを実現する 3 つのテーブルエンジンがあります: Join (基本的には事前計算済みの hash table) 、EmbeddedRocksDB、および Dictionary です。ここでは Dictionary ベースのアプローチを説明しますが、仕組みはこれら 3 つのエンジンすべてで同じです。
Direct Join アルゴリズムでは、右側のテーブルが Dictionary を基盤としている必要があります。つまり、そのテーブルの JOIN 対象データが、低レイテンシのキー・バリュー データ構造として、あらかじめメモリ上に存在している必要があります。
例
posts テーブルと votes テーブルを使った JOIN を必要とします。
このクエリは高速ですが、高いパフォーマンスを得るには、JOINの右側には、より小さいデータセットを使用してください: このクエリは、PostIdによる絞り込みを外側のクエリとサブクエリの両方で行っているため、必要以上に冗長に見えるかもしれません。これは、クエリの応答時間を高速に保つためのパフォーマンス最適化です。最適なパフォーマンスを得るには、JOINの右側が常により小さい集合であり、できるだけ小さくなるようにしてください。JOIN のパフォーマンスを最適化するためのヒントや、利用可能なアルゴリズムを理解するには、このブログ記事シリーズを参照することをお勧めします。
JOIN を注意深く記述する必要があります。理想的には、まず投稿のうち “SQL” を含むものだけに絞り込み、そのうえで、その投稿の部分集合について UpVote と DownVote の件数を確認し、メトリクスを計算できるのが望ましいです。
Dictionary の適用
votes テーブルのサイズを確認します。
以下の例では、Dictionary の元データは ClickHouse テーブルです。これは Dictionary で最も一般的なデータソースですが、さまざまなソース がサポートされており、ファイル、HTTP、Postgres などのデータベースも利用できます。後で示すように、Dictionary は自動的に更新できるため、頻繁に変更される小規模なデータセットを direct joins で使える状態に保つのに最適です。この Dictionary では、ルックアップを行うための主キーが必要です。これは概念的にはトランザクションデータベースの主キーと同じで、一意である必要があります。上記のクエリでは、結合キー
PostId に対するルックアップが必要です。したがって、Dictionary には votes テーブルから PostId ごとの賛成票と反対票の合計を格納する必要があります。この Dictionary 用のデータを取得するクエリは次のとおりです。
セルフマネージドの OSS では、上記のコマンドをすべてのノードで実行する必要があります。ClickHouse Cloud では、Dictionary は自動的にすべてのノードへレプリケートされます。上記の処理は RAM 64GB の ClickHouse Cloud ノードで実行しており、読み込みには 36 秒かかりました。Dictionary が消費しているメモリを確認するには:
PostIdに対する賛成票と反対票は、シンプルなdictGet関数で取得できるようになりました。以下では、投稿11227902の票数を取得します。
controversial の値だけを保存するようにすれば、さらに最適化できます。
クエリ時エンリッチメント
インデックス時のエンリッチメント
Location は変わらないと仮定しましょう (実際には変わります) 。具体的には、users テーブルの Location カラムです。location ごとに posts テーブルを分析するクエリを実行したいとします。このテーブルには UserId が含まれています。
Dictionary は、users テーブルを基に、ユーザー ID から location へのマッピングを提供します。
posts テーブルでこの辞書をデータ挿入時に利用するには、スキーマを変更する必要があります。Id < 0のユーザーは除外します。これにより、HashedDictionary 型を使用できます。Id < 0のユーザーはシステムユーザーです。
Location は MATERIALIZED カラムとして宣言されています。これは、値を INSERT クエリの一部として指定でき、その場合でも常に計算されることを意味します。
ClickHouse は DEFAULT columns もサポートしています (この場合、値は挿入することも、指定されていない場合は計算させることもできます) 。
テーブルにデータを投入するには、通常どおり S3 からの INSERT INTO SELECT を使用できます:
Dictionary の高度なトピック
Dictionary の更新
MIN 600 MAX 900 の LIFETIME を指定しました。LIFETIME はDictionaryの更新間隔で、ここでの値を指定すると、600〜900 秒のランダムな間隔で定期的に再読み込みされます。このランダムな間隔は、多数のサーバーで更新する際に Dictionary ソースへの負荷を分散するために必要です。更新中もDictionaryの古いバージョンに対して引き続きクエリを実行できますが、クエリがブロックされるのは初期ロード時だけです。なお、(LIFETIME(0)) を設定するとDictionaryは更新されなくなります。
Dictionary は SYSTEM RELOAD DICTIONARY コマンドを使って強制的に再読み込みできます。
ClickHouse や Postgres などのデータベースソースでは、一定間隔で更新する代わりに、Dictionary が実際に変更された場合にのみ更新するクエリを設定できます (これを判定するのはそのクエリの応答です) 。詳しくは こちら を参照してください。
その他の Dictionary の種類
参考資料
- Dictionary のベストプラクティス — レイアウトの選択、Dictionary と JOIN の使い分け、監視
- Dictionary を使ってクエリを高速化する
- Dictionary の高度な設定