Bigtableを徹底解説!

記事タイトルとURLをコピーする

当記事は みずほリサーチ&テクノロジーズ × G-gen エンジニアコラボレーション企画 で執筆されたものです。


G-gen の片岩です。当記事では Google Cloud のデータベースサービスである Bigtable を徹底解説します。ビジネスにおいてデータ活用が重要なことは改めて記載するまでもありません。大量のデータを高速に処理でき、スケーラビリティのある Bigtable は、より効率的かつ正確なビジネス上の意思決定に貢献できそうです。また、高度で詳細な監査要件の求められる金融機関のシステムにおいてはログの蓄積・解析などでの利用も考えられそうです。

基本事項

Cloud Bigtable とは

Cloud Bigtable (以降、Bigtable) は NoSQL ビッグデータ向けのフルマネージドなデータベースサービスです。

特徴はなんと言っても 低レイテンシかつ高スループット であることで、膨大なデータをリアルタイムで処理する ことが可能です。 Bigtable は Google 検索、Google Analytics、Google マップ、Gmail など、Google の主要サービスを支えているサービスでもあります。

Bigtable は NoSQL データベースであり、行 (Key) と列 (Value) で構成される Key-Value マップにデータを格納します。行と列が存在しますが、リレーショナルデータベース (RDB) と異なり、使用していない領域はストレージを消費しないスパース (低密度) な構造となっています。またダウンタイムなしでクラスタサイズを変更でき、スケーラビリティに優れています。

Bigtable のデータへは、API 経由でアクセスします。Go, Java, Python, PHP, Ruby, C# など各言語用のクライアントライブラリが用意されている他、Apache HBase (オープンソースの列指向・分散データベース) 互換の API も用意されています。

ユースケース

Bigtable は大量データのリアルタイム処理で真価を発揮 します。 前述の Google のサービスで使われているほか、大量のログや IoT デバイスから送信されるデータを取り扱うケース等で採用されています。

料金

概要

Bigtable では以下の料金が発生します。

  • コンピューティング料金
  • ストレージ料金 (バックアップ含む)
  • 通信料金

料金表は 公式ページ を参照ください。

コンピューティング料金

コンピュート処理能力はノード (後述) 単位で最低1時間の課金が発生します。たとえば1台のノードが100分間稼働した場合は2時間分の料金が発生します。

また、実際にリクエストを処理していなくても課金が発生するため、リクエストが少ない時間帯はノード数を減らすことで料金を節約できます。

ストレージ料金

利用したストレージ (テーブルとバックアップ) 分だけ支払いが発生する従量課金です。設置するリージョンやディスク (SSD / HDD) に応じて単価が異なります。

Bigtable はコンパクションと呼ばれる自動圧縮処理を定期的に実行しており、課金はこの圧縮後のデータサイズに対して計算されます。

また複数のクラスタ (後述) を含むインスタンス (後述) の場合、クラスタごとにデータのコピーを保持するため、その分の課金が発生します。

ネットワーク料金

データの書き込み (Bigtable に入っていく方向) は無料です。

データの読み込み (Bigtable から出ていく方向) は同一リージョン内の通信は無料ですが、異なるリージョン間の通信やインターネットへ向けた通信には料金が掛かります。

計算例

参考までに料金の計算例を記載します (単価は2023年7月時点のもの)。

  • 東京リージョンで 1 ヶ月を通して 1 ノードのコンピュートリソースを使用
  • 平均 50 GB のデータを SSD ドライブに保存
  • 東京リージョンへの 50GB のネットワーク下り通信
課金要素 数量
コンピューティング料金 1ノード * 30 days * 24h * $0.85 $612.00
ストレージ料金 (SSD) 50GB * $0.22 $11.00
ネットワーク料金 同一リージョン間通信のため無料 $0.00
- 合計 $623

データの操作

概要

一般的な RDB データの操作 (読取・書込等) には SQL を利用しますが、Bigtable は Web API を用います。

といっても直接 Web API への HTTP リクエストを行うことは稀です。実際には cbt という CLI ツールや、Python、Java、Go、PHP といった各言語に用意されたクライアントライブラリなど、Web API をラップしたツールを用いて操作するのが一般的です。

クライアントライブラリ

例としてここでは Python のクライアントライブラリを用いて Bigtable にデータを登録するサンプルコードを紹介します。

greetings = ["Hello World!", "Hello Cloud Bigtable!", "Hello Python!"]
rows = []
column = "greeting".encode()
for i, value in enumerate(greetings):
    row_key = "greeting{}".format(i).encode()
    row = table.direct_row(row_key)
    row.set_cell(
        column_family_id, column, value, timestamp=datetime.datetime.utcnow()
    )
    rows.append(row)
table.mutate_rows(rows)

row.set_cell(column_family_id, column, value, timestamp) にてテーブルにデータを追加しています。

このように Bigtable では行キー (row_key)、列名 (column)、値 (value) 等を設定してデータを登録します。

cbt CLI

cbt CLI は Bigtable の操作を実行するためのツールです。Cloud Shell やローカル開発環境にインストールして Bigtable を操作することができます。

my-table という名前のテーブルは以下のコマンドで作成できます。

cbt createtable my-table

my-table からデータを読み取るコマンドは以下になります。

cbt read my-table

他の Google Cloud サービス

BigQuery (データウェアハウスサービス) から Bigtable を外部テーブルとして定義することで、Bigtable のデータを BigQuery にコピーしなくても、BigQuery から直接クエリを発行することができます。

また Dataflow (Apache Beam のマネージドサービス) から Bigtable に接続するためのコネクタが用意されており、Dataflow パイプラインの中から Bigtable のデータへアクセスすることが可能です。

コンポーネント

全体像

Bigtable のコンポーネント構成の全体像は、以下のとおりです。

全体像

インスタンス

インスタンス は Bigtable の最も基本的な管理単位です。

インスタンスは複数のクラスタとストレージを含み、テーブルもインスタンスに所属します。テーブルは後述のクラスタやノードに所属するのではなく、インスタンスに所属し、各クラスタにレプリケーションされます。

クラスタ

クラスタ はノード (個々のサーバ) をグルーピングした概念です。一つのインスタンスに所属し、特定のゾーンに存在します。アプリケーションがインスタンスにリクエストを送信すると、いずれかのクラスタが処理します。

なお Bigtable ではコンピューティングとストレージが分離されているため、クラスタにはストレージが含まれません。

たとえば東京リージョン (内のとあるゾーン) と大阪リージョン (同) にクラスタを展開すると

  • ①最寄りのクラスタでリクエストを処理するためレイテンシを低下できる
  • ②東京リージョンで障害が発生しても大阪リージョンでサービスを継続できる

といったことが可能になります。

ノード

ノードはクラスタを構成するサーバのイメージです。ノードを増やすことでクラスタの処理能力を向上できます。

CPU 使用率やストレージ使用率に応じて自動的にノードを追加することも可能です。

自動スケーリング

ストレージ

ストレージはデータが保存される領域です。クラスタに所属します。

複数クラスタとレプリケーション

概要

Bigtable インスタンスの中には、複数のクラスタを配置できます。クラスタ間でストレージはレプリケーションされ、データの可用性と耐久性を向上させます。

Bigtable インスタンスは、Google Cloud リージョンのうち最大 8 つのリージョンにクラスタを配置できます。またリージョン内はゾーンで分かれていますが、一つのゾーンに配置できるクラスタは 1 つのみです。

あるリージョン内で複数のゾーンにクラスタを配置することもできますし、別々のリージョンにクラスタを配置することもできます。

レイテンシと整合性

クラスタ間のレプリケーションは非同期であり、レイテンシがあること、また結果整合性であることに注意が必要です。

レプリケーションのレイテンシがどのくらいあるかについては一概には言えませんが、通常は数秒〜数分の間であり、数時間に達することはありません。

ただし、書込後の読取に整合性を持たせることも可能です。後述のアプリプロファイルにて「単一クラスタのルーティング」を選択した場合のみ、単一行レベルでの強整合性を確保することが可能です。

複数クラスタのユースケース

可用性向上

複数クラスタを別々のゾーンに配置することで高い可用性を担保できます。自動フェイルオーバさせることも可能です。

オンラインとバッチの分離

複数クラスタを用意することで、業務アプリケーションと分析目的のジョブのワークロードを分離することができます。

分離には、後述のアプリプロファイルを利用できます。

グローバルなレイテンシ短縮

世界中にアプリケーションの利用者がいる場合、利用者に近い地域のリージョンにクラスタを作成することで、読取レイテンシの低減に繋がります。

バックアップ・リストア

Bigtable では任意の時点のバックアップを取得することができます。

Bigtable のバックアップはテーブルバックアップであり、指定したテーブルのスキーマとデータを保持します。Compute Engine のスナップショット等は異なり、インスタンス (あるいはクラスタやノード) をまるごとバックアップするものではありません。

バックアップからの復元時は、任意の既存インスタンスを選択してその中にテーブルをリストアできます。

バックアップからのリストアは、オペレーションミスやアプリケーションによりデータが破壊された場合などに加え、例えば本番環境テーブルからステージング環境テーブルを複製して作成する、などの用途にも使えます。

バックアップの復元

内部構造

ストレージモデル

まずは Bigtable に登録されたデータがどのように保管されるのか、ストレージモデルを見ていきます。ストレージモデルは後述のスキーマ設計に大きく関わってきます。

画像は 公式ドキュメント から引用

  • 行 (Key) と列 (Value) で構成される Key-Value マップにデータを格納します。
  • 各行は一意の行キーを持っています。
  • 相互に関連する列を列ファミリーとしてグループ化できます。
  • 行と列が交差する場所には複数のセルを含むことができます。
  • 使用していない列はデータの保存領域を消費しません。

インフラ・アーキテクチャ

Bigtable はデータを保管するストレージと、クエリを実行するコンピューティングリソース(ノード)が分離した構成になっています。

これにより大量データの保管と高速なクエリを実現しています。

実際にクエリが処理される流れを見ていきます。

アーキテクチャ

  • クライアント リクエストは Bigtable クラスターに送信されます。
  • Bigtable クラスターは複数のノードで構成されており、リクエストが各ノードに割り当てられます。(図の場合、行キーが D から始まるクエリを処理する場合はノード 1 が割り当てられます)
  • ノードはそれぞれ独立してリクエストを処理します。ノード追加によりパフォーマンスを向上できます。
  • 実データは辞書順に並び替えられ、ストレージに保管されています。

なお、上図は各アルファベットから始まるデータが均等な場合のイメージです。実際には各ノードの負荷が均等になるようにノードとデータは関連付けられます。

スキーマ設計

スキーマ設計のポイント

データベースのパフォーマンスを最大限発揮できるようなスキーマ設計は重要です。 従量課金のクラウドサービスでは過大な課金につながる可能性もあります。 アーキテクチャやストレージの仕組みのポイントをおさらいします。

  • Key-Value ストアであること
    • テーブル結合は利用できません。
    • トランザクションは 1 つの行内でのみ完結します。複数行にまたがるトランザクションは利用できません。
  • 行の特徴
    • 各行キーは一意である必要があります。
    • 行は、行キーのビッグエンディアン順(バイナリのアルファベット順に相当する)に並べ替えられます。
    • 各テーブルのインデックス(行キー)は 1 つのみです。二次インデックスはありません。
  • 列の特徴
    • 列ファミリーは特定の順序では保存されません。
    • 列は、列ファミリー別にグループ化され、列ファミリー内で辞書順に並べ替えられます。
  • 読み取りと書き込みは(テーブルの行スペース全体に)均等に分散されるのが理想
  • Bigtable で使用していない列は空になり、保存領域を消費しない

スキーマ設計のベストプラクティス

上記で整理した内容をスキーマ設計の観点に読み替えると、以下の 2 点が挙げられます。

  • データは結合不要な形で保管する。非正規化して1つのテーブルにまとめる
  • 各ノードに均等にリクエストが割り当てられるようにする。時刻やシーケンス番号など、辞書順で並べた際に偏りのある情報は行キーの先頭では使用しない

また、辞書順で偏らないためにハッシュ化した値を利用する方法もありそうですが、これはアンチパターンです。トラブルシューティングをする際、読解不可能な文字列では支障があるため、行キーには読解可能な文字列を使用します。

詳細はスキーマ設計の ベストプラクティス をご参照ください。

スキーマ設計例

時系列データのスキーマ設計例をみてみましょう。 気象バルーンが 1 分ごとに測定した圧力等のデータを Bigtable に保存することを想定します。

スキーマ設計に画一的な正解はなく、いくつかパターンがあります。 自身のケースに置き換えてみて、適切なパターンを選択することが重要です。

測定するごとに行を追加するパターン

1分ごとに新しい行を登録するパターンです。 シンプルで開発が容易 なことが特徴です。

このパターンで書き込まれる例を示します。 行キーには1分ごとの日時を示す文字列が含まれます。

行キー 圧力 温度 湿度 標高
us-west2#3698#2023-06-05-1200 94558 9.6 61 612
us-west2#3698#2023-06-05-1201 94122 9.7 62 611
us-west2#3698#2023-06-05-1202 95992 9.5 58 602
us-west2#3698#2023-06-05-1203 96025 9.5 66 598
us-west2#3698#2023-06-05-1204 96021 9.6 63 624

測定するごとに列を追加するパターン

1分ごとに列を追加するパターンです。 1行にたとえば1週間分のデータを格納します。 保存容量を節約できる ことが特徴です。

このパターンで書き込まれる例として、3分後の pressure (圧力) のデータを示します。 行キーには n 週目の圧力を示す文字列が含まれます。 value は測定値に対する測定日時が登録されたデータ構造になります。

行キー 94558 94122 95992
us-west2#3698#pressure#week1 t2023-06-05-1200 t2023-06-05-1201 t2023-06-05-1202

毎回測定値が異なると列が増えてしまいますが、 同じような測定値が繰り返される場合は、複数のセルにまとまって情報が登録されるため保存容量が節約されます。

測定するごとにセルを追加するパターン

1分ごとにセルを追加するパターンです。 1行にたとえば1週間分のデータを格納します。 測定値の経時変化を扱える ことが特徴です。

3分後の圧力列と温度列は次のようになります。 行キーには n 週目を示す文字列が含まれます。

行キー 圧力 温度
asia-south2#3698#week1 94558(t2023-06-05-1200) 9.5(t2023-06-05-1200)
94122(t2023-06-05-1201) 9.4(t2023-06-05-1201)
95992(t2023-06-05-1202) 9.2(t2023-06-05-1202)

1 行の読み取りで 1 週間分のデータを読み取るため、経時変化を扱う場合に有用です。温度だけ必要であれば、温度の列だけ選択することも可能です。

詳細は 公式ドキュメント をご参照ください。

アプリプロファイル

アプリプロファイル (app profiles) は、Bigtable がアプリケーションから受け取ったリクエストを処理する方法について定義した設定です。

アプリプロファイルは、複数のクラスタを使用するインスタンスで特に重要になります。トランザクションの整合性に関する設定や、クラスタへのルーティングの方法などを定義します。

例えば業務アプリケーションのトラフィックと、分析用のトラフィックに別々のアプリプロファイルを当てはめ、別々のクラスタへルーティングすることで、クラスタへの負荷を分散させることができます。

アプリケーションプロファイル

以下はアプリ側の サンプルコード です。赤字箇所でアプリケーションプロファイルを指定しています。

from google.cloud import bigtable

client = bigtable.Client(project=project_id)
instance = client.instance(instance_id)
table = bigtable.table.Table(table_id, instance, '[APP_PROFILE_ID]')

パフォーマンスに関する注意点

概要

データの操作時も Bigtable の内部構造を考慮する必要があります。Bigtable のパフォーマンスを十分に発揮させるには以下の点が重要です。

  • 辞書順でデータが格納されてノードが割り当てられることを考慮して、まとまった単位でデータを格納すること
  • 類似の情報を一括して読み取り・書き込みし、クエリの発行回数を抑える こと
  • 多数のクエリを発行する場合は、1つのノードに偏らない こと

書き込み

Bigtable では単一行を書き込む方法のほかに、複数行を同時に書き込むバッチ書き込みが利用できます。

隣接したデータを更新する場合は、バッチ書き込みを利用したほうがクエリの発行回数を抑えることができてパフォーマンスが良くなります。

読み取り

Bigtable では行キーの辞書順でデータが保管されているため、連続した複数行の読み取りは低レイテンシ です。

しかしランダムな複数行の読み取りはテーブル全体をスキャンすることになるため非効率です。

よく使用するクエリが低レイテンシで応答できるよう、同時に読み取ることの多いデータが近くなるようなスキーマ設計 を意識すると良いでしょう。
また、読み取る列を絞り込むことでパフォーマンスを改善する ことができます。

モニタリング

リソース使用状況の確認

Bigtable のコンソール画面や Cloud Monitoring のコンソール画面で CPU 使用率やディスクの使用量などを モニタリング することができます。

CPU 使用率やディスク使用量が増加傾向にあれば、必要に応じてノードを追加して負荷を分散させましょう。

Key Visualizer

Key Visualizer は Bigtable の使用状況の分析に役立つツールです。 テーブルの行全体がバランスよくアクセスされているかどうか等を確認することができます。

以下の画像は Key Visualizer スキャンの画面です。

横軸が時間で縦軸が行キーを表しており、色が明るいほど読み込みや書き込みなどの処理が実行されていることを示します。たとえば特定の行キーにのみ明るい色がついている場合、そこに負荷が集中していることが判ります。

均等分布や順次読み取り/書き込みの場合等、いくつかのパターンが こちら で紹介されています。

画像は公式ドキュメント から引用

片岩 裕貴 (記事一覧)

データアナリティクス準備室

2022年5月にG-gen にジョイン。
AI/ML系に関心が強く、ディープラーニングE資格とProfessional Machine Learningを取得。最近話題のGenerative AIにも興味がある。毎日の日課は三人乗りの電動自転車で子供を幼稚園に送り迎えすること。和歌山県在住。