最適化は速度の問題ではない。コストの問題だ
クラウドDWHには、オンプレミス時代と決定的に異なる性質がある。 すべてのクエリに値札がついている という事実だ。
Snowflakeのコンピュートコストは、クエリが消費するウェアハウスの稼働時間で決まる。つまり、不要なデータを読めば読むほど、課金額が上がる。「動けばいい」で書いたSQLが、月末の請求書に直結する。
この構造を理解すると、Snowflakeが地味に進化させている機能群の意味が見えてくる。SEARCH関数、HyperLogLog、LIMIT Pruning。どれもプレスリリースの見出しにはならないが、 実務の請求額を変える機能 だ。
よくある失敗:コストを意識しないクエリ
現場でよく見る3つのパターンがある。
1. 数十億行に対する COUNT(DISTINCT)
-- 全データを読む。正確だが、コストも正確に高い
SELECT COUNT(DISTINCT user_id) FROM events;
ユニークユーザー数を出すだけで、数十億行のフルスキャンが走る。ダッシュボードの更新が毎時回っていれば、月間のコストは無視できない金額になる。
2. LIKE ‘%keyword%’ の安易な使用
-- インデックスが効かない。全行スキャン確定
SELECT * FROM logs WHERE message LIKE '%timeout%';
先頭ワイルドカードを使った瞬間、Snowflakeのプルーニングは無力化される。数百GBのログテーブルを毎回フルスキャンする羽目になる。
3. クエリプロファイルを見ない
「結果が返ってくるから問題ない」。この判断が最も高くつく。Snowflakeのクエリプロファイルには、スキャンしたバイト数、パーティションのプルーニング率、スピルの有無が記録されている。 見ないということは、請求書を見ないのと同じ だ。
コスト視点で見るSnowflakeの3機能
SEARCH関数:LIKEを置き換えるコスト削減策
SEARCH関数は、テキスト検索を オプティマイザが最適化できる形 に変換する。
-- コストが高い:プルーニング無効
SELECT * FROM inquiries
WHERE content LIKE '%キャンセル%' OR content LIKE '%返品%';
-- コストが低い:オプティマイザが最適化
SELECT * FROM inquiries
WHERE SEARCH(content, 'キャンセル OR 返品');
構文の違いは小さい。しかしLIKEの先頭ワイルドカードがプルーニングを無効にするのに対し、SEARCH関数はSnowflakeの内部インデックスを活用できる。問い合わせログの分析、自由記述欄の分類、大量テキストの検索。これらのユースケースで スキャン量が桁違いに変わる 。
HyperLogLog:98%の精度で十分なら、コストは1/1000
「このデータセットに何種類のユーザーがいるか」。この質問に正確に答えるには、全行を読む必要がある。だが、多くのビジネス判断において「100万人」と「99万8千人」の差は意味を持たない。
-- 正確だが高コスト:全行スキャン
SELECT COUNT(DISTINCT user_id) FROM events;
-- 98%精度で1/1000のコスト
SELECT APPROX_COUNT_DISTINCT(user_id) FROM events;
HyperLogLogは、ハッシュ値の統計的性質を利用してユニーク数を推定するアルゴリズムだ1。 約1.5KBのメモリ で数億件のユニークカウントを実現する。Redis、Elasticsearch、ClickHouseでも採用されている、実績ある技術だ。
ポイントは「精度を落とす」のではなく、 「必要十分な精度を選ぶ」 という判断にある。毎時更新のダッシュボードに小数点以下の正確さは要らない。
LIMIT Pruning:読まないデータには課金されない
Snowflakeはデータをマイクロパーティション単位で管理している。LIMIT句がある場合、必要なパーティションだけを読んで結果を返す仕組みがある2。
-- プルーニングが効けば、数パーティションで完了
SELECT * FROM large_table
ORDER BY created_at DESC
LIMIT 100;
プルーニングが効くと スキャン量が劇的に減り、課金も比例して下がる 。Apache DataFusionでも同様の最適化が実装されており、「読むデータ量を最小化する」方向はデータ基盤全体のトレンドだ。
80/20ルール:コストの源泉を特定する
DWHのコスト最適化で最も効果が高いのは、新機能の導入ではない。 コストの80%を生んでいる20%のクエリを特定すること だ。
Snowflakeのクエリ履歴には、各クエリのスキャンバイト数、実行時間、ウェアハウスの消費クレジットが記録されている。これを週次でレビューするだけで、最適化すべきクエリが見える。
-- コスト上位のクエリを特定する
SELECT query_id, query_text,
bytes_scanned,
total_elapsed_time,
partitions_scanned,
partitions_total
FROM snowflake.account_usage.query_history
WHERE start_time > DATEADD(day, -7, CURRENT_TIMESTAMP())
ORDER BY bytes_scanned DESC
LIMIT 20;
この20件を改善するだけで、月間コストが数十パーセント下がることは珍しくない。最新のAI機能を検討する前に、まずこのクエリを実行すべきだ。
見えない最適化がROIを決める
クラウドDWHの本当のROIは、導入時の機能比較表では測れない。日々のクエリが生むコストと、それを抑制する地味な最適化の積み重ねで決まる。
SEARCH関数でLIKEを置き換える。APPROX_COUNT_DISTINCTで十分な場面を見極める。クエリプロファイルを定期的に確認する。どれも派手さはないが、 請求書に反映される改善 だ。
データ基盤の価値は、導入した瞬間ではなく、運用の中で可視化される。その可視化を支えるのは、プレスリリースに載る機能ではなく、こうした地味な最適化技術だ。
Footnotes
-
Philippe Flajolet, Éric Fusy, Olivier Gandouet, Frédéric Meunier. “HyperLogLog: the analysis of a near-optimal cardinality estimation algorithm”. DMTCS Proceedings, 2007. ↩
-
Snowflake Documentation. “Understanding & Using Query Profile”. Pruning and partition elimination. ↩