SELECT クエリの計算は一度だけで済み、以降の同じクエリの実行結果はキャッシュから直接返せます。
クエリの種類によっては、これにより ClickHouse server のレイテンシとリソース使用量を大幅に削減できます。
背景、設計、および制限事項
- トランザクション整合性のあるキャッシュでは、
SELECTクエリの結果が変化した場合、または変化する可能性がある場合に、データベースがキャッシュされたクエリ結果を無効化 (破棄) します。ClickHouse では、データを変更する操作には、table への insert/update/delete や、折りたたみ merge が含まれます。トランザクション整合性のあるキャッシュは、たとえば MySQL (クエリキャッシュ は v8.0 以降で削除) や Oracle のような OLTP データベースに特に適しています。 - トランザクション整合性のないキャッシュでは、すべてのキャッシュエントリに有効期間が設定され、期限切れになると失効する (たとえば 1 分) こと、さらにその期間中は基盤となるデータがほとんど変化しないことを前提として、クエリ結果に多少の不正確さが生じることを許容します。この方式は、全体として OLAP データベースにより適しています。トランザクション整合性のないキャッシュで十分な例として、複数のユーザーが同時に利用するレポートツールの時間別売上レポートを考えてみてください。通常、売上データの変化は十分に緩やかなため、データベースはレポートを 1 回だけ計算すればよく (最初の
SELECTクエリがこれに相当します) 、以降のクエリはクエリキャッシュから直接返せます。この例では、妥当な有効期間は 30 分です。
設定と使用方法
clickhouse-local は一度に 1 つのクエリしか実行できません。クエリ結果をキャッシュしても意味がないため、clickhouse-local ではクエリ
結果キャッシュは無効になっています。
use_query_cache = true も指定している場合) 、計算済みの結果をキャッシュから読み取り、即座に返します。
use_query_cache の設定およびそのほかのすべてのクエリキャッシュ関連設定は、単独の SELECT ステートメントに対してのみ有効です。特に、
CREATE VIEW AS SELECT [...] SETTINGS use_query_cache = true によって作成されたビューに対する SELECT の結果は、その SELECT
ステートメントを SETTINGS use_query_cache = true 付きで実行しない限り、キャッシュされません。true) を使って、より詳細に構成できます。前者の設定は
クエリ結果をキャッシュに保存するかどうかを制御し、後者の設定は、データベースがクエリ
結果をキャッシュから取得しようとするかどうかを決定します。たとえば、次のクエリはキャッシュを受動的にのみ使用します。つまり、キャッシュから読み取ろうとはしますが、その
結果は保存しません。
use_query_cache、enable_writes_to_query_cache、および
enable_reads_from_query_cache は特定のクエリに対してのみ指定することを推奨します。ユーザーまたはプロファイルレベルでキャッシュを有効にすることも可能です (例: SET use_query_cache = true) が、その場合はすべての SELECT クエリがキャッシュされた結果を返す可能性がある点に留意してください。
クエリキャッシュは、ステートメント SYSTEM CLEAR QUERY CACHE を使用してクリアできます。クエリキャッシュの内容は、システムテーブル
system.query_cache に表示されます。データベースの起動以降のクエリキャッシュのヒット数とミス数は、システムテーブル
system.events のイベント
“QueryCacheHits” および “QueryCacheMisses” として確認できます。どちらのカウンターも、
SELECT クエリが設定 use_query_cache = true で実行された場合にのみ更新され、その他のクエリは “QueryCacheMisses” に影響しません。フィールド query_cache_usage
は、システムテーブル system.query_log にあり、実行された各クエリについて、そのクエリ結果がクエリキャッシュに書き込まれたか、または
クエリキャッシュから読み取られたかを示します。システムテーブル
system.metrics のメトリクス QueryCacheEntries および QueryCacheBytes は、クエリキャッシュに現在含まれているエントリ数 / バイト数を示します。
クエリキャッシュは ClickHouseサーバープロセスごとに 1 つ存在します。ただし、キャッシュ結果はデフォルトではユーザー間で共有されません。これは
変更できます (下記を参照) が、セキュリティ上の理由から推奨されません。
クエリ結果は、クエリキャッシュ内で、そのクエリの Abstract Syntax Tree (AST) によって
参照されます。これは、キャッシュが大文字/小文字を区別しないことを意味し、たとえば SELECT 1 と select 1 は同じクエリとして扱われます。より
自然な照合を行うために、クエリキャッシュに関連するすべてのクエリレベル設定と 出力フォーマット)
は AST から削除されます。
クエリが例外またはユーザーによるキャンセルによって中断された場合、そのエントリはクエリキャッシュに書き込まれません。
クエリキャッシュのサイズ (バイト単位) 、cache エントリの最大数、および個々の cache エントリの最大サイズ (バイト単位および
レコード単位) は、さまざまな サーバー設定オプション を使用して構成できます。
users.xml のユーザープロファイルに
query_cache_max_size_in_bytes と
query_cache_max_entries の設定を追加し、その後、両方の設定を
読み取り専用にします。
Age および Expires ヘッダーに、キャッシュされたエントリの経過時間 (秒単位) と有効期限のタイムスタンプを設定します。
クエリキャッシュ内のエントリは、デフォルトで圧縮されています。これにより、クエリキャッシュへの書き込み / 読み取りは遅くなるものの、全体的なメモリ消費量は削減されます
。圧縮を無効にするには、設定 query_cache_compress_entries を使用します。
同じクエリに対して複数の結果をキャッシュしておくことが有用な場合があります。これは、
クエリキャッシュのエントリに対するラベル (またはネームスペース) として機能する設定 query_cache_tag を使うことで実現できます。クエリキャッシュは、
同じクエリであってもタグが異なる結果は別のものとして扱います。
同じクエリに対して 3 つの異なるクエリキャッシュエントリを作成する例:
tag の付いたエントリのみを削除するには、ステートメント SYSTEM CLEAR QUERY CACHE TAG 'tag' を使用します。
ClickHouse は、テーブルデータを max_block_size 行ごとのブロックで読み取ります。フィルタリングや集約、
その他の処理の影響により、結果ブロックは通常 ‘max_block_size’ よりかなり小さくなりますが、これよりずっと大きくなる場合もあります。設定
query_cache_squash_partial_results (デフォルトで有効) は、結果ブロックを
クエリ結果キャッシュに挿入する前に、 (非常に小さい場合は) まとめるか、 (大きい場合は) ‘max_block_size’ サイズのブロックに分割するかを制御します。
これによりクエリキャッシュへの書き込み性能は低下しますが、キャッシュエントリの圧縮率が向上し、後でクエリ結果がクエリキャッシュから返される際の
ブロック粒度がより自然になります。
その結果、クエリキャッシュには各クエリごとに複数の (部分的な)
結果ブロックが保存されます。この挙動はデフォルトとしては適切ですが、設定
query_cache_squash_partial_results を使用して無効にできます。
また、非決定論的関数を含むクエリの結果は、デフォルトではキャッシュされません。こうした関数には次のものが含まれます。
- Dictionaries にアクセスする関数:
dictGet()など - XML の
定義内にタグ
<deterministic>true</deterministic>がない user-defined functions - 現在の日付または時刻を返す関数:
now(),today(),yesterday()など - ランダムな値を返す関数:
randomString(),fuzzBits()など - クエリ処理で使われる内部 chunk のサイズや順序に結果が依存する関数:
nowInBlock()など,rowNumberInBlock(),runningDifference(),blockSize()など - 環境に依存する関数:
currentUser(),queryID(),getMacro()など