このSnowflakeウェアハウスの自動停止時間は何秒が最適なのか?クエリ履歴から算出するコスト最小化検証実験(超節約生活実験)
1. はじめに
SnowflakeのウェアハウスにはAUTO_SUSPENDというパラメータがあり、一時停止までの時間を任意で設定できます。通常はこの値を60秒などに設定して、その後変更することはほとんどありませんが、「変えることでどれくらい効果があるのだろうか?」と気になることもあります。
以下は、特定ウェアハウスの直近のクエリ履歴に基づき、AUTO_SUSPENDの値によって課金時間がどのように変化するかを可視化した例です。本記事では、このグラフが、どのような仮説によって出来上がったかを説明します。
実運用での効果は限定的であり、あくまで理論的な考察・検証実験の一例として捉えてください。

特定ウェアハウスの直近の利用状況から、AUTO_SUSPENDの最適値を可視化したグラフ
本記事は、シミュレーションを単純化するために、以下の前提で作成しています。
- 単一クラスターウェアハウスを対象。
- ウェアハウスのデータキャッシュの効果は考慮しない。
- 自動停止の30秒ポーリング挙動を考慮しない。
この記事は、NTTデータ Snowflakeアドベントカレンダーの1日目です。
2. Snowflake課金の基礎とAUTO_SUSPENDの挙動
-
課金の基本
- Snowflakeのウェアハウスは秒課金ですが、ウェアハウスの「起動/再開」ごとに最小60秒は必ず課金されます。
- 60秒以降であれば、停止するまで秒単位で課金されます。
-
AUTO_SUSPEND
- 最後のアクティビティからN秒間 実行状態で待機し、自動停止します。
- 設定値=0 は、「即時停止」ではなく「自動停止しない」設定になります。
3. ワークロード別の検討例
ウェアハウスは「最小60秒は必ず課金される」ため、ワークロードによって最適な設定値は異なってきます。3つの具体的なワークロードとともに、自動停止時間(AUTO_SUSPEND)の検討ポイントを説明します。
ワークロードA:1分以上のクエリが実行される
想定ケース:10分のジョブが1本。ジョブ終了してから5分後に10分のジョブがもう1本実行される長時間バッチ処理
- AUTO_SUSPEND=10[秒]
→ 課金 = max(60, 600+10) + max(60, 600+10) = 1220[秒]課金 - AUTO_SUSPEND=60[秒]
→ 課金 = max(60, 600+60) + max(60, 600+60) = 1320[秒]課金
検討ポイント:60秒以上のクエリのみが実行される場合は、短い値が有利。

ワークロードB:数秒のクエリが数秒間隔で実行される
想定ケース:5秒のクエリが1本。クエリが完了してから15秒後に5秒のクエリがもう1本実行されるリアルタイム連携処理
- AUTO_SUSPEND=10[秒]
→ 課金 = max(60, 5+10) + max(60, 5+10) = 120[秒]課金 - AUTO_SUSPEND=60[秒]
→ 課金 = max(60, 5+15+5+60) = 90[秒]課金
検討ポイント:短く設定するとウェアハウスが2回起動されるため、長めの値が有利。

ワークロードC:実行時間・間隔が様々
想定ケース:ランダムな実行時間・間隔でクエリが実行される自由分析(20秒実行 → 10秒待機 → 15秒実行 → 70秒待機 → 25秒実行 → 15秒待機 → 10秒実行 → 20秒待機 → 40秒実行)
- AUTO_SUSPEND=10[秒]
→ 課金 = max(60, 60) + max(60, 35) + max(60, 20) + max(60, 50) + max(60, 65) = 305[秒]課金 - AUTO_SUSPEND=60[秒]
→ 課金 = max(60, 105) + max(60, 155) = 260[秒]課金
AUTO_SUSPENDが30[秒]なら?40[秒]なら?・・・設定によってウェアハウスの起動回数が変わるため、コストの把握が複雑で、最適な値を見つけるのが難しい。

4. 自動最適化ツールの設計
ワークロードCのような実行時間・間隔が様々である場合、AUTO_SUSPENDの最適値を調査することが難しくなります。そのため、Snowflakeのクエリ履歴を対象データとし、SnowsightのNotebookを利用して、最適値を分析してみます。
ウェアハウス課金の仕様を考慮して、以下のアルゴリズムでツールを作成します。
1. クエリ履歴の取得 (_fetch_query_history)
-
ACCOUNT_USAGE.QUERY_HISTORYビューから指定ウェアハウスの過去months_backヶ月分のクエリを取得。
2. 処理時間帯の統合 (_merge_windows)
- クエリの開始時間・終了時間のペアを見て、重なりや連続する時間帯を1つの区間に統合。
- 結果を 重複のない連続処理区間リスト として計算。
3. 課金時間の計算 (_billed_seconds)
- 処理区間の間隔が
s秒以上空いていたら、新しい課金区間として合計に加算。 - 課金区間の長さ +
s秒を課金時間として計算(最低課金単位 60 秒)。 - 全課金区間を順次計算して総課金時間を算出。
4. メイン処理 (compute_billing)
- クエリ履歴を取得。
- 処理区間を統合。
-
AUTO_SUSPEND=1..60秒の各秒数で課金時間を計算。 - 結果を DataFrame にまとめ返却
5. Python Notebookで実行
- SnowsightからPython Notebookを作成します。
- Matplotlibパッケージを追加します。
- 処理プログラムコードを実行します。
処理プログラムコード
from typing import List, Tuple
import math
import pandas as pd
from snowflake.snowpark.context import get_active_session
from snowflake.snowpark.functions import col, lit
MAX_AUTO_SUSPEND_SEC = 60 # S の最大値 (1..60)
def _fetch_query_history(warehouse: str, months_back: int) -> pd.DataFrame:
"""QUERY_HISTORY から対象期間のクエリを取得 (START_TIME/END_TIME のみ)。"""
session = get_active_session()
t = session.sql(f"""
select
dateadd('month', -{months_back}, current_timestamp()) as since_ts,
current_timestamp() as now_ts,
dateadd('second', -{MAX_AUTO_SUSPEND_SEC}, dateadd('month', -{months_back}, current_timestamp())) as since_shifted
""").collect()[0]
qh = (
session.table("SNOWFLAKE.ACCOUNT_USAGE.QUERY_HISTORY")
.filter(col("WAREHOUSE_NAME") == warehouse)
.filter(col("START_TIME") >= lit(t['SINCE_SHIFTED']))
.filter(col("START_TIME") <= lit(t['NOW_TS']))
.filter(col("END_TIME").is_not_null())
.select("START_TIME", "END_TIME")
).to_pandas()
qh['START_TIME'] = pd.to_datetime(qh['START_TIME'])
qh['END_TIME'] = pd.to_datetime(qh['END_TIME'])
return qh.sort_values('START_TIME').reset_index(drop=True)
def _merge_windows(df: pd.DataFrame) -> List[Tuple[pd.Timestamp, pd.Timestamp]]:
"""重なり/連続する稼働時間帯を統合。"""
merged: List[Tuple[pd.Timestamp, pd.Timestamp]] = []
for s, e in df[['START_TIME', 'END_TIME']].itertuples(index=False):
if not merged:
merged.append((s, e))
continue
ls, le = merged[-1]
if s <= le:
if e > le:
merged[-1] = (ls, e)
else:
merged.append((s, e))
return merged
def _billed_seconds(windows: List[Tuple[pd.Timestamp, pd.Timestamp]], s: int) -> int:
"""与えられた S に対する総課金時間を計算。"""
if not windows:
return 0
total, g_start, g_end = 0, *windows[0]
for ws, we in windows[1:]:
if (ws - g_end).total_seconds() >= s:
total += max(60, (g_end - g_start).total_seconds() + s)
g_start, g_end = ws, we
else:
if we > g_end:
g_end = we
total += max(60, (g_end - g_start).total_seconds() + s)
return int(math.ceil(total))
def compute_billing(warehouse: str, months_back: int) -> pd.DataFrame:
"""
対象ウェアハウスの直近 N ヶ月について
AUTO_SUSPEND 秒 (1..60) ごとの billed_seconds / billed_hours を返す。
"""
df = _fetch_query_history(warehouse, months_back)
windows = _merge_windows(df)
rows = [
(s, (bs := _billed_seconds(windows, s)), round(bs / 3600, 6))
for s in range(1, MAX_AUTO_SUSPEND_SEC + 1)
]
return pd.DataFrame(rows, columns=["auto_suspend_sec", "billed_seconds", "billed_hours"])
- 可視化用のプログラムコードを実行します。
引数として「ウェアハウス名」と「処理対象期間」を指定します。
可視化プログラムコード
if __name__ == "__main__":
import matplotlib.pyplot as plt
WAREHOUSE = "COMPUTE_WH"
MONTHS = 3
df = compute_billing(WAREHOUSE, MONTHS)
plt.figure(figsize=(7, 3))
plt.plot(df["auto_suspend_sec"], df["billed_hours"], marker="o", linewidth=1, label="Billed Hours")
# 最小値を強調表示
best_idx = df["billed_hours"].idxmin()
best_s = int(df.loc[best_idx, "auto_suspend_sec"])
best_h = df.loc[best_idx, "billed_hours"]
plt.scatter([best_s], [best_h], color="red", zorder=3, label=f"Best={best_s}s")
plt.title(f"AUTO_SUSPEND vs billed hours (last {MONTHS} months, {WAREHOUSE})")
plt.xlabel("AUTO_SUSPEND (sec)")
plt.ylabel("Billed hours")
plt.grid(alpha=0.3)
plt.legend()
plt.tight_layout()
plt.show()
- 実行すると、AUTO_SUSPENDを1~60秒まで変化させた場合に、総課金時間(billed hours)がどのように変化するかの課金シミュレーション結果を取得できます。
この例では、COMPUTE_WHの直近3ヶ月のクエリ履歴を基に、AUTO_SUSPENDの値に応じてどれくらい課金時間が変わるかを表しています。AUTO_SUSPEND=11[秒]が最も課金時間が少なく、効率的に稼働していることが分かります。一方で、AUTO_SUSPEND=60[秒]にすると、最適値と比べて50%以上もコストが発生してしまうことが分かりました。

私の環境で特定のウェアハウスにおけるAUTO_SUSPENDの最適値を算出したグラフ
6. まとめ
単純なワークロードを例に、ウェアハウスの課金とAUTO_SUSPENDの関係を検証し、ワークロードによって最適なAUTO_SUSPENDの値が変わることを確認しました。実行時間・間隔が様々なワークロードでは最適値を判断しづらいため、クエリ履歴を用いたシミュレーションは状況を把握するための有効な検証手段となります。
ただし、ここで扱った内容はあくまで理論的な実験であり、本番運用に取り込むことを目的としたものではありません。細かな設定を意識せずとも良いのが Snowflake の魅力であり、本記事がコスト最適化を考えるための小さなヒントになれば幸いです。
7. 仲間募集
NTTデータ ソリューション事業本部 では、以下の職種を募集しています。
Snowflake、生成AIを活用したデータ基盤構築/活用支援(Snowflake Data Superheroesとの協働)
Databricks、生成AIを活用したデータ基盤構築/活用支援(Databricks Championとの協働)
プロジェクトマネージャー(データ分析プラットフォームソリューションの企画~開発~導入/生成AI活用)
クラウドを活用したデータ分析プラットフォームの開発(ITアーキテクト/PM/クラウドエンジニア)
8. ソリューション紹介
Trusted Data Foundationについて
~データ資産を分析活用するための環境をオールインワンで提供するソリューション~
最新のクラウド技術を採用して弊社が独自に設計したリファレンスアーキテクチャ(Datalake+DWH+AI/BI)を顧客要件に合わせてカスタマイズして提供します。
可視化、機械学習、DeepLearningなどデータ資産を分析活用するための環境がオールインワンで用意されており、これまでとは別次元の量と質のデータを用いてアジリティ高くDX推進を実現できます。
TDFⓇ-AM(Trusted Data Foundation - Analytics Managed Service)について
~データ活用基盤の段階的な拡張支援(Quick Start) と保守運用のマネジメント(Analytics Managed)をご提供することでお客様のDXを成功に導く、データ活用プラットフォームサービス~ TDFⓇ-AMは、データ活用をQuickに始めることができ、データ活用の成熟度に応じて段階的に環境を拡張します。プラットフォームの保守運用はNTTデータが一括で実施し、お客様は成果創出に専念することが可能です。また、日々最新のテクノロジーをキャッチアップし、常に活用しやすい環境を提供します。なお、ご要望に応じて上流のコンサルティングフェーズからAI/BIなどのデータ活用支援に至るまで、End to Endで課題解決に向けて伴走することも可能です。
NTTデータとSnowflakeについて
NTTデータとSnowflakeについて
NTTデータでは、Snowflake Inc.とソリューションパートナー契約を締結し、クラウド・データプラットフォーム「Snowflake」の導入・構築、および活用支援を開始しています。
NTTデータではこれまでも、独自ノウハウに基づき、ビッグデータ・AIなど領域に係る市場競争力のあるさまざまなソリューションパートナーとともにエコシステムを形成し、お客さまのビジネス変革を導いてきました。
Snowflakeは、これら先端テクノロジーとのエコシステムの形成に強みがあり、NTTデータはこれらを組み合わせることでお客さまに最適なインテグレーションをご提供いたします。
NTTデータとDatabricksについて
NTTデータは、お客様企業のデジタル変革・DXの成功に向けて、「databricks」のソリューションの提供に加え、情報活用戦略の立案から、AI技術の活用も含めたアナリティクス、分析基盤構築・運用、分析業務のアウトソースまで、ワンストップの支援を提供いたします。
NTT DATA公式アカウントです。 技術を愛するNTT DATAの技術者が、気軽に楽しく発信していきます。 当社のサービスなどについてのお問い合わせは、 お問い合わせフォーム nttdata.com/jp/ja/contact-us/ へお願いします。