1. はじめに
この Codelab では、Cloud Bigtable のモニタリング ツールを使用して、Cloud Dataflow と Java HBase クライアントでデータの書き込みと読み取りを行い、さまざまな芸術作品を作成します。
方法を学ぶ対象
- Cloud Dataflow を使用して大量のデータを Bigtable に読み込む
- データの取り込み時に Bigtable のインスタンスとテーブルをモニタリングする
- Dataflow ジョブを使用して Bigtable にクエリを実行する
- スキーマ設計に起因するホットスポットを見つけるために使用できる Key Visualizer ツールを確認する
- Key Visualizer を使用したアート作成

Cloud Bigtable の使用経験をどのように評価されますか。
このチュートリアルの利用方法をお選びください。
<ph type="x-smartling-placeholder">2. Bigtable データベースを作成する
Cloud Bigtable は、Google が提供する NoSQL ビッグデータ データベース サービスで、検索、アナリティクス、マップ、Gmail など、多数の Google 主要サービスを支えているのと同じデータベースを使用しています。大規模な分析ワークロードの実行や低レイテンシ アプリケーションの構築に最適です。詳細については、Cloud Bigtable Codelab の概要をご覧ください。
プロジェクトを作成する
まず、新しいプロジェクトを作成します。組み込みの Cloud Shell を使用する([Cloud Shell をアクティブにする] をクリックすると、ボタンをタップします。

次の環境変数を設定して、Codelab コマンドを簡単にコピーして貼り付けられるようにします。
BIGTABLE_PROJECT=$GOOGLE_CLOUD_PROJECT INSTANCE_ID="keyviz-art-instance" CLUSTER_ID="keyviz-art-cluster" TABLE_ID="art" CLUSTER_NUM_NODES=1 CLUSTER_ZONE="us-central1-c" # You can choose a zone closer to you
Cloud Shell には、この Codelab で使用するツールである gcloud コマンドライン ツール、cbt コマンドライン インターフェース、Maven がすでにインストールされています。
次のコマンドを実行して、Cloud Bigtable API を有効にします。
gcloud services enable bigtable.googleapis.com bigtableadmin.googleapis.com
次のコマンドを実行してインスタンスを作成します。
gcloud bigtable instances create $INSTANCE_ID \
--cluster=$CLUSTER_ID \
--cluster-zone=$CLUSTER_ZONE \
--cluster-num-nodes=$CLUSTER_NUM_NODES \
--display-name=$INSTANCE_ID
インスタンスを作成したら、cbt 構成ファイルにデータを入力し、次のコマンドを実行してテーブルと列ファミリーを作成します。
echo project = $GOOGLE_CLOUD_PROJECT > ~/.cbtrc echo instance = $INSTANCE_ID >> ~/.cbtrc cbt createtable $TABLE_ID cbt createfamily $TABLE_ID cf
3. 学習: Dataflow を使用した Bigtable への書き込み
文章作成の基本
Cloud Bigtable に書き込むには、CloudBigtableTableConfiguration 構成オブジェクトを指定する必要があります。このオブジェクトは、テーブルのプロジェクト ID とインスタンス ID、テーブル自体の名前を指定します。
CloudBigtableTableConfiguration bigtableTableConfig =
new CloudBigtableTableConfiguration.Builder()
.withProjectId(PROJECT_ID)
.withInstanceId(INSTANCE_ID)
.withTableId(TABLE_ID)
.build();
その後、パイプラインは HBase Mutation オブジェクト(Put と Delete を含む)を渡すことができます。
p.apply(Create.of("hello", "world"))
.apply(
ParDo.of(
new DoFn<String, Mutation>() {
@ProcessElement
public void processElement(@Element String rowkey, OutputReceiver<Mutation> out) {
long timestamp = System.currentTimeMillis();
Put row = new Put(Bytes.toBytes(rowkey));
row.addColumn(...);
out.output(row);
}
}))
.apply(CloudBigtableIO.writeToTable(bigtableTableConfig));
LoadData Dataflow ジョブ
次のページでは LoadData ジョブを実行する方法を説明しますが、ここではパイプラインの重要な部分について説明します。
データを生成するには、GenerateSequence クラス(for ループと同様)を使用して、数メガバイトのランダムなデータを含む複数の行を書き込むパイプラインを作成します。行キーは、パディングされて反転したシーケンス番号であるため、250 は 0000000052 になります。
LoadData.java
String numberFormat = "%0" + maxLength + "d";
p.apply(GenerateSequence.from(0).to(max))
.apply(
ParDo.of(
new DoFn<Long, Mutation>() {
@ProcessElement
public void processElement(@Element Long rowkey, OutputReceiver<Mutation> out) {
String paddedRowkey = String.format(numberFormat, rowkey);
// Reverse the rowkey for more efficient writing
String reversedRowkey = new StringBuilder(paddedRowkey).reverse().toString();
Put row = new Put(Bytes.toBytes(reversedRowkey));
// Generate random bytes
byte[] b = new byte[(int) rowSize];
new Random().nextBytes(b);
long timestamp = System.currentTimeMillis();
row.addColumn(Bytes.toBytes(COLUMN_FAMILY), Bytes.toBytes("C"), timestamp, b);
out.output(row);
}
}))
.apply(CloudBigtableIO.writeToTable(bigtableTableConfig));
4. Bigtable にデータを生成して流入をモニタリングする
次のコマンドは、テーブルに 40 GB のデータを生成する Dataflow ジョブを実行します。このデータ量は Key Visualizer のアクティブ化に十分な量です。
Cloud Dataflow API を有効にする
gcloud services enable dataflow.googleapis.com
GitHub からコードを取得し、ディレクトリに移動する
git clone https://github.com/GoogleCloudPlatform/java-docs-samples.git cd java-docs-samples/bigtable/beam/keyviz-art
データを生成する(スクリプトには 15 分ほどかかります)
mvn compile exec:java -Dexec.mainClass=keyviz.LoadData \ "-Dexec.args=--bigtableProjectId=$BIGTABLE_PROJECT \ --bigtableInstanceId=$INSTANCE_ID --runner=dataflow \ --bigtableTableId=$TABLE_ID --project=$GOOGLE_CLOUD_PROJECT"
インポートをモニタリングする
ジョブは Cloud Dataflow UI でモニタリングできます。また、Cloud Bigtable インスタンスの負荷をモニタリング UI で確認することもできます。
Dataflow UI では、ジョブグラフと、処理された要素、現在の vCPU、スループットなどのさまざまなジョブ指標を表示できます。


Bigtable には、インスタンス、クラスタ、テーブルのレベルで、読み取り/書き込みオペレーション、ストレージ使用量、エラー率などの標準モニタリング ツールがあります。さらに、Bigtable には Key Visualizer もあります。Key Visualizer は、30 GB 以上のデータが生成された時点で使用される行キーに基づいて使用量を分類するツールです。

5. 学習: Dataflow を使用した Bigtable からの読み取り
読み方の基本
Cloud Bigtable から読み取る場合は、CloudBigtableTableScanConfiguration 構成オブジェクトを指定する必要があります。これは CloudBigtableTableConfiguration と似ていますが、スキャンして読み取る行を指定できます。
Scan scan = new Scan();
scan.setCacheBlocks(false);
scan.setFilter(new FirstKeyOnlyFilter());
CloudBigtableScanConfiguration config =
new CloudBigtableScanConfiguration.Builder()
.withProjectId(options.getBigtableProjectId())
.withInstanceId(options.getBigtableInstanceId())
.withTableId(options.getBigtableTableId())
.withScan(scan)
.build();
次に、これを使用してパイプラインを開始します。
p.apply(Read.from(CloudBigtableIO.read(config)))
.apply(...
ただし、パイプラインの一部として読み取りを行う場合は、AbstractCloudBigtableTableDoFn を拡張する doFn に CloudBigtableTableConfiguration を渡すことができます。
p.apply(GenerateSequence.from(0).to(10))
.apply(ParDo.of(new ReadFromTableFn(bigtableTableConfig, options)));
次に、構成を指定して super() を呼び出し、getConnection() を呼び出して分散接続を取得します。
public static class ReadFromTableFn extends AbstractCloudBigtableTableDoFn<Long, Void> {
public ReadFromTableFn(CloudBigtableConfiguration config, ReadDataOptions readDataOptions) {
super(config);
}
@ProcessElement
public void processElement(PipelineOptions po) {
Table table = getConnection().getTable(TableName.valueOf(options.getBigtableTableId()));
ResultScanner imageData = table.getScanner(scan);
}
}
ReadData Dataflow ジョブ
この Codelab では、テーブルを 1 秒ごとに読み取る必要があるため、生成されたシーケンスでパイプラインを開始できます。このシーケンスにより、入力された CSV ファイルの時刻に基づいて複数の読み取り範囲がトリガーされます。
時間を考慮してスキャンする行範囲を決定するには少々計算がありますが、詳細を知りたい場合は、ファイル名をクリックしてソースコードを表示できます。
ReadData.java
p.apply(GenerateSequence.from(0).withRate(1, new Duration(1000)))
.apply(ParDo.of(new ReadFromTableFn(bigtableTableConfig, options)));
ReadData.java
public static class ReadFromTableFn extends AbstractCloudBigtableTableDoFn<Long, Void> {
List<List<Float>> imageData = new ArrayList<>();
String[] keys;
public ReadFromTableFn(CloudBigtableConfiguration config, ReadDataOptions readDataOptions) {
super(config);
keys = new String[Math.toIntExact(getNumRows(readDataOptions))];
downloadImageData(readDataOptions.getFilePath());
generateRowkeys(getNumRows(readDataOptions));
}
@ProcessElement
public void processElement(PipelineOptions po) {
// Determine which column will be drawn based on runtime of job.
long timestampDiff = System.currentTimeMillis() - START_TIME;
long minutes = (timestampDiff / 1000) / 60;
int timeOffsetIndex = Math.toIntExact(minutes / KEY_VIZ_WINDOW_MINUTES);
ReadDataOptions options = po.as(ReadDataOptions.class);
long count = 0;
List<RowRange> ranges = getRangesForTimeIndex(timeOffsetIndex, getNumRows(options));
if (ranges.size() == 0) {
return;
}
try {
// Scan with a filter that will only return the first key from each row. This filter is used
// to more efficiently perform row count operations.
Filter rangeFilters = new MultiRowRangeFilter(ranges);
FilterList firstKeyFilterWithRanges = new FilterList(
rangeFilters,
new FirstKeyOnlyFilter(),
new KeyOnlyFilter());
Scan scan =
new Scan()
.addFamily(Bytes.toBytes(COLUMN_FAMILY))
.setFilter(firstKeyFilterWithRanges);
Table table = getConnection().getTable(TableName.valueOf(options.getBigtableTableId()));
ResultScanner imageData = table.getScanner(scan);
} catch (Exception e) {
System.out.println("Error reading.");
e.printStackTrace();
}
}
/**
* Download the image data as a grid of weights and store them in a 2D array.
*/
private void downloadImageData(String artUrl) {
...
}
/**
* Generates an array with the rowkeys that were loaded into the specified Bigtable. This is
* used to create the correct intervals for scanning equal sections of rowkeys. Since Bigtable
* sorts keys lexicographically if we just used standard intervals, each section would have
* different sizes.
*/
private void generateRowkeys(long maxInput) {
...
}
/**
* Get the ranges to scan for the given time index.
*/
private List<RowRange> getRangesForTimeIndex(@Element Integer timeOffsetIndex, long maxInput) {
...
}
}
6. 作品作り

Bigtable にデータを読み込んで Dataflow で読み取る方法を理解したところで、最後のコマンドを実行してみましょう。このコマンドは、8 時間でモナリザの画像を生成します。
mvn compile exec:java -Dexec.mainClass=keyviz.ReadData \ "-Dexec.args=--bigtableProjectId=$BIGTABLE_PROJECT \ --bigtableInstanceId=$INSTANCE_ID --runner=dataflow \ --bigtableTableId=$TABLE_ID --project=$GOOGLE_CLOUD_PROJECT"
使用できる既存のイメージを含むバケットがあります。または、このツールを使用して独自の画像から入力ファイルを作成し、GCS の公開バケットにアップロードすることもできます。
ファイル名は gs://keyviz-art/[painting]_[hours]h.txt から作成されます。例: gs://keyviz-art/american_gothic_4h.txt
ペイント オプション:
- american_gothic
- mona_lisa
- pearl_earring
- persistence_of_memory
- starry_night
- sunday_afternoon
- the_scream
時間オプション: 1、4、8、12、24、48、72、96、120、144
allUsers にロール Storage Object Viewer を付与して、GCS バケットまたはファイルを公開します。

画像を選択したら、次のコマンドの --file-path パラメータを変更します。
mvn compile exec:java -Dexec.mainClass=keyviz.ReadData \ "-Dexec.args=--bigtableProjectId=$BIGTABLE_PROJECT \ --bigtableInstanceId=$INSTANCE_ID --runner=dataflow \ --bigtableTableId=$TABLE_ID --project=$GOOGLE_CLOUD_PROJECT \ --filePath=gs://keyviz-art/american_gothic_4h.txt"
7. 後で確認する
画像全体が表示されるまでに数時間かかることがありますが、30 分経過するとキー ビジュアライザにアクティビティが表示されるようになります。調整できるパラメータには、ズーム、明るさ、指標などがあります。ズームするには、マウスのスクロール ホイールを使用するか、キー ビジュアライザ グリッドの長方形をドラッグします。
明るさは画像のスケーリングを変更します。これは、非常に暑い部分を詳しく見る場合に役立ちます。

表示する指標を調整することもできます。OP、Read bytes client、Writes bytes client など、いくつかの種類があります。「Read bytes client」滑らかな画像が生成されているようですが、は、より多くの線を含む画像を生成します。一部の画像では見栄えが良くなります。

8. 完了
料金が発生しないようにクリーンアップする
この Codelab で使用したリソースについて、Google Cloud Platform アカウントに課金されないようにするには、インスタンスを削除する必要があります。
gcloud bigtable instances delete $INSTANCE_ID
学習した内容
- Dataflow を使用した Bigtable への書き込み
- Dataflow を使用した Bigtable からの読み取り(パイプラインの開始時、パイプラインの途中)
- Dataflow モニタリング ツールの使用
- Key Visualizer を含む Bigtable モニタリング ツールの使用
次のステップ
- 詳しくは、Key Visualizer アートの作成方法をご覧ください。
- Cloud Bigtable の詳細については、ドキュメントをご覧ください。
- GCP のその他の機能を試すには、チュートリアルをご覧ください。