BigQuery ハンズオン教科書|2026-08 版
第 12 章

ストリーミングとリアルタイム分析

Storage Write API と Pub/Sub 直結でデータを秒で届け、continuous query で即応する

この章で学ぶこと

前提: 課金プロジェクト必須(ハンズオンは少量データで数円未満〜数円程度)。BigQuery サンドボックスはストリーミングデータの取り込みに対応していません。

リアルタイム取り込みの選択肢と選択マップ

第 3 章のバッチロードは「まとまった量のファイルを一括で読み込む」方式でした。これに対してストリーミングは、発生したデータを数秒〜数十秒のオーダーで BigQuery に届け続ける方式です。BigQuery でリアルタイム取り込みを実現する経路は大きく 4 つあり、どれを選ぶかはレイテンシ・変換要否・運用コストの 3 軸で決まります。

経路レイテンシ変換運用コスト配信保証
Storage Write API 直叩き最小(数百 ms 級)アプリ側で実装実装・監視は自前Committed ストリームで exactly-once
Pub/Sub 直結サブスクリプション数秒不可(整形は限定的)ほぼゼロ(マネージド)at-least-once のみ
Dataflow 経由秒〜分変換・集計・重複排除が可能パイプラインの運用が必要テンプレート次第で exactly-once
Datastream(CDC)秒〜分RDB スキーマの変換のみマネージド、監視は必要at-least-once(BigQuery 側でマージ)
実務の視点: 「コードを書かずにトピックからテーブルへ届けばよい」だけなら Pub/Sub 直結サブスクリプションで要件は満たせます。ウィンドウ集計・重複排除・複数ソースの結合・外部ルックアップによるエンリッチが必要になった瞬間に Dataflow が必要になる、と覚えておくと選定で迷いません。
RDBの変更を継続反映 したい? Datastream(CDC) はい いいえ 変換・集計・重複排除が必要? Dataflow はい いいえ 最小レイテンシで直接送りたい? Storage Write API はい いいえ Pub/Sub 直結 サブスクリプション
図 12-1 ストリーミング取り込みは、継続反映の要件・変換要否・レイテンシ優先度の順に問うと 4 経路へ自然に分岐する。

Storage Write API — ストリームの種類と exactly-once

Storage Write API(gRPC)は BigQuery への高スループットな書き込みを担う現行の中核 API です。ストリームには 4 種類あります。

exactly-once は Committed ストリームで、クライアントが行ごとに付与するオフセットをサーバー側の「次に期待するオフセット」と照合する仕組みで成立します。一致した場合だけ書き込みが確定してオフセットが進み、同じオフセットでの再送は重複として無視されるため、リトライしても二重挿入が起きません。

クライアント Storage Write API (Committed ストリーム) offset=N を送信 一致 不一致 書き込み成立 次オフセットへ 重複として無視 現在値を返す
図 12-2 Committed ストリームは、クライアントが指定するオフセットとサーバー側の期待値を照合し、一致した場合だけ書き込みを確定して exactly-once を実現する。

レガシーな streaming insert(tabledata.insertAll)は現在も REST エンドポイントとして存在しますが、2026-08 時点で本書執筆にあたり確認したところ、その解説ガイドページ(docs/streaming-data-into-bigquery)は write-api-rest(Storage Write API (REST))へ 301 リダイレクトされており、実質的に Storage Write API (REST) の文脈へ統合されています。公式ドキュメントは新規プロジェクトに対して「Storage Write API (REST) より Storage Write API (gRPC) を推奨」と明言しており、理由は料金の低さと exactly-once を含む堅牢な機能です。

実務の視点: 料金は Storage Write API (REST/旧 insertAll 相当) が挿入成功行に対する従量課金(基準額はおよそ 200 MiB あたり 0.01 USD、行は 1 KB 最小サイズで計算)で無料枠の記載なし。対して Storage Write API (gRPC) は 1 GiB あたりおよそ 0.025 USD で、月あたり最初の 2 TiB が無料です(2026-08 時点、米国リージョン基準。実額はリージョンで変動するため公式料金ページで確認してください)。新規実装で REST 版を選ぶ理由は基本的にありません。

書き込み直後のデータは「ストリーミングバッファ」に入ります。バッファ内のデータもクエリでは即座に参照できます。gRPC 版 Storage Write API で書き込んだ行なら、直近 30 分以内のものにも UPDATE・DELETE・MERGE などの DML を実行できます(この直近データへの DML 対応は REST 版で書き込んだデータには及ばず、マルチステートメントトランザクション内でも使えません)。バッファの中身はバックグラウンド処理で継続的にカラムナストレージへ移されます。

書き込みリクエスト Storage Write API ストリーミング バッファ カラムナ ストレージ 書込み 追記 移行 クエリ (SELECT) 参照 参照
図 12-3 書き込み直後のデータはストリーミングバッファに入り、バックグラウンド処理で継続的にカラムナストレージへ移行する。クエリはその間も両方を横断してほぼ即時に参照できる。

Storage Write API を Python から直接使う場合、既定ストリームへの最小構成はおおむね次のようになります(読み物として掲載。実行には事前に定義した Protocol Buffer 行スキーマが必要なため、本書では実装までは踏み込みません)。

from google.cloud import bigquery_storage_v1
from google.cloud.bigquery_storage_v1 import types, writer

write_client = bigquery_storage_v1.BigQueryWriteClient()
parent = write_client.table_path(PROJECT_ID, "handson_ch12", "pubsub_events")
stream_name = f"{parent}/_default"  # 既定ストリーム(at-least-once)

request_template = types.AppendRowsRequest()
request_template.write_stream = stream_name
append_rows_stream = writer.AppendRowsStream(write_client, request_template)

proto_rows = types.ProtoRows()
proto_rows.serialized_rows.append(row.SerializeToString())  # 事前定義した行スキーマ
request = types.AppendRowsRequest()
request.proto_rows = types.AppendRowsRequest.ProtoData(rows=proto_rows)
append_rows_stream.send(request)

Pub/Sub 直結・Datastream・continuous queries の使い分け

Pub/Sub BigQuery サブスクリプション

Pub/Sub のトピックに BigQuery サブスクリプションを直接アタッチすると、内部で Storage Write API (gRPC) を使って自動的にテーブルへ書き込まれます。コードは不要です。宛先テーブルの列は 2 通りの決め方があります。

--write-metadata を有効にすると、subscription_name(STRING)・message_id(STRING)・publish_time(TIMESTAMP)・attributes(STRING/JSON)が追加列として書き込まれます。配信保証は at-least-once のみで、Dataflow のように厳密な重複排除は行われません。

Pub/Sub 直結サブスクリプション パブリッシャー トピック BigQuery サブスクリプション BigQuery テーブル 発行 配信 自動書込み Dataflowを挟む経路 パブリッシャー トピック Dataflow (変換・集計) BigQuery テーブル 発行 配信 書込み
図 12-4 Pub/Sub BigQuery サブスクリプションはコード不要でテーブルへ直結するが変換はできない。変換・集計・重複排除が必要なら Dataflow を挟む。

Datastream — RDB からの CDC

Datastream はサーバーレスの CDC(変更データキャプチャ)サービスで、MySQL・PostgreSQL(AlloyDB を含む)・Oracle・SQL Server・MongoDB・Spanner に加え、Salesforce などのアプリケーションソースにも対応します。運用データベースの変更を最小限のレイテンシで BigQuery へレプリケーションし、ほぼリアルタイムの分析基盤を組めます。

continuous queries — BigQuery 内での常駐変換

continuous queries は SQL を常駐実行し続け、書き込まれたデータをリアルタイムに変換・出力する機能です。INSERT 文で BigQuery テーブルへ、EXPORT DATA 文で Pub/Sub・Bigtable・Spanner(リバース ETL)へ出力できます。コア機能(INSERT・EXPORT DATA・生成 AI 関数呼び出し)は GA ですが、JOIN・集計・ウィンドウ集計などのステートフル操作はプレビュー段階です。前提条件として BigQuery Enterprise または Enterprise Plus エディション(第 7 章参照)が必須で、オンデマンド課金では利用できません。専用の CONTINUOUS 割り当てタイプの予約が必要で、クエリあたり最低 10 スロット、対応する予約 1 つにつき最大 500 スロットの制限があります(増枠申請可)。ユーザーアカウントで実行するジョブは最大 2 日、サービスアカウントなら最大 150 日で停止します。

ソーステーブル (INSERTを検知) continuous query (常駐実行) 検知 BigQuery テーブル Pub/Sub Bigtable/Spanner INSERT EXPORT DATA リバースETL
図 12-5 continuous query はソーステーブルへの書き込みを常駐実行で検知し続け、変換結果を BigQuery テーブルへの INSERT・Pub/Sub や Bigtable/Spanner への EXPORT DATA のいずれかで出力する。

ハンズオン

ハンズオン 12-1|Pub/Sub トピックと BigQuery 直結サブスクリプションを作る

所要時間の目安は 10 分。課金プロジェクト必須(サンドボックスはストリーミング非対応)。作成するリソース自体に基本料金はかからず、後段でメッセージを数件送るだけなら実費は数円未満に収まります。ロケーションは US を使います。

  1. 受け皿となるデータセットとテーブルを作成する。--write-metadata を使うため data 列に加えてメタデータ列も用意します。
bq --location=US mk --dataset PROJECT_ID:handson_ch12

bq mk --table PROJECT_ID:handson_ch12.pubsub_events \
  subscription_name:STRING,message_id:STRING,publish_time:TIMESTAMP,data:STRING,attributes:STRING
  1. Pub/Sub トピックを作成する。
gcloud pubsub topics create ch12-events
  1. Pub/Sub のサービスエージェントに、宛先テーブルへ書き込むための BigQuery Data Editor ロールを付与する(プロジェクト番号は gcloud projects describe で確認できます)。
PROJECT_NUMBER=$(gcloud projects describe PROJECT_ID --format='value(projectNumber)')

gcloud projects add-iam-policy-binding PROJECT_ID \
  --member="serviceAccount:service-${PROJECT_NUMBER}@gcp-sa-pubsub.iam.gserviceaccount.com" \
  --role="roles/bigquery.dataEditor"
  1. BigQuery サブスクリプションを作成する(テーブルスキーマは使わず、メタデータ列を書き込む設定にします)。
gcloud pubsub subscriptions create ch12-events-to-bq \
  --topic=ch12-events \
  --bigquery-table=PROJECT_ID.handson_ch12.pubsub_events \
  --write-metadata

後片付け: このハンズオンで作ったリソースは 12-2 の最後でまとめて削除します。

ハンズオン 12-2|メッセージを送って BigQuery で確認する

所要時間の目安は 5 分。課金プロジェクト必須。数件のテスト送信であれば、Pub/Sub の BigQuery サブスクリプションのスループット課金(およそ 1 TiB あたり 50 USD、無料枠なし)を適用しても実費は 1 円未満に収まります。

  1. トピックへメッセージを 2〜3 件発行する。
gcloud pubsub topics publish ch12-events --message='{"event":"ping","seq":1}'
gcloud pubsub topics publish ch12-events --message='{"event":"ping","seq":2}'
  1. 数十秒待ってから BigQuery Studio のクエリエディタで確認する(処理: 数 KB 程度、無料枠内)。
SELECT message_id, publish_time, data
FROM `PROJECT_ID.handson_ch12.pubsub_events`
ORDER BY publish_time DESC
LIMIT 10;

数十秒〜1 分ほどで data 列に送信した JSON 文字列(例: {"event":"ping","seq":1})を含む行が現れれば成功です。すぐに出ない場合は Pub/Sub の配信遅延なので、少し待って再実行してください。

後片付け: サブスクリプション・トピック・データセットを削除します(IAM ロールの付与はプロジェクト単位の設定なので、他の用途でも使い回せるようそのままで問題ありません)。

gcloud pubsub subscriptions delete ch12-events-to-bq
gcloud pubsub topics delete ch12-events
bq rm -r -f -d PROJECT_ID:handson_ch12

つまずきポイント

サンドボックスでストリーミングを試して失敗する: BigQuery サンドボックスは公式ドキュメントで「Streaming data」を非対応機能として明記しています。ハンズオン前に必ず課金を有効化したプロジェクトへ切り替えてください。
直近データへの UPDATE/DELETE/MERGE が失敗する: gRPC 版 Storage Write API で書き込んだ直近 30 分以内の行への DML はサポートされていますが、REST 版で書き込んだ直近データへの変更系 DML と、マルチステートメントトランザクション内からの直近ストリーミングデータへの DML は非対応です。エラーになったら書き込み経路を確認し、急ぎでない変更は時間を置いてから実行してください。
メッセージがバックログに滞留し続ける: Pub/Sub のサービスエージェント(service-PROJECT_NUMBER@gcp-sa-pubsub.iam.gserviceaccount.com)に BigQuery Data Editor ロールを付与し忘れると、書き込み権限がなく配信が止まります。スキーマ不一致でも同様にバックログに残ります。
Pub/Sub 直結サブスクリプションは重複を許容する設計: 配信保証は at-least-once のみで exactly-once ではありません。重複が致命的な集計(金額の積み上げなど)では宛先テーブル側で重複排除するか、Storage Write API の Committed ストリームを検討してください。

理解度チェック

Q1. Storage Write API の Committed ストリームが exactly-once を実現する仕組みを説明してください。

クライアントが行ごとにオフセットを指定して送信し、サーバー側が「次に期待するオフセットと一致するか」を照合したうえで、一致した場合だけ書き込みを確定してオフセットを進める。同じオフセットでの再送は重複として無視されるため、リトライしても二重挿入が起きない。

Q2. レガシーの tabledata.insertAll は現行(2026-08 時点)のドキュメント上どう扱われているか?

独立した解説ページ(docs/streaming-data-into-bigquery)は Storage Write API (REST) のページへ 301 リダイレクトされており、実質的に Storage Write API (REST) の文脈へ統合されている。新規実装では料金・機能面で優れる Storage Write API (gRPC) が推奨される。

Q3.(シナリオ)オンプレミスの PostgreSQL の変更を、変換なしでほぼリアルタイムに BigQuery へ反映し続けたい。最も適した方法は?
A. Storage Write API を自前実装する B. Pub/Sub BigQuery サブスクリプション C. Datastream D. continuous queries

C。 Datastream は RDB の変更を CDC で検知し BigQuery へ継続的にレプリケーションするために作られたサーバーレスサービス。A・B はアプリ側でのイベント発行が前提で RDB の変更検知そのものは行わない。D は BigQuery 内のテーブルを起点にした常駐変換で、外部 RDB の直接 CDC には使わない。

Q4. Pub/Sub BigQuery サブスクリプションで、トピック/テーブルのスキーマを使わない場合に宛先テーブルへ必ず用意しなければならない列は何か?

data 列(型は BYTES・STRING・JSON のいずれか)。Pub/Sub はメッセージ本文をそのままこの列へ書き込む。メタデータ(subscription_name・message_id・publish_time・attributes)も受け取りたい場合は --write-metadata オプションを有効にする。

Q5.(シナリオ)課金プロジェクトで BigQuery Enterprise Plus エディションを契約済み。BigQuery のソーステーブルへの INSERT をトリガーに、リアルタイムで変換した結果を Spanner へ継続的に書き戻したい。最も適した機能は?
A. Dataflow のバッチパイプライン B. continuous queries C. Pub/Sub BigQuery サブスクリプション D. tabledata.insertAll

B。 continuous queries は BigQuery テーブルへの書き込みを常駐実行の SQL で検知し、EXPORT DATA 文で Spanner や Bigtable へのリバース ETL を行える。A はバッチであり継続的な常駐変換ではない。C・D はいずれも BigQuery 内部のテーブルを起点にした常駐変換という要件には合わない。

まとめと次章へ

次章の BigQuery ML では、ここまで整えたデータ基盤を土台に、SQL だけで機械学習モデルを構築します。