つい先日、"SnowflakeのHands On Essentials - Data Warehouse"を獲得したDBManiaです。
相変わらずSnowflakeのDWHとしての性能、機能に惚れ込んで、使い倒す日々を送っております。
クエリー結果キャッシュとは
さて、Snowflakeのパフォーマンスを上げるための大きなポイントにクエリー結果キャッシュを使用する、というのがあります。
クエリー結果キャッシュというのは、過去に実行されたSQLをある程度の時間(基本は24時間)、Snowflakeのクラウドサービスレイヤー上に保管しておく仕組みで、次に同じSQLが要求されたときにはその下のクエリープロセッシングレイヤーやデータベースストレージレイヤーを使わずに直接結果を返します。
図:Snowflake DOCUMENTATION 重要な概念およびアーキテクチャより引用
そのため、クエリー結果キャッシュが効いていると、処理はクエリープロセッシングレイヤーにあるWAREHOUSEを使わずにクラウドサービスレイヤーだけで処理されるため、圧倒的なパフォーマンスが得られるだけでなく、WAREHOUSEコストなしで実行でき、お財布にも優しくなります。
クエリー結果キャッシュが使用されたかは、Snowsightのアクティビティからクエリー履歴を見ればわかります。
クエリー結果キャッシュが働いた場合、ウェアハウスの列(画像の右端のところ)が「-」で表示されます。
ここまでは、Snowflakeをお使いの皆様であれば常識だと思います。
しかし、クエリー結果キャッシュが効く条件が想定しているより厳しいことをご存じでしょうか。
こちらの中に「新しいクエリは、以前に実行したクエリと構文的に一致する。」という文章があります。
この「構文的に一致」とは、何を示しているのでしょうか。
クエリー結果キャッシュの評価
実際の評価に使用したのはSnowflakeのHandsOnで使用したsnowflake_sample_dataを以下のようにview化したものです。
CREATE VIEW garden_plants.flowers.sqr_test AS SELECT c_nationkey, c_custkey FROM snowflake_sample_data.tpch_sf1000.customer;
これを用いて以下のSQLをベースに、クエリー結果キャッシュが効くか確認してみました。
select * from sqr_test where c_custkey = 67686865 and c_nationkey=17;
なお、SQL実行にはSnowsightのワークシートを使用しています。また、違いがわかりやすいように、コードではなく引用タグを使用しています。ご承知おきください。
まったく同じ
select * from sqr_test where c_custkey = 67686865 and c_nationkey=17;
↓
select * from sqr_test where c_custkey = 67686865 and c_nationkey=17;
当然クエリー結果キャッシュは効きました。
*をカラムに展開する
select * from sqr_test where c_custkey = 67686865 and c_nationkey=17;
↓
select c_custkey,c_nationkey from sqr_test where c_custkey = 67686865 and c_nationkey=17;
カラムを展開するとクエリー結果キャッシュが効きませんでした。
条件の左右を入れ替える
select * from sqr_test where c_custkey = 67686865 and c_nationkey=17;
↓
select * from sqr_test where c_nationkey=17 and c_custkey = 67686865;
同一の条件であっても、whereで問う順番を変えたところクエリー結果キャッシュが効きませんでした。
テーブル名にスキーマ修飾する
select * from sqr_test where c_custkey = 67686865 and c_nationkey=17;
↓
select * from flowers.sqr_test where c_custkey = 67686865 and c_nationkey=17;
スキーマ修飾を追加したところ、クエリー結果キャッシュが効きませんでした。
中間考察
これらの結果からわかることは、クエリー結果キャッシュはオプティマイザー的に等価であっても効かない、ということです。
こうなると、どこまで一致している必要があるのか気になりますよね。
大文字小文字を変える
select * from sqr_test where c_custkey = 67686865 and c_nationkey=17;
↓
select * from SQR_TEST where c_custkey = 67686865 and c_nationkey=17;
SELECT * from sqr_test where c_custkey = 67686865 and c_nationkey=17;
どちらもクエリー結果キャッシュが効きませんでした。
;を外す
select * from sqr_test where c_custkey = 67686865 and c_nationkey=17;
↓
select * from sqr_test where c_custkey = 67686865 and c_nationkey=17
さすがにこれは効くでしょうと思ったのですが、クエリー結果キャッシュが効きませんでした。
;のありなしですらキャッシュが効かなくなるんですね。
コメントを入れる
select * from sqr_test where c_custkey = 67686865 and c_nationkey=17;
↓
select * from sqr_test /* test */ where c_custkey = 67686865 and c_nationkey=17;
クエリー結果キャッシュは効きました。
区切り文字(空白、改行、タブなど)を変える
select * from sqr_test where c_custkey = 67686865 and c_nationkey=17;
↓
select *
from sqr_test
where c_custkey = 67686865
and c_nationkey=17
クエリー結果キャッシュは効きました。
結論
クエリー結果キャッシュで「構文的に一致する」とは、空白、改行、タブ、コメントを除いて「大文字小文字はもちろん末尾の";"の有無まで含めて、完全に一致する文字列」のことでした。
BIツールなどで自動生成されたSQLであれば、同じSQLなのに文字列の違いでクエリー結果キャッシュが効かないということは発生しないでしょうが、バッチ処理や手動で生成したSQLの場合は、大文字小文字レベルの違いでクエリー結果キャッシュが効かない、ということが発生するでしょう。(むしろ発生しないほうが凄いです。 )
これを防ぐためには、かなり厳密なコーディング規約を定めて、統一的なSQLを書く必要がありそうです。