CloudBigtableの概要

このコードラボでは、あなたが使っへの紹介を受けるだろうのCloud BigtableをたJavaのHBaseクライアント

あなたは方法を学びます

  • スキーマ設計でよくある間違いを避ける
  • シーケンスファイルにデータをインポートする
  • データをクエリする

完了すると、ニューヨーク市のバスデータを示すいくつかのマップが表示されます。たとえば、マンハッタンでのバス旅行のヒートマップを作成します。

7349d94f7d41f1d1.png

Google Cloud Platformの使用経験をどのように評価しますか?

初心者中級熟練している

このチュートリアルをどのように使用しますか?

読むだけそれを読んで、演習を完了してください

ニューヨーク市のバスに関するデータセットを確認します。 300以上のバス路線とそれらの路線をたどる5,800台の車両があります。私たちのデータセットは、目的地名、車両ID、緯度、経度、到着予定時刻、到着予定時刻を含むログです。データセットは、2017年6月に約10分ごとに取得されたスナップショットで構成されています。

Cloud Bigtableから最高のパフォーマンスを得るには、スキーマ設計するときに慎重に行う必要があります。 Cloud Bigtableのデータは辞書式順序で自動的に並べ替えられるため、スキーマを適切に設計すると、関連データのクエリが非常に効率的になります。 Cloud Bigtableでは、連続する行のセットを返す行キーまたは行範囲スキャンによるポイントルックアップを使用したクエリが可能です。ただし、スキーマが十分に検討されていない場合は、複数の行ルックアップをつなぎ合わせたり、さらに悪いことに、非常に遅い操作である全表スキャンを実行したりする可能性があります。

クエリを計画する

私たちのデータにはさまざまな情報がありますが、このコードラボでは、バスの場所目的地を使用します

その情報を使用して、次のクエリを実行できます。

  • 特定の時間に1つのバスの場所を取得します。
  • バス路線または特定のバスの1日分のデータを取得します。
  • 地図上の長方形ですべてのバスを見つけます。
  • すべてのバスの現在の場所を取得します(このデータをリアルタイムで取り込んでいる場合)。

この一連のクエリは、すべてを最適に一緒に実行できるわけではありません。たとえば、時間で並べ替える場合、全表スキャンを実行せずに場所に基づいてスキャンを実行することはできません。最も一般的に実行するクエリに基づいて優先順位を付ける必要があります。

このコードラボでは、次の一連のクエリの最適化と実行に重点を置きます。

  • 1時間以上特定の車両の位置を取得します。
  • 1時間以上のバス路線全体の場所を取得します。
  • マンハッタンのすべてのバスの場所を1時間で取得します。
  • マンハッタンのすべてのバスの最新の場所を1時間で取得します。
  • 1か月間のバス路線全体の場所を取得します。
  • 1時間以上特定の目的地のバス路線全体の場所を取得します。

行キーを設計する

このコードラボでは、静的データセットを使用しますが、スケーラビリティーのためのスキーマを設計します。より多くのバスデータをテーブルにストリーミングし、それでも良好に機能することを可能にするスキーマを設計します。

行キーの提案されたスキーマは次のとおりです。

[バス会社/バス路線/タイムスタンプは時間に切り捨て/車両ID]。各行には1時間のデータがあり、各セルには複数のタイムスタンプ付きバージョンのデータが保持されます。

このコードラボでは、1つの列ファミリーを使用して物事を単純にします。これは、データがどのように見えるかの例のビューです。データは行キーでソートされます。

行キー

cf:VehicleLocation.Latitude

cf:VehicleLocation.Longitude

..。

MTA / M86-SBS / 1496275200000 / NYCT_5824

40.781212 @ 20:52:54.00 40.776163 @ 20:43:19.00 40.778714 @ 20:33:46.0​​0

-73.961942 @ 20:52:54.00 -73.946949 @ 20:43:19.00 -73.953731 @ 20:33:46.0​​0

..。

MTA / M86-SBS / 1496275200000 / NYCT_5840

40.780664 @ 20:13:51.00 40.788416 @ 20:03:40.00

-73.958357 @ 20:13:51.00 -73.976748 @ 20:03:40.00

..。

MTA / M86-SBS / 1496275200000 / NYCT_5867

40.780281 @ 20:51:45.00 40.779961 @ 20:43:15.00 40.788416 @ 20:33:44.00

-73.946890 @ 20:51:45.00 -73.959465 @ 20:43:15.00 -73.976748 @ 20:33:44.00

..。

..。

..。

..。

..。

次に、CloudBigtableテーブルを作成します。

まず、新しいプロジェクトを作成します。組み込みのCloudShellを使用します。これは、右上隅にある[Activate CloudShell]ボタンをクリックして開くことができます。

a74d156ca7862b28.png

次の環境変数を設定して、codelabコマンドのコピーと貼り付けを簡単にします。

INSTANCE_ID="bus-instance"
CLUSTER_ID="bus-cluster"
TABLE_ID="bus-data"
CLUSTER_NUM_NODES=3
CLUSTER_ZONE="us-central1-c"

Cloud Shellには、このコードラボで使用するツール、 gcloudコマンドラインツールcbtコマンドラインインターフェース、およびMavenがすでにインストールされています。

このコマンドを実行して、Cloud BigtableAPIを有効にします。

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

次の手順で、 gs://cloud-bigtable-public-datasets/bus-dataからこのコードラボのシーケンスファイルのセットをインポートします。

このコマンドを実行して、Cloud DataflowAPIを有効にします。

gcloud services enable dataflow.googleapis.com

次のコマンドを実行して、テーブルをインポートします。

NUM_WORKERS=$(expr 3 \* $CLUSTER_NUM_NODES)
gcloud beta dataflow jobs run import-bus-data-$(date +%s) \
--gcs-location gs://dataflow-templates/latest/GCS_SequenceFile_to_Cloud_Bigtable \
--num-workers=$NUM_WORKERS --max-workers=$NUM_WORKERS \
--parameters bigtableProject=$GOOGLE_CLOUD_PROJECT,bigtableInstanceId=$INSTANCE_ID,bigtableTableId=$TABLE_ID,sourcePattern=gs://cloud-bigtable-public-datasets/bus-data/*

インポートを監視する

Cloud DataflowUIでジョブを監視できます。また、 モニタリングUIを使用してCloudBigtableインスタンスの負荷を表示できます。インポート全体で5分かかるはずです。

git clone https://github.com/googlecodelabs/cbt-intro-java.git
cd cbt-intro-java

次のコマンドを実行して、Java11に変更します。

sudo update-java-alternatives -s java-1.11.0-openjdk-amd64 && export JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64/

実行する最初のクエリは、単純な行ルックアップです。 2017年6月1日の午前12:00から午前1:00までのM86-SBS線のバスのデータを取得します。その時、 NYCT_5824車両がバス路線にあります。

その情報を使用し、スキーマ設計(バス会社/バスライン/タイムスタンプを時間に切り捨て/車両ID)を知っていると、行キーは次のように推測できます。

MTA/M86-SBS/1496275200000/NYCT_5824

BusQueries.java

private static final byte[] COLUMN_FAMILY_NAME = Bytes.toBytes("cf");
private static final byte[] LAT_COLUMN_NAME = Bytes.toBytes("VehicleLocation.Latitude");
private static final byte[] LONG_COLUMN_NAME = Bytes.toBytes("VehicleLocation.Longitude");

String rowKey = "MTA/M86-SBS/1496275200000/NYCT_5824";
Result getResult =
    table.get(
        new Get(Bytes.toBytes(rowKey))
            .addColumn(COLUMN_FAMILY_NAME, LAT_COLUMN_NAME)
            .addColumn(COLUMN_FAMILY_NAME, LONG_COLUMN_NAME));

結果には、その時間内のバスの最新の場所が含まれている必要があります。ただし、すべての場所を表示する必要があるため、getリクエストでバージョンの最大数を設定します。

BusQueries.java

Result getResult =
    table.get(
        new Get(Bytes.toBytes(rowKey))
            .setMaxVersions(Integer.MAX_VALUE)
            .addColumn(COLUMN_FAMILY_NAME, LAT_COLUMN_NAME)
            .addColumn(COLUMN_FAMILY_NAME, LONG_COLUMN_NAME));

Cloud Shellで、次のコマンドを実行して、そのバスの1時間にわたる緯度と経度のリストを取得します。

mvn package exec:java -Dbigtable.projectID=$GOOGLE_CLOUD_PROJECT \
-Dbigtable.instanceID=$INSTANCE_ID -Dbigtable.table=$TABLE_ID \
-Dquery=lookupVehicleInGivenHour

緯度と経度をコピーしてMapMakerアプリに貼り付け、結果を視覚化できます。数層後、無料のアカウントを作成するように指示されます。アカウントを作成することも、既存のレイヤーを削除することもできます。このコードラボには、フォローしたいだけの場合に備えて、各ステップの視覚化が含まれています。この最初のクエリの結果は次のとおりです。

f1a1fac6051c6210.png

それでは、その時間のバス路線のすべてのデータを表示しましょう。スキャンコードはgetコードと非常によく似ています。スキャナーに開始位置を指定してから、タイムスタンプ1496275200000で示される1時間以内にM86-SBSバスラインの行のみが必要であることを示します。

BusQueries.java

Scan scan;
scan = new Scan();
scan.setMaxVersions(Integer.MAX_VALUE)
    .addColumn(COLUMN_FAMILY_NAME, LAT_COLUMN_NAME)
    .addColumn(COLUMN_FAMILY_NAME, LONG_COLUMN_NAME)
    .withStartRow(Bytes.toBytes("MTA/M86-SBS/1496275200000"))
    .setRowPrefixFilter(Bytes.toBytes("MTA/M86-SBS/1496275200000"));
ResultScanner scanner = table.getScanner(scan);

次のコマンドを実行して、結果を取得します。

mvn package exec:java -Dbigtable.projectID=$GOOGLE_CLOUD_PROJECT \
-Dbigtable.instanceID=$INSTANCE_ID -Dbigtable.table=$TABLE_ID \
-Dquery=scanBusLineInGivenHour

c18a4ac6522d08a2.png

マップメーカーアプリは一度に複数のリストを表示できるため、最初に実行したクエリからどのバスが車両であるかを確認できます。

234c1b51e3b201e.png

このクエリの興味深い変更は、M86-SBSバスラインの1か月分のデータを表示することです。これは、非常に簡単に実行できます。結果を取得するには、開始行とプレフィックスフィルターからタイムスタンプを削除します。

BusQueries.java

scan.withStartRow(Bytes.toBytes("MTA/M86-SBS/"))
    .setRowPrefixFilter(Bytes.toBytes("MTA/M86-SBS/"));

// Optionally, reduce the results to receive one version per column
// since there are so many data points.
scan.setMaxVersions(1);

次のコマンドを実行して、結果を取得します。 (結果の長いリストがあります。)

mvn package exec:java -Dbigtable.projectID=$GOOGLE_CLOUD_PROJECT \
-Dbigtable.instanceID=$INSTANCE_ID -Dbigtable.table=$TABLE_ID \
-Dquery=scanEntireBusLine

結果をMapMakerにコピーすると、バスルートのヒートマップを表示できます。オレンジ色のブロブは停車地を示し、真っ赤なブロブはルートの始点と終点です。

346f52e61b3d8902.png

次に、東に向かうバスと西に向かうバスをフィルタリングし、それぞれに個別のヒートマップを作成します。

BusQueries.java

Scan scan;
ResultScanner scanner;
scan = new Scan();
SingleColumnValueFilter valueFilter =
    new SingleColumnValueFilter(
        COLUMN_FAMILY_NAME,
        Bytes.toBytes("DestinationName"),
        CompareOp.EQUAL,
        Bytes.toBytes("Select Bus Service Yorkville East End AV"));

scan.setMaxVersions(1)
    .addColumn(COLUMN_FAMILY_NAME, LAT_COLUMN_NAME)
    .addColumn(COLUMN_FAMILY_NAME, LONG_COLUMN_NAME);
scan.withStartRow(Bytes.toBytes("MTA/M86-SBS/"))
    .setRowPrefixFilter(Bytes.toBytes("MTA/M86-SBS/"));
scan.setFilter(valueFilter);
scanner = table.getScanner(scan);

次のコマンドを実行して、東に向かうバスの結果を取得します。

mvn package exec:java -Dbigtable.projectID=$GOOGLE_CLOUD_PROJECT \
-Dbigtable.instanceID=$INSTANCE_ID -Dbigtable.table=$TABLE_ID \
-Dquery=filterBusesGoingEast

バスを西に移動するには、valueFilterの文字列を変更します。

BusQueries.java

SingleColumnValueFilter valueFilter =
    new SingleColumnValueFilter(
        COLUMN_FAMILY_NAME,
        Bytes.toBytes("DestinationName"),
        CompareOp.EQUAL,
        Bytes.toBytes("Select Bus Service Westside West End AV"));

次のコマンドを実行して、西に向かうバスの結果を取得します。

mvn package exec:java -Dbigtable.projectID=$GOOGLE_CLOUD_PROJECT \
-Dbigtable.instanceID=$INSTANCE_ID -Dbigtable.table=$TABLE_ID \
-Dquery=filterBusesGoingWest

東に向かうバス

76f6f62096a6847a.png

西に向かうバス

2b5771ee9046399f.png

2つのヒートマップを比較することで、ルートの違いを確認できるだけでなく、ペースの違いにも気付くことができます。データの解釈の1つは、西に向かうルートでは、特にセントラルパークに入るときにバスがより停止しているということです。そして、東に向かうバスでは、チョークポイントはあまり見られません。

最後のクエリでは、エリア内の多くのバス路線が気になる場合に対処します。

BusQueries.java

private static final String[] MANHATTAN_BUS_LINES = {"M1","M2","M3",...

Scan scan;
ResultScanner scanner;
List<RowRange> ranges = new ArrayList<>();
for (String busLine : MANHATTAN_BUS_LINES) {
  ranges.add(
      new RowRange(
          Bytes.toBytes("MTA/" + busLine + "/1496275200000"), true,
          Bytes.toBytes("MTA/" + busLine + "/1496275200001"), false));
}
Filter filter = new MultiRowRangeFilter(ranges);
scan = new Scan();
scan.setFilter(filter);
scan.setMaxVersions(Integer.MAX_VALUE)
    .addColumn(COLUMN_FAMILY_NAME, LAT_COLUMN_NAME)
    .addColumn(COLUMN_FAMILY_NAME, LONG_COLUMN_NAME);
scan.withStartRow(Bytes.toBytes("MTA/M")).setRowPrefixFilter(Bytes.toBytes("MTA/M"));
scanner = table.getScanner(scan);

次のコマンドを実行して、結果を取得します。

mvn package exec:java -Dbigtable.projectID=$GOOGLE_CLOUD_PROJECT \
-Dbigtable.instanceID=$INSTANCE_ID -Dbigtable.table=$TABLE_ID \
-Dquery=scanManhattanBusesInGivenHour

7349d94f7d41f1d1.png

料金を回避するためのクリーンアップ

このコードラボで使用されているリソースに対してGoogleCloud Platformアカウントに料金が発生しないようにするには、インスタンスを削除する必要があります。

gcloud bigtable instances delete $INSTANCE_ID

私たちがカバーしたこと

  • スキーマ設計
  • インスタンス、テーブル、およびファミリの設定
  • データフローを使用したシーケンスファイルのインポート
  • ルックアップ、スキャン、フィルターを使用したスキャン、およびマルチレンジスキャンを使用したクエリ

次のステップ