メインコンテンツへスキップ
ストレージを最適化したら、次はクエリパフォーマンスの改善です。 このセクションでは、ORDER BY キーの最適化と materialized view の活用という 2 つの主要な手法を取り上げます。 これらのアプローチによって、クエリ時間を数秒からミリ秒へ短縮できることを見ていきます。

ORDER BY キーを最適化する

ほかの最適化を試す前に、ClickHouse で可能な限り高速な結果を得られるよう、ORDER BY キーを最適化しておく必要があります。 適切なキーの選択は、主に実行するクエリによって決まります。たとえば、クエリの大半が project カラムと subproject カラムで絞り込まれるとします。 この場合、それらを ORDER BY キーに追加するのが適切です。さらに、時間でもクエリするため、time カラムも追加します。 それでは、wikistat と同じカラム型を持ち、(project, subproject, time) で並べ替えられた別バージョンのテーブルを作成してみましょう。
では、複数のクエリを比較して、ソートキー式がパフォーマンスにどれほど重要かを確認してみましょう。なお、前述のデータ型や codec の最適化はまだ適用していないため、各クエリのパフォーマンス差はソート順の違いのみによるものです。
クエリ(time)(project, subproject, time)
2.381 秒1.660 秒
2.148 秒0.058 秒
2.192 秒0.012 秒
2.968 秒0.010 秒

Materialized views

もう 1 つの方法は、materialized view を使って、よく実行されるクエリの結果を集計して保存することです。これらの結果は、元のテーブルではなくこちらを対象にクエリできます。ここでは、次のクエリが頻繁に実行されるケースを考えます。

materialized view を作成する

次の materialized view を作成します。

宛先テーブルのバックフィル

この宛先テーブルが更新されるのは、wikistat テーブルに新しいレコードが挿入されたときだけです。そのため、バックフィルを行う必要があります。 これを行う最も簡単な方法は、ビューの SELECT クエリ (変換) を使ってINSERT INTO SELECT ステートメントで materialized view のターゲットテーブルに直接挿入することです。
生データセットのカーディナリティによっては (ここでは10億行あります!) 、これはメモリを大量に消費するアプローチになる可能性があります。代わりに、必要なメモリを最小限に抑えられる方法を使うこともできます。
  • Null table engine を使って一時テーブルを作成する
  • 通常使用している materialized view のコピーをその一時テーブルに接続する
  • INSERT INTO SELECT クエリを使って、生データセット内のすべてのデータをその一時テーブルにコピーする
  • 一時テーブルと一時 materialized view を削除する
このアプローチでは、生データセットの行がブロック単位で一時テーブルにコピーされます (このテーブルにそれらの行は保存されません) 。そして、各ブロックの行ごとに部分的な state が計算されてターゲットテーブルに書き込まれ、それらの state はバックグラウンドで段階的にマージされます。
次に、wikistat_backfill から読み取り、wikistat_top に書き込む materialized view を作成します
そして最後に、元の wikistat テーブルから wikistat_backfill にデータを投入します:
そのクエリの実行が完了したら、バックフィル用のテーブルとmaterialized viewを削除できます:
これで、元のテーブルではなく materialized view に対してクエリを実行できます:
ここでの性能向上は劇的です。 以前はこのクエリの答えを計算するのに 2 秒強かかっていましたが、今ではわずか 4 ミリ秒です。
最終更新日 2026年6月19日