「使ってもらいたい」と「コストを管理したい」は両立する。本記事では、民主化のスケールに耐える コスト隔離・負荷隔離の実装パターンを、 / の両方でカバーします。
1. クエリ単位の上限を切る
BigQuery: maximum_bytes_billed
from google.cloud import bigquery
bq = bigquery.Client()ONE_GB = 10 ** 9
# このクエリは絶対に 10 GB 以上スキャンさせないconfig = bigquery.QueryJobConfig(maximum_bytes_billed=10 * ONE_GB)job = bq.query("SELECT ... FROM events ...", job_config=config)
# 上限超過時は QueryJobConfigError がスロー → アプリ側でハンドリングSnowflake: STATEMENT_TIMEOUT_IN_SECONDS / RESOURCE_MONITOR
-- 個別ロールへのタイムアウトALTER ROLE BI_USERS SET STATEMENT_TIMEOUT_IN_SECONDS = 600;
-- ウェアハウス単位のクレジット監視CREATE OR REPLACE RESOURCE MONITOR rm_bi_warehouse WITH CREDIT_QUOTA = 100 -- 月あたり 100 クレジット TRIGGERS ON 75 PERCENT DO NOTIFY ON 100 PERCENT DO SUSPEND ON 110 PERCENT DO SUSPEND_IMMEDIATE;
ALTER WAREHOUSE BI_WH SET RESOURCE_MONITOR = rm_bi_warehouse;2. ワークロード隔離(ノイジーネイバー対策)
「営業時間中に分析者の重いクエリで全社の run が止まる」を防ぐには、ワークロードを物理的に分けます。
| 隔離手段 | 粒度 | |
|---|---|---|
| BigQuery | Reservation + Assignment | プロジェクト × ジョブタイプ |
| Snowflake | Warehouse 分離 | ロール / 用途 |
| Redshift | WLM Queue | ユーザグループ |
CREATE WAREHOUSE BATCH_WH WITH WAREHOUSE_SIZE = 'LARGE' AUTO_SUSPEND = 60 AUTO_RESUME = TRUE;CREATE WAREHOUSE BI_WH WITH WAREHOUSE_SIZE = 'MEDIUM' AUTO_SUSPEND = 60 AUTO_RESUME = TRUE;CREATE WAREHOUSE ADHOC_WH WITH WAREHOUSE_SIZE = 'SMALL' AUTO_SUSPEND = 30 AUTO_RESUME = TRUE STATEMENT_TIMEOUT_IN_SECONDS = 600;
-- ロールに専用 Warehouse をアサインGRANT USAGE ON WAREHOUSE BATCH_WH TO ROLE DBT_RUNNER;GRANT USAGE ON WAREHOUSE BI_WH TO ROLE BI_VIEWER;GRANT USAGE ON WAREHOUSE ADHOC_WH TO ROLE ANALYST;3. クエリ予算アラートの自動化
-- 過去24時間で 100GB 超のクエリSELECT user_email, query, total_bytes_billed / POW(10, 9) AS gb_billed, total_slot_ms / 1000 / 60 AS slot_minutes, creation_timeFROM `region-us`.INFORMATION_SCHEMA.JOBS_BY_PROJECTWHERE creation_time >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 1 DAY) AND total_bytes_billed > 100 * POW(10, 9)ORDER BY total_bytes_billed DESC;強すぎる(毎日 30 件 Slack に流れる)と慣れて見なくなる。「日次 5 件まで」「予算超過時のみ」など 閾値を絞り、月次レビュー会で長期傾向を別途確認するのが運用しやすい。
4. パーティション・クラスタリングを「強制」する
コスト爆発の原因の多くは パーティション忘れ。テーブル設計時に `require_partition_filter = TRUE` を必須化し、`WHERE _PARTITIONTIME` がないクエリを DDL レベルでリジェクトする。
CREATE OR REPLACE TABLE `proj.dwh.events`PARTITION BY DATE(event_time)CLUSTER BY user_id, event_typeOPTIONS ( require_partition_filter = TRUE, -- これがあれば WHERE _PARTITIONTIME 必須 partition_expiration_days = 365);5. 月次のコスト責任配分
民主化が進むと、「誰がいくら使ってるか分からない」状態になる。Slack 通知だけでは責任は固まらない。部署別・チーム別に DWH コストをタグ付け / Reservation 割当てで 月次のコスト振り分けを可視化する。
| DWH | 部署タグの方法 |
|---|---|
| BigQuery | Reservation Assignment + ラベル / プロジェクト分離 |
| Snowflake | Warehouse の所有ロール、`QUERY_TAG` 強制 |
| Redshift | ユーザグループ + WLM Queue |
-- セッション開始時に QUERY_TAG が空ならエラー(実装は session policy で)ALTER ROLE BI_USERS SET QUERY_TAG_REQUIRED = TRUE;
-- ユーザは自分のセッションで明示的にタグを設定ALTER SESSION SET QUERY_TAG = 'team:marketing,project:campaign-q4';
-- 月次集計で部署別コストSELECT PARSE_JSON(query_tag):team::STRING AS team, SUM(credits_used_cloud_services) AS creditsFROM SNOWFLAKE.ACCOUNT_USAGE.QUERY_HISTORYWHERE start_time >= DATEADD(month, -1, CURRENT_DATE())GROUP BY 1;6. 「予算」ではなく「予測」で先回り
月初 → 月末で予算消化が線形でないことが多い。中旬時点で「月末超過予測」を計算し、早期に調整する仕組みが効く。
from datetime import datetimefrom calendar import monthrange
def project_monthly_spend(spent_to_date: float, today: datetime) -> dict: days_in_month = monthrange(today.year, today.month)[1] day_of_month = today.day daily_avg = spent_to_date / day_of_month projected = daily_avg * days_in_month return { "spent_to_date": spent_to_date, "daily_avg": daily_avg, "projected_eom": projected, "remaining_days": days_in_month - day_of_month, }
# 例: 15日時点で $750 使ってるprint(project_monthly_spend(750, datetime(2027, 12, 15)))# {'projected_eom': 1500.0, ...} → 月予算 $1000 なら早めに調整次の話
EP.07 では、メタデータ基盤。dbt docs / DataHub / Atlan のどれを選ぶか、運用にどう乗せるか。「このテーブル何ですか?」に機械的に答える土台。
この記事の感想を教えてください
あなたの 1 クリックで、本当にこの記事は更新されます。「もっと詳しく」「続編希望」が一定数集まった記事は、 ふくふくが 実際に内容を拡充したり続編記事を公開 します。 送信したリアクションはお使いのブラウザに記録され、再カウントされません。