dbtでデータ基盤を運用し始めた最初の数ヶ月は、Snowflakeのクレジット消費もそこそこに収まります。「これなら年間予算で十分」と思っていたある月末、コストが2倍3倍に跳ねていて経理から問い合わせが来る、というのはよくある光景です。原因は判断ミスの積み重ねです。
dbt + Snowflakeのコスト構造は明瞭で、抑える勘どころも限られます。効くポイントを順に押さえます。
何にお金がかかっているのか
Snowflakeの課金は、おおまかにストレージとコンピュートに分かれます。dbt運用で跳ねるのはコンピュート、つまり仮想ウェアハウスの稼働時間です。仕組みはSnowflakeとはに整理しています。
| 原因 | 典型症状 |
|---|---|
| materializationの選定ミス | 大きなテーブルがtable/viewのまま、毎回全件処理 |
| ウェアハウスサイズが過剰 | 軽い処理にLargeを使い続ける |
| full-refreshの乱用 | 本来incrementalで済むモデルを毎晩作り直す |
| auto_suspend が長い | ジョブ終了後も10分以上動き続ける |
| ジョブの並行衝突 | BIとETLが同じウェアハウスで取り合い |
| マートが過剰 | 使われていないGold層のテーブルが毎晩構築される |
対策1:materializationを正しく選ぶ
各モデルのmaterialization見直しが最も効果的です。判断軸はシンプルです。
| 状況 | 推奨 |
|---|---|
| クエリが軽く、参照が少ない | view |
| 中規模、追加・更新が複雑 | table |
| 大規模、追加中心 | incremental |
| 履歴管理が必要 | snapshot |
大きなファクトテーブルがtableのままになっていないか、まず棚卸しします。incrementalの設計はincremental modelsの設計で詳しく扱っています。
対策2:ウェアハウスをサイズと用途で分ける
1つのウェアハウスで全部をさばくと、用途の違う処理が互いを遅くしてコストも膨らみます。dbt運用なら、最低限こう分けると効きます。
| ウェアハウス | 用途 | 推奨サイズ |
|---|---|---|
| WH_DBT_TRANSFORM | dbtのrun/test/build | S〜M(負荷次第でscale up) |
| WH_BI | BIツールからの参照 | XS〜S(同時実行多めならマルチクラスタ) |
| WH_ADHOC | 分析者の手動クエリ | XS〜S |
-- 用途別ウェアハウスの最小設定
CREATE WAREHOUSE wh_dbt_transform
WAREHOUSE_SIZE = 'SMALL'
AUTO_SUSPEND = 60 -- 60秒アイドルで停止
AUTO_RESUME = TRUE
MIN_CLUSTER_COUNT = 1
MAX_CLUSTER_COUNT = 1; -- 単一クラスタで十分
対策3:auto_suspendを短く
auto_suspendはアイドル状態のウェアハウスを停止するまでの待ち時間です。既定の10分は、dbt運用には長すぎます。60秒で十分なケースが多いです。
注意点は、ウェアハウスが停止するとキャッシュも失われ、次回起動時に再度コンパイル・データ読み込みが必要になることです。BIで頻繁に参照されるウェアハウスは、auto_suspendを少し長め(300〜600秒)にして、キャッシュ効果を残すと体感速度とコストのバランスが取れます。dbt実行用は60秒、BI参照用は300秒、と用途で分けるのが定石です。
対策4:full-refreshを安易に使わない
CI/CDで`dbt build –full-refresh`を毎晩走らせている、というケースを時々見ます。原因はだいたい「過去データの修正に対応するため」ですが、それなら遅延データ用の窓を入れたincrementalで対応できます。
-- 遅延データ対策:過去3日まで遡って再評価
{% if is_incremental() %}
WHERE ordered_at >= DATEADD(day, -3, (SELECT MAX(ordered_at) FROM {{ this }}))
{% endif %}
full-refreshが必要なのは、ロジック変更後の初回ビルドと、月1回程度の整合性確認だけにします。日次運用から外すと、それだけでコストが大きく下がります。
対策5:使われていないモデルを止める
Gold層に「念のため」作ったマートが、誰にも使われていないのに毎晩構築されている、というのは現場でよく起こります。Snowflakeの`ACCESS_HISTORY`や`QUERY_HISTORY`から、参照されていないテーブルを洗い出せます。
-- 過去30日間に参照されていないテーブル
SELECT
table_name,
MAX(query_start_time) AS last_used
FROM snowflake.account_usage.access_history,
LATERAL FLATTEN(direct_objects_accessed)
WHERE query_start_time >= DATEADD(day, -30, CURRENT_TIMESTAMP())
AND value:objectDomain = 'Table'
GROUP BY table_name
HAVING MAX(query_start_time) IS NULL
ORDER BY last_used;
洗い出したモデルは、`enabled: false`で一時的に止めるか、削除します。dbtのexposuresを書いていれば「BIが参照しているか」も追跡できるので、判断が安全になります。
対策6:dbtのクエリにタグを付けて可視化する
どのモデルがどれだけクレジットを消費しているか、を可視化するとボトルネックが一目で分かります。dbtの`+query_tag`を使うと、Snowflakeのクエリ履歴にタグを付与できます。
-- dbt_project.yml
models:
my_project:
silver:
+query_tag: "{{ 'dbt_silver_' ~ this.identifier }}"
gold:
+query_tag: "{{ 'dbt_gold_' ~ this.identifier }}"
これでSnowflakeのクエリ履歴に`dbt_silver_fct_orders`のようなタグが付き、`query_history`をクエリすればモデル別にクレジット消費を集計できます。「重いトップ10」が見えるだけで、改善の優先順位が決まります。
まとめ
- 大きなファクトをtableのままにしない。incrementalに切り替える。
- ウェアハウスは用途で分け、サイズは控えめから始めて必要時にscale upする。
- auto_suspendは60秒を基本に、BI参照用だけ長めにする。
- full-refreshを日次から外し、遅延データは増分の窓で吸収する。
- 参照されていないGoldマートを止める。query_tagで重いトップ10を可視化する。
関連:dbt incremental modelsの設計、dbt tests 実践、dbt snapshotsでSCD Type 2。Snowflake全般の設計はSnowflakeとは、クラウドDWH選定はクラウドDWH入門もあわせてどうぞ。コストレビューや改善の壁打ちは、DE-STKの初回相談(30分・無料)もご利用ください。
よくある質問(FAQ)
Q. ウェアハウスを小さくしすぎると、ジョブが終わらないのでは?
A. 小さすぎると確かに終わらなくなりますが、まず計測してから調整するのが正解です。XSやSで動かしてみて、許容できる時間内に終わるなら、それで十分です。クレジット消費はサイズと時間の積なので、「Lで30分」と「Sで60分」が同じ金額のケースもあります。終わらない処理だけサイズを上げる、という調整が現実的です。
Q. クエリのタグ付けは、後からでも入れられますか?
A. 入れられます。`dbt_project.yml`に`+query_tag`を足してデプロイすれば、次の実行から効きます。既存の運用を止めずに導入できる施策なので、まず最初に入れるとよいです。タグ付けされたデータが1週間ほど溜まれば、重いトップ10が見えてきます。
Q. BIツールがSnowflakeに直接クエリを投げると、コストが膨らみませんか?
A. BIの利用頻度次第ですが、対策はあります。事前集計したワイドテーブルをGold層に用意し、BIはそれだけを参照する。BI専用のウェアハウスを分けて、auto_suspendを短く、サイズを控えめにする。LookerのPersistent Derived Tablesのようなキャッシュ機構を活用する。これらで、利用が増えてもコストの線形増を抑えられます。
Q. クレジット消費が急に増えました。どこから調べますか?
A. まず`account_usage.warehouse_metering_history`で、どのウェアハウスが消費しているかを切り分けます。次に、そのウェアハウスの`query_history`から長時間・高コストのクエリを特定します。「最近追加された大きいモデルがfull-refreshされている」「BIの新ダッシュボードが重い」「auto_suspendが解除された」あたりが定番の原因です。改善の効果は数日で見えるので、計測しながら一つずつ潰します。