Upgrade to Pro — share decks privately, control downloads, hide ads and more …

【DWH】Snowflakeで等価条件なのにコストが変わる理由

Avatar for Shin Shin
January 27, 2026
1

 【DWH】Snowflakeで等価条件なのにコストが変わる理由

Avatar for Shin

Shin

January 27, 2026
Tweet

More Decks by Shin

Transcript

  1. 目次 1. はじめに 2. Snowflakeのコスト構造 3. 「Query Profile」とは何か 4. なぜ等価条件でコスト差が出るのか

    5. 検証 6. 実務でどう生かすか 7. まとめ ※ 本検証は等価条件・IN 条件を題材としたものであり、 すべてのクエリパターンに当てはまるものではありません。
  2. 2.Snowflakeのコスト構造 Snowflakeのコストは以下2つ • ストレージコスト(置いておくだけの料金) ◦ データを保存している量に対する料金 ◦ 圧縮後サイズが基準 ◦ クエリを実行しても

    基本的に増えない • コンピュートコスト( Virtual Warehouse)動かした分だけの料金 ◦ クエリを実行している間だけ発生 ◦ Virtual Warehouse(計算用エンジン)の使用料 ◦ Warehouseサイズが大きいほど 時間単価が高い 👉 今回の検証は「コンピュート」に注目
  3. 2.Snowflakeのコスト構造 • コンピュートコストの計算 ◦ コンピュートコスト ≒ Warehouseサイズ × 実行時間 ▪

    Warehouseサイズ → 計算機の「大きさ・馬力」 ▪ 実行時間 → 計算機を「動かしていた時間」 • 実行時間を決める最大要因 ◦ スキャン量(Bytes scanned) ▪ どれだけデータを読んだか ▪ 読む量が多いほど時間がかかる ◦ プルーニングの効き具合 ▪ 不要なデータを最初から読まない仕組み ▪ 効くほどスキャン量が減る ▪ 効かないと ほぼ全データを読む
  4. 3.「Query Profile」とは何か • Query Profileとは ◦ クエリ実行後に確認できる実行結果の可視化 ◦ クエリがどの処理を通り、どこで時間とリソースを使ったかを表示する •

    Snowflakeにおける位置づけ ◦ Snowflakeでは RDBのような「事前の実行計画」は重視されない ◦ 代わりに実行後の結果(実測)を Query Profile で確認する 👉 Query Profile が「実行計画」に相当 • Query Profileで分かること ◦ クエリ全体の実行時間 ◦ 各処理(Scan / Join / Aggregate など)の内訳 ◦ データの読み取り量や処理負荷
  5. 3.「Query Profile」とは何か • Query Profileで見るポイント ◦ Table Scan • Partitions

    scanned / total ◦ どれだけの partition を読んだか ▪ Bytes scanned • 実際に読み取ったデータ量 👉 スキャン量が実行時間・コストに直結 ◦ 各処理の実行時間 ▪ Scan / Join / Aggregate など • どの処理で時間を使ったかを見る 👉 コスト増加の原因を特定できる
  6. 4.なぜ等価条件でコスト差が出るのか • Snowflakeは「行」ではなく「 micro-partition」でデータを読む ◦ Snowflakeは 1行ずつ条件判定をしながら読む仕組みではない。 ◦ データは自動的にmicro-partition と呼ばれる

    ◦ 物理的なデータの塊に分割されて保存される。 クエリ実行時は この micro-partition 単位で読む/読まないを判断する。 • Micro-partitionが持つメタデータ ◦ 最小値(MIN) ◦ 最大値(MAX) ◦ NULL の有無 など 👉 実データを読まずに、中身の傾向を把握できる • 読む/読まないの判断基準 ◦ クエリの条件を見て「この partition に条件の値が入り得るか?」を判定 ◦ 入り得ないと判断された partition は 最初から読み飛ばされる(プルーニング ) 👉 行を見に行く前に、不要なデータを除外できる
  7. 4.なぜ等価条件でコスト差が出るのか • 等価条件でも差が出る理由 ◦ パターン①:値がまとまっている(プルーニングが効く ) ▪ 同じ値が限られた partition に集中

    ▪ 条件に合わない partition を 丸ごとスキップ ▪ 👉 読む partition が少ない 👉 スキャン量が小さい 👉 コストが低い ◦ パターン②:値が散らばっている(プルーニングが効かない ) ▪ 同じ値が多くの partition に分散 ▪ 多くの partition が 「条件に合う可能性あり」と判定される ▪ 👉 ほぼ全 partition を読む 👉 スキャン量が大きい 👉 コストが高い
  8. 5.検証 • 検証の目的 ◦ 同じ等価条件でも、物理配置の違いによって スキャン量・実行時間・コストに差が出るかを確認する ▪ 検証の前提条件 • 同じSQL

    • 同じデータ(件数・値の分布) • 同じWarehouseサイズ • 違うのは「物理配置」だけ 👉 差が出た場合、原因はプルーニング効率の違いと説明できる • 検証方法と見るポイント ◦ 検証方法(概要) ▪ 同じ元データから配置の異なる2つのテーブルを作成 • 値がまとまる配置 • 値が散らばる配置 • 同じ等価条件クエリを実行
  9. 5.検証 • 検証に使用するデータ量 ◦ 行数:数百万行(約300万行) ◦ micro-partition 数:16 ◦ データサイズ:数

    MB 程度 ▪ 列構成: • 等価条件に使用する列(category)アルファベット26種類 • 日付列(created_at) • ペイロード列(文字列、サイズ調整用) • 検証用テーブルの構成(概要) ◦ 同じ元データから作成した 2つのテーブル ◦ データ内容・件数・分布は 完全に同一 ◦ 違いは データの物理的な並び順のみ ▪ テーブル構成 • T_ORDERED • category 列で並べ替えて作成 • 同じ値が近くに配置される ▪ T_RANDOM • ランダム順で作成 • 同じ値が全体に散らばる
  10. 5.検証 • ①使用するクエリ(等価条件) ◦ SELECT COUNT(*) FROM T_ORDERED WHERE category

    = 'A'; ◦ SELECT COUNT(*) FROM T_RANDOM WHERE category = 'A'; ▪ 等価条件(=)を使用 ▪ 返却行数の影響を避けるため COUNT(*) ▪ キャッシュの影響を避けて実行 • ②使用するクエリ(等価条件・複数値) ◦ SELECT COUNT(*) FROM T_ORDERED WHERE category IN ('A','B','C','D','E'); ◦ SELECT COUNT(*) FROM T_RANDOM WHERE category IN ('A','B','C','D','E'); ▪ 等価条件の 複数値指定(IN) ▪ 対象データ範囲を意図的に拡張 ▪ プルーニング効率の変化を確認 ▪ キャッシュの影響を避けて実行
  11. 5.検証 ①等価条件 SELECT COUNT(*) FROM T_ORDERED WHERE category = 'A';

    SELECT COUNT(*) FROM T_RANDOM WHERE category = 'A';
  12. 5.検証 • Query Profile(ORDERED) ◦ 観察結果 ▪ Partitions scanned: 1

    / 16 ▪ Bytes scanned: 0.02MB ▪ 条件値 category = 'A' は 特定の micro-partition に集中 ◦ 考察 ▪ micro-partition の MIN/MAX メタデータにより 多くの partition がスキップされた ▪ 物理的に値がまとまっていると 等価条件でも高いプルーニング効果 • Query Profile(RANDOM) ◦ 観察結果 ▪ Partitions scanned: 16 / 16 ▪ Bytes scanned: 1.76MB ▪ 同じ値が全体に散らばっている ◦ 考察 ▪ 各 micro-partition に category = 'A' が 含まれる可能性あり ▪ MIN / MAX 判定で除外できず 全 partition をスキャン • 📌まとめ ◦ 同じ等価条件・同じデータ件数でも、 物理配置の違いにより読まれる micro-partition 数が大きく変わる
  13. 5.検証 • Query Profile(ORDERED) ◦ 観察結果 ▪ Partitions scanned: 4

    / 16 ▪ Bytes scanned: 0.15MB ▪ 対象行数:約 576.9k ◦ 考察 ▪ categoryが物理的にまとまって配置されているため、IN 条件でも必要なmicro-partitionのみをスキャン ▪ 単一値(= 'A')よりは増えるが、 全体スキャンにはならない 👉 条件が広がってもプルーニングは段階的に効く • Query Profile(RANDOM) ◦ 観察結果 ▪ Partitions scanned: 16 / 16 ▪ Bytes scanned: 1.76MB ▪ 対象行数:約 576.9k ◦ 考察 ▪ 複数値が全体に散らばっているため、すべての micro-partition に該当する可能性あり ▪ MIN / MAX メタデータで除外できず、 全 partition をスキャン 👉 IN 条件により、差がさらに明確化 • 📌まとめ ◦ IN 条件で対象が増えても、物理的にまとまっていれば プルーニングは段階的に効くが、 散らばっている場合は最初から全スキャンになる。
  14. 6.実務でどう生かすか • よく使う絞り込み条件を意識する ◦ 等価条件・IN 条件で頻繁に使う列は重要 ▪ 種別 ▪ ステータス

    ▪ 日付 など ◦ それらが 物理的に近くに配置されているかで micro-partition プルーニングの効き方が変わる 👉「WHERE句で絞れる」より 「同じ値が近くに置かれているか」が効く • Query Profileで「時間ではなく読む量」を見る ◦ 実行時間はキャッシュや環境でブレやすいため、実務では以下を優先して確認 ▪ Partitions scanned ▪ Bytes scanned ◦ 同じ結果・同じSQLでも 読む量が違えば将来のコスト差になる 👉 速さより「どれだけ読んだか」 • 「今は速いからOK」は危険 ◦ データ量が少ないうちは差が見えにくい。将来データが増えると ... ▪ 無駄な全スキャン ▪ クレジット消費増 ▪ 並列実行時の競合 ◦ 👉 Query Profileで早めに気づけるかが重要
  15. 7.まとめ • 今回分かったこと ◦ 同じ等価条件・同じデータでも、 物理配置の違いによって 読まれる micro-partition 数が大きく変わる ◦

    読む量(Partitions / Bytes)が変わることで、 将来的なコスト差につながる • Snowflakeらしい見方 ◦ Snowflakeでは実行時間よりも「どれだけ読んだか」が重要 ◦ その判断は Query Profile を見ることで説明できる • 実務への持ち帰り ◦ よく使う等価条件・IN 条件が 効きやすい配置になっているかを意識する ◦ 「今は速いからOK」にせず、 Query Profileで読む量を確認する 📌 Snowflakeでは、Query Profileを通して 読む量を意識することが、コスト最適化の第一歩。