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

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

Avatar for Shin Shin
January 27, 2026
7

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

Avatar for Shin

Shin

January 27, 2026

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を通して 読む量を意識することが、コスト最適化の第一歩。