はじめに
この記事はNikkei Advent Calendar 2021の15日目の記事です。
はじめまして。Data Platform グループで Data Engineer をしている玉越と申します。普段は日経内製のリアルタイムアクセスログ解析基盤 Atlas の開発、運用を行なっています。
BigQuery の運用施策の話に入る前に、まずこの Atlas が何かというお話をします。
Atlas は、2016年頃に 「リアルタイムに」「全てのデータを」扱える、データの計測から活用までを担うプラットフォームを目指して、AWS 上に構築されたリアルタイムアクセスログ解析基盤です。
この Atlas の導入(とその活用を進める施策と多数の人の数多の努力)により、日経内でデータ活用の風土が育ち、データドリブンな組織文化が醸成されつつあります。
リアルタイムアクセスログ分析基盤をAWSに構築した話 (JAWS UG ビッグデータ支部)
冒頭で BigQuery と言っておきながらなぜ AWS と思われるかもしれません。実は当初 AWS の Redshift で Atlas のセルフサービス分析基盤を構築していましたが、昨年から今年にかけて Redshift から BigQuery に移行しました。
本記事では移行した BigQuery を対象とします。
BigQuery の利用方法
Atlas はアクセスログデータ(クリックストリームデータ)を用途ごとに複数種類のデータベースに格納しています。そのうちの一つのデータベースにおいて、サービス開発担当、マーケティング担当、データアナリスト、データサイエンティスト等の多数のユーザが自身でデータの集計や分析を実施できる、いわゆるセルフサービスなデータ集計/分析基盤を整えています。
上述した通り元々はこのセルフサービスなデータ集計/分析基盤として AWS の Redshift を活用していましたが、日々増加する大量のデータを扱うためのスケーラビリティ上の課題から BigQuery へ移行しました。 (BigQuery への移行の経緯や技術的詳細については別途公開しようと考えています。)
BigQuery は(間接的な利用を含めて)ほぼ全ての社員が利用しています。 アドホックなクエリが約2千件、バッチジョブが約1万件毎日実行されており、その活用は以下の通り多岐に渡ります。
- 日経電子版や日経ID事業の閲覧データや会員データの蓄積と分析
- 編集部門に提供しているダッシュボード用データ提供
- ユーザセグメントの作成
- 日経電子版上でのターゲティング広告配信
- 高度活用(レコンメンドやABテスト等)
...etc
安心・安全・便利・効率的なBigQuery の運用の実現のために実施していること
こうした活用がされる BigQuery はペタバイト規模の費用対効果に優れたフルマネージド型の分析データウェアハウスですが、 運用に当たってはいくつか気を付けるべき点があります。
特に日経においては多数のユーザが多種多様な目的でクエリを実行するため、
- すべての利用者が便利に安心してデータを利用できるような仕組みを整えること
- データガバナンスを確立すること
- 予測不可能なコストを削減しコンピューティングリソースを効率的に利用できるようにすること
が必要です。
1. Terraform によるデータマネジメント
BigQuery の管理はさまざまな方法で行うことができますが、日経では terraform を用いています。
BigQuery はデータセット、テーブル、ビュー、マテリアルビュー、ユーザ定義関数、権限等あらゆるものが API 経由で管理できるため、 terraform の GCP プロバイダーを利用して BigQuery に関する変更を管理、統制することができます。
terraform を利用することで BigQuery に関する変更を一元管理でき、管理されていない表やビュー定義の作成を防げます。後述する通り、変更は全て Github の PR ベースで行われるため、データガバナンスを効かせることができます。
1-1. データセット、テーブル、ビュー、マテリアルビュー、ユーザ定義関数の管理
日経では BigQuery にクリックストリームデータ以外にも種々のデータを格納して、利用者がデータマートを構築できるようにしています。 GCP のコンソールや API Client を用いて利用者が好き勝手にデータマートを作成すると統制が取れなくなるため、 基本的に全てのデータセット及びテーブル、ビュー、マテリアルビュー、ユーザ定義関数を terraform 経由で管理する運用方針としています。
ただし、データセット、テーブル、ビューの作成をすべてデータエンジニアが行うことは工数的に不可能であるため、利用者が設定ファイルを修正し、その修正に対するレビューを介して自動的にデプロイされる仕組みを整えています。

なお、terraform の記述言語である HCL を利用者にがっつりと書いてもらうのは非常にハードルが高いため、以下のように HCL の知識が少なくても (他の定義を真似して)簡単にリソース定義が書けるようにしてあります。
# データセットの定義
variable "datasets" {
default = {
test_ds1 = { description = "データセット1" },
test_ds2 = { description = "データセット2" },
}
}
# データセットの定義を元に BigQuery のデータセットのリソースを作成
# ここは管理者以外変更を加えない
resource "google_bigquery_dataset" "dataset" {
for_each = var.datasets
dataset_id = each.key
description = each.value.description
...
}
1-2. データセットのアクセス制御
セルフサービスの分析環境である BigQuery はあらゆる利用者があらゆる用途でクエリできかつそれが安全に行われるようにするために、 日経では以下の要件があります。
- 基本的にすべての IAM ユーザは各種データセットを READ できる
- ただし特定のデータセットは、private に設定して特定のユーザからしか READ できない
- 基本的にはテーブル作成、削除は terraform 経由でしか行えないようにする
上記要件を満たすために、日経では terraform にて 以下の指針で BigQuery のアクセス制御を管理しています。
- プロジェクトレベルの BigQuery 権限を付与しない
- リソースレベル(データセット)に対してアクセス制御の設定を行う
- private なデータセットは明示的に設定されたユーザのアクセスしか許容しない
- BigQuery 基本ロール(READER, WRITER, OWNER) から、データセット/テーブルの作成/削除権限を剥奪したカスタムロールを基本的に割り当てる
# データセットのアクセス制御
variable "bigquery_dataset_access_control" {
default = {
test_ds01 = {
"hogehoge@xxxx.iam.gserviceaccount.com" = "WRITER"
}
}
}
resource "google_bigquery_dataset" "dataset" {
for_each = var.datasets
...
dynamic "access" {
for_each = var.bigquery_dataset_access_control[each.key]
content {
role = access.value
user_by_email = access.key
}
}
}
2. ドキュメントの整備
terraform で BigQuery 関連のリソースを管理することで、データセットやテーブルのメタデータ収集も容易になります。 データセットの description やテーブルの列定義等のメタデータはすべて GCP の Data Catalog に収集されるため、 BigQuery 利用者に対して GCP の Data Catalog API を利用したメタデータの情報提供を社内 web サイトを通じて行なっています。

また、メタデータだけでなく、BigQuery を含めた Atlas の利用ガイドについても併せてドキュメントを提供し、初めての利用者でも簡単に安心してクエリが実行できるような情報提供を行っています。

3. Slack Workflow + Notion を活用したテクニカルサポート体制の構築
利用者が使いやすいようにドキュメントを整備しているものの、それだけでは十分でなく、利用者からデータ定義の内容に関する質問や特殊なデータマートの追加等データチームのサポートを求めることが多々あります。こうしたケースにデータチームにユーザから直接依頼を引き受けた場合、依頼の内容や依頼背景が適切に伝えられずにコミュニケーションコストが発生したり、特定の人に依頼が集中して対応が属人化してしまう可能性があります。
そのため、データチームでは以下のように、Slack のワークフロー経由でサポートチケットを Notion のボードに自動起票し、対応したチケットを蓄積するフローを構築しています。

こうすることで依頼の窓口を統一し、依頼の記載内容の抜け漏れをなくし、コミュニケーションコスト/タスク管理のコストの削減、属人化の排除が実現できています。 また、対応済みチケットを異なる Notion の異なるビューで一覧化して検索可能にすることで、ユーザが自己解決可能な仕組みも整えています。
4. コストパフォーマンスの高い計算リソースの制御
ドキュメントやテクニカルサポートにより利用者にとって安全で便利な仕組みは整えられますが、多数のユーザが多種多様なクエリを発行している状況でクラウド特有の従量課金を利用するとリソースをたくさん消費する重いクエリによって予期せぬコストが発生する可能性があります。 また、重いクエリによってリソースが占有され続けると、軽いクエリを実行したいユーザがいつまでも待たせることになります。そして、バッチ処理で BigQuery を利用している場合、リソースが足りずに予定時間にバッチが終わらない、いわゆるバッチの突き抜けが発生してしまうかもしれません。
このような予測できないコストの変動や効率的にリソースを使えないことによって発生する種々の問題を防ぐために以下の施策を実施しています。
4-1. BigQuery Reservations によるリソース割り当て
BigQuery Reservations でコミットメントを購入することによって従量課金から定額料金にしています。これにより予測できないコストの変動を抑えます。 コミットメントするスロットの数や期間については、BigQuery のリソースグラフ を参照しながら決定しています。
4-2. 重いクエリの自動キャンセル
Reservations によってコストの予測可能性は高まりますが、今度はコミットメントされたスロット数にリソースが制限されるため、(何ヶ月ものアクセスログを一度にフルスキャンするような)重いクエリが実行される場合、そのクエリによって多数のクエリが待たされることになってしまいます。 そこで、定期的の実行されているジョブをチェックして重いクエリを排除する仕組みを導入しています。
具体的には Cloud Composer (Airflow) で条件に合致するクエリをキャンセルし、キャンセルしたクエリの内容について Slack に投稿する DAG を実行しています。
その(AND) 条件は以下の通りです。
- job の type が query
- INTERACTIVE モードで実行されている
- 60 分以上実行し続けている
- コミットメントしているスロットをフルに使ったとしても 5分以上時間がかかると想定される

4-3. Flex Slot による一時的なリソース増強
重いクエリの自動キャンセルだけでは、突発的なワークロードの増加に対応したり、バッチの突き抜けを回避することはできません。 そこで、Flex Slot を活用して一時的にリソースを増強する仕組みを導入しています。
こちらも重いクエリの自動キャンセルと同じように、Cloud Composer で条件に合致する場合に Flex Slot を購入してリソース増強する (逆にリソースが足りている場合には Flex Slot をキャンセルしてリソースを元に戻す) DAG を定期実行しています。
その(AND) 条件は以下の通りです。
- job の type が query
- BATCH モードで実行されている
- 60 分以上実行し続けている
- 計算リソースが割り当てられていない

5. 重複排除の仕組み
コストパフォーマンスの高い計算リソースの制御により効率的に BigQuery を利用できていますが、さらにもう一段階効率を高めるためにクリックストリームデータの格納に一工夫しています。
Atlas によって BigQuery にアクセスログが送られてくるデータ連携の図を簡略化したものが以下です。

上記の図中に存在する Amazon SQS や Amazon Kinesis Data Stream では伝送保証が at-least-once であるため(*)、最終的に BigQuery にデータが格納されるタイミングでデータが重複している可能性があります。
(*) Amazon SQS では FIFO キューを使用すると at-least-once の伝送保証が実現できますが、現在の Atlas では利用していません。
そのため、何らかの方法でデータ重複を排除する必要があります。データ重複を排除する場合の大まかな方法としては以下の2通りがあります。
方法 | メリット | デメリット |
---|---|---|
1. データ挿入時に重複を排除する | クエリ時の負荷が低い | データ挿入時の負荷が高い(挿入時により多数のスキャンとソートを行う必要があるため) |
2. クエリ時に重複を排除する | データ挿入の負荷が低い | クエリ時の負荷が高い(クエリ時により多数のスキャンとソートを行う必要があるため) |
現在データチームで採用している方式は上記の方法 1,2 の折衷案です。以下のようにして重複排除を実現しています。
- t_latest 表に重複ありの状態でデータを格納し、定期的にデータを挿入する
- スケジュールクエリによって 15分間に一度 t_latest 表を重複を排除しながら t_accum 表にデータを挿入する
- t_accum 表をラップした v_accum 表をユーザに見せることで、重複排除を意識せずにクエリを実行できる

この重複排除の仕組みにより、ユーザが重複排除をクエリ時に実行することなく、またデータ挿入時の負荷も限定的にすることができ、効率的に BigQuery のリソースを活用することができています。
今後の改善点
上記で説明した BigQuery に関する種々の施策だけで十分かというとそうではなく、より安全に安心に便利に効率的に BigQuery を活用していくために、以下のような仕組みの導入にチャレンジしていこうと考えています。
- Storage Write API を利用した Stream Insert
- dbt によるデータ連携
- DLP API による個人情報の自動検出
おわりに
長くなりましたが、日経における BigQuery の安心・安全・便利・効率的な運用の実現のために整えた仕組みについて紹介させていただきました。
ここで紹介した今後の改善点を含めて、やりたいことがまだまだたくさんあります。BigQuery を含めた Atlas のさらなる機能開発や大規模データを扱う仕組みに興味がある人はぜひぜひ気軽にご連絡をください。