⚡ BigQuery キャッシュの仕組み

クエリ結果キャッシュの保存単位と動作原理

📖 BigQueryキャッシュとは

🎯 概要

BigQueryは、クエリの結果を自動的にキャッシュします。 同一のクエリを再実行した場合、実際にデータをスキャンせずにキャッシュから結果を返すため、 コストが無料で、応答が超高速になります。

キャッシュヒット時の流れ

🔍
クエリ実行
SELECT * FROM table
WHERE date = '2024-11-25'
キャッシュチェック
キャッシュヒット!
24時間以内・データ不変
結果返却
無料
0バイトスキャン
数ミリ秒で完了
↓ キャッシュミスの場合
🔍
クエリ実行
キャッシュなし
実データをスキャン
💰
結果返却
課金対象
データスキャン発生
数秒〜数分

🔑 キャッシュの保存単位:クエリテキスト単位

🎯 重要な概念

BigQueryのキャッシュは、クエリのテキスト(SQL文字列)をキーとして保存されます。 内部的には、クエリテキストのハッシュ値をキーとして、 そのクエリの実行結果全体を値として保存します。

キャッシュの構造

🔑 キー(Key)
クエリテキストのハッシュ値
-- 元のクエリ SELECT product_id, SUM(amount) AS total FROM `project.dataset.sales` WHERE date = '2024-11-25' GROUP BY product_id ORDER BY total DESC LIMIT 100; -- ↓ ハッシュ化 Hash: "a7f3e8b2c9d1..." (内部的に生成)
💾 値(Value)
クエリの実行結果全体
product_id total
PROD-123 150,000
PROD-456 120,000
... ...
+ メタデータ(スキーマ、行数、タイムスタンプ等)
🔍 重要な特性:

具体例:クエリテキストの違いによるキャッシュへの影響

✅ ケース1: 完全に同一のクエリ
クエリA:
SELECT * FROM dataset.table WHERE date = '2024-11-25'
クエリB:
SELECT * FROM dataset.table WHERE date = '2024-11-25'
✅ 同一のハッシュ値 → キャッシュヒット
❌ ケース2: 空白の違い
クエリA:
SELECT * FROM dataset.table WHERE date = '2024-11-25'
クエリB:
SELECT * FROM dataset.table WHERE date = '2024-11-25'
❌ 異なるハッシュ値 → キャッシュミス
❌ ケース3: 大文字小文字の違い
クエリA:
SELECT * FROM dataset.table WHERE date = '2024-11-25'
クエリB:
select * from dataset.table where date = '2024-11-25'
❌ 異なるハッシュ値 → キャッシュミス
❌ ケース4: コメントの追加
クエリA:
SELECT * FROM dataset.table WHERE date = '2024-11-25'
クエリB:
-- 本日のデータ取得 SELECT * FROM dataset.table WHERE date = '2024-11-25'
❌ 異なるハッシュ値 → キャッシュミス

⏱️ キャッシュの有効期間

キャッシュの24時間ライフサイクル

0h
クエリ実行
キャッシュ作成
6h
キャッシュ有効
12h
キャッシュ有効
18h
キャッシュ有効
24h
キャッシュ失効
削除される
📌 ポイント:
  • クエリ結果は最大24時間キャッシュされる
  • 24時間以内に再実行すればキャッシュヒット
  • 24時間経過後は自動的に削除される
  • キャッシュの延長は手動では不可能

🚫 キャッシュが無効化される条件

キャッシュが使われない・無効化されるケース

🔄
1. テーブルが更新された
クエリが参照するテーブルにINSERT、UPDATE、DELETE、MERGEなどが実行された場合
-- テーブルにデータ挿入 INSERT INTO dataset.sales VALUES (...); -- ↓ このクエリのキャッシュは無効化される SELECT * FROM dataset.sales WHERE ...;
2. 24時間が経過した
キャッシュの有効期限(24時間)が切れた場合
🎲
3. 非決定的関数を使用
CURRENT_TIMESTAMP()、RAND()、SESSION_USER()などの実行ごとに結果が変わる関数
-- 非決定的 → キャッシュされない SELECT *, CURRENT_TIMESTAMP() AS query_time FROM dataset.table; -- 非決定的 → キャッシュされない SELECT *, RAND() AS random_value FROM dataset.table;
🌊
4. ストリーミングバッファを参照
ストリーミングインサートで挿入された直後のデータ(まだバッファにある)を含む場合
🎯
5. DML文を実行
INSERT、UPDATE、DELETE、MERGEなどのDML文自体はキャッシュされない
🌐
6. 外部データソースを参照
Cloud Storage、Google Drive、Cloud Bigtableなどの外部テーブルを参照する場合
⚙️
7. キャッシュを明示的に無効化
クエリオプションでuse_query_cache=falseを設定した場合
-- キャッシュを使用しない SELECT * FROM dataset.table OPTIONS(use_query_cache=false);
📊
8. テーブルのメタデータが変更された
スキーマ変更、パーティション追加など、テーブルの構造が変わった場合

💰 キャッシュヒットによるコスト削減

✅ キャッシュヒット

$0.00
無料
データスキャン: 0 GB
応答時間: 数ミリ秒
同一クエリの再実行

❌ キャッシュミス

$5.00
課金対象
データスキャン: 1 TB
応答時間: 数秒〜数分
実際にデータをスキャン
($5/TB で計算)

💡 コスト削減の具体例

シナリオ: ダッシュボードで同じクエリを1日100回実行
スキャン量: 1クエリあたり500 GB
キャッシュなしの場合:
100回 × 500 GB = 50,000 GB = 50 TB
コスト: 50 TB × $5 = $250/日
キャッシュありの場合:
初回のみスキャン: 500 GB = 0.5 TB
残り99回はキャッシュ使用
コスト: 0.5 TB × $5 = $2.5/日
💰 節約額: $247.5/日 = $7,425/月 = $89,100/年

🎯 キャッシュの有効活用方法

✅ 推奨される使い方

  • クエリの標準化: チームでクエリのフォーマットを統一
  • 保存されたクエリの活用: 頻繁に使うクエリを保存
  • ダッシュボードツール: Looker、Data Studioなどが自動的にキャッシュを活用
  • 定期的なクエリ: スケジュールクエリで結果を事前生成
  • ビューの活用: 複雑なクエリをビューにラップ

❌ 避けるべき使い方

  • 動的なSQL生成: 毎回異なるフォーマットになる
  • 不要なコメント追加: コメントも含めてハッシュ化される
  • 大文字小文字の不統一: キャッシュミスの原因
  • CURRENT_TIMESTAMPの多用: キャッシュされない
  • 個別のクエリ作成: 似たようなクエリを毎回書く

実践的なテクニック

1️⃣ クエリの標準化

-- ❌ 避けるべき: フォーマットがバラバラ -- ユーザーA SELECT * FROM dataset.sales WHERE date='2024-11-25' -- ユーザーB select * from dataset.sales where date = '2024-11-25' -- ✅ 推奨: フォーマットを統一 -- 全員が同じフォーマットを使用 SELECT * FROM dataset.sales WHERE date = '2024-11-25'

2️⃣ 保存されたクエリの活用

BigQueryコンソールで「保存されたクエリ」機能を使用すると:
  • クエリテキストが完全に一致
  • チーム全体でキャッシュを共有
  • フォーマットの不一致を防止

3️⃣ パラメータ化されたクエリ

-- パラメータを使用して動的な値を設定 DECLARE target_date DATE DEFAULT '2024-11-25'; SELECT * FROM dataset.sales WHERE date = target_date; -- クエリの構造は同じなので、キャッシュの恩恵を受けやすい

4️⃣ 時刻関数の最適化

-- ❌ 避けるべき: 非決定的関数 SELECT * FROM dataset.sales WHERE date = CURRENT_DATE() -- キャッシュされない -- ✅ 推奨: 具体的な日付を指定 SELECT * FROM dataset.sales WHERE date = '2024-11-25' -- キャッシュ可能 -- または、パーティションフィルタを使用 SELECT * FROM dataset.sales WHERE _PARTITIONDATE = '2024-11-25'

📊 キャッシュの確認方法

クエリ実行後の確認

BigQueryコンソールでクエリ実行後、以下を確認できます:
✅ キャッシュヒットの場合:
"This query will process 0 B when run."
"Results cached"
料金: $0.00
❌ キャッシュミスの場合:
"This query will process 10.5 GB when run."
料金: $0.05 (estimated)

APIでの確認

# Python クライアントライブラリ from google.cloud import bigquery client = bigquery.Client() job = client.query(query_string) # ジョブの統計情報を確認 if job.cache_hit: print("✅ キャッシュヒット!") print(f"スキャンバイト数: {job.total_bytes_processed}") # 0 else: print("❌ キャッシュミス") print(f"スキャンバイト数: {job.total_bytes_processed}")

🔍 高度なキャッシュの仕組み

項目 詳細
キャッシュの保存場所 Google管理の分散キャッシュシステム(ユーザーからは不可視)
キャッシュのサイズ制限 128 MB(圧縮後)。超える場合は自動的にキャッシュされない
キャッシュの共有 同じプロジェクト内の全ユーザーで共有される
パーティション パーティションが異なれば別のクエリとして扱われる
リージョン データセットのロケーションごとに独立したキャッシュ
暗号化 キャッシュデータも暗号化されて保存される
⚠️ 制限事項:

📚 まとめ

🎓 BigQueryキャッシュの重要ポイント

🔑 キャッシュの保存単位:
  • キー: クエリテキストのハッシュ値
  • 値: クエリの実行結果全体(行データ + メタデータ)
  • 完全一致が必要: 1文字でも違うと別のキャッシュエントリ
💡 ベストプラクティス:
  • チーム内でクエリフォーマットを統一する
  • 頻繁に使うクエリは「保存されたクエリ」として登録
  • CURRENT_TIMESTAMP()などの非決定的関数は避ける
  • ダッシュボードツールを活用して自動的にキャッシュを利用
  • 大文字小文字、空白、コメントを統一する
適切にキャッシュを活用することで、
コストを最大99%削減し、クエリ応答を100倍高速化できます!