1. はじめに
この Codelab は、Apache Cassandra から Google Cloud Bigtable にクエリを移行する方を対象としたガイドです。
この Codelab では、
- Cloud Bigtable エミュレータを使用する
- 時系列のユースケースを確認する
- テーブルと列ファミリーを作成する
- CQL の Insert、Update、Select、Delete に相当する Cloud Bigtable Java の機能について学ぶ
Google Cloud Platform の使用経験をどのように評価されますか。
このチュートリアルの利用方法をお選びください。
<ph type="x-smartling-placeholder">2. 設定
ここでは、主要なコンセプトをすばやく理解できるように、わずか数行のサンプル データセットを表示します。
Cassandra
各ステップには同等の Cassandra クエリが存在するため、必要に応じてローカル クラスタに沿って操作するか、クリック デプロイ Cassandra クラスタをすばやく設定して SSH で接続します。
手順を進める場合は、キースペースを作成して使用します。ここではレプリケーション戦略は重要ではありません。
cqlsh> create keyspace mykeyspace with replication = {'class':'SimpleStrategy','replication_factor' : 2}; cqlsh> use mykeyspace;
Cloud Bigtable
テーブルには Cloud Bigtable インスタンスが必要です。エミュレータを使用すると、ローカル インスタンスを無料でセットアップできます。Cloud Bigtable でキースペースを作成する必要はありません。レプリケーションはインスタンス構成によって処理されます。
次のコマンドを使用して、エミュレータを起動します。
gcloud beta emulators bigtable start
次に、別のシェル ウィンドウまたはタブで、次のコマンドを使用してエミュレータの環境変数を設定します。
$(gcloud beta emulators bigtable env-init) #Sets BIGTAB`LE_EMULATOR_HOST
次に、コードサンプルの実行に使用する Java プロジェクトを作成し、Maven、Gradle、または SBT で Cloud Bigtable クライアントをインポートします。次に、新しい Java ファイルで、データ クライアントへの接続を作成します。
BigtableDataSettings settings =
BigtableDataSettings.newBuilder().setProjectId(projectId).setInstanceId(instanceId).build();
try {
dataClient = BigtableDataClient.create(settings);
} catch (Exception e) {
System.out.println("Error during data client connection: \n" + e.toString());
}
3. テーブルの作成
Cassandra テーブルと Cloud Bigtable テーブルの両方で、各行にキーが関連付けられています。Cassandra キーにはパーティション キーとクラスタリング列があり、これらは別々にすることも重複することも可能です。Cloud Bigtable のキー全体は、分割(パーティション)と順序付けに使用されます。これら 2 つのシステムは、主キー/行キーの構成という点では非常によく似ています。どちらのシステムも基本的には辞書順に並べられたリストであり、キーはノード間の行分散の主要な形式として機能します。ほとんどの場合、Cloud Bigtable では同じキーを再利用できます。
この Codelab では、Cloud Bigtable を使用してスマートフォンやタブレットに関する時系列データを保存します(Bigtable タブレットとは異なります)。テーブルを作成する手順は次のとおりです。
Cassandra
cqlsh:mykeyspace> create table mobileTimeSeries ( deviceid text, devicetype text, date date, connected_cell map<timestamp,Boolean>, os_build text, os_name text, PRIMARY KEY((devicetype, deviceid), date));
Cloud Bigtable
テーブルと列ファミリーは Java クライアントを使用して作成できますが、cbt ツールで次のコマンドを使用するのが最も簡単です。
cbt createtable mobile-time-series families=stats_summary
Cloud Bigtable は NoSQL データベースであるため、テーブルの作成時にスキーマを定義する必要はありませんが、実行するクエリを検討し、その行キーを最適化する必要があります。
キーの移行の最も一般的な方法は、すべてのパーティション キーとクラスタリング列を単純に結合して、Cloud Bigtable 行キーを表す文字列を形成することです。通常は、Key Visualizer を使用した鍵配布のデバッグに役立つ文字列ベースの鍵を使用することをおすすめします。ハッシュ「#」などの区切り文字を使用できます。行と列の間の関係を見ていきます。
この例では、行キー「[DEVICE_TYPE]#[DEVICE_ID]#[YYYYMMDD]」を使用します。
4. インサート
挿入は、Cassandra と Cloud Bigtable の間でよく似ています。ここでは、行キー「[DEVICE_TYPE]#[DEVICE_ID]#[YYYYMMDD]」を使用して行を 1 つ挿入してから、複数の行を挿入します。
Cassandra
シングル
cqlsh:mykeyspace> insert into mobileTimeSeries (deviceid, devicetype, date, connected_cell, os_build) values ('4c410523', 'phone',toDate(now()), {toTimeStamp(now()): true}, 'PQ2A.190405.003');
Batch
cqlsh:mykeyspace> BEGIN BATCH insert into mobileTimeSeries (deviceid, devicetype, date, os_name, os_build) values ('a0b81f74', 'tablet', '2019-01-01', 'chromeos', '12155.0.0-rc1'); insert into mobileTimeSeries (deviceid, devicetype, date, os_name, os_build) values ('a0b81f74', 'tablet', '2019-01-02','chromeos', '12145.0.0-rc6'); APPLY BATCH;
Cloud Bigtable
シングル
使用する行キーとデータでミューテーションを作成し、データ クライアントでミューテーションを適用します。スマートフォンのデータ行と、そのスマートフォンの接続とオペレーティング システムに関する情報を追加します。
try {
System.currentTimeMillis();
long timestamp = (long) 1556712000 * 1000; // Timestamp of June 1, 2019 12:00
String rowKey = "phone#4c410523#20190501";
ByteString one = ByteString.copyFrom(new byte[] {0, 0, 0, 0, 0, 0, 0, 1});
RowMutation rowMutation =
RowMutation.create(tableId, rowKey)
.setCell(
COLUMN_FAMILY_NAME,
ByteString.copyFrom("connected_cell".getBytes()),
timestamp,
one)
.setCell(COLUMN_FAMILY_NAME, "os_build", timestamp, "PQ2A.190405.003");
dataClient.mutateRow(rowMutation);
} catch (Exception e) {
System.out.println("Error during Write: \n" + e.toString());
}
Batch
batchMutation オブジェクトに複数のミューテーションを定義してから、データ クライアントを使用して 1 回の API 呼び出しですべてのミューテーションを適用します。モバイル タブレットのオペレーティング システムの名前とバージョンに関する数日分のデータを追加します。
try {
long timestamp = (long) 1556712000 * 1000; // Timestamp of June 1, 2019 12:00
BulkMutation bulkMutation =
BulkMutation.create(tableId)
.add(
"tablet#a0b81f74#20190501",
Mutation.create()
.setCell(COLUMN_FAMILY_NAME, "os_name", timestamp, "chromeos")
.setCell(COLUMN_FAMILY_NAME, "os_build", timestamp, "12155.0.0-rc1"))
.add(
"tablet#a0b81f74#20190502",
Mutation.create()
.setCell(COLUMN_FAMILY_NAME, "os_name", timestamp, "chromeos")
.setCell(COLUMN_FAMILY_NAME, "os_build", timestamp, "12155.0.0-rc6"));
dataClient.bulkMutateRows(bulkMutation);
} catch (Exception e) {
System.out.println("Error during WriteBatch: \n" + e.toString());
}
5. 更新情報
ここでは、まだ書き込まれていないセルを更新し、以前のバージョンを維持したままセルに新しい値を書き込みます。
Cassandra
セルの追加
cqlsh:mykeyspace> UPDATE mobileTimeSeries SET os_name = 'android' WHERE devicetype='phone' AND deviceid = '4c410523' AND date = '2019-09-06';
セルの更新
cqlsh:mykeyspace> UPDATE mobileTimeSeries SET connected_cell = connected_cell + {toTimeStamp(now()): false} WHERE devicetype='phone' AND deviceid = '4c410523' AND date = '2019-09-06';
Cloud Bigtable
Cloud Bigtable では、更新を書き込みと同じように扱うだけです。
セルの追加
これはセルの書き込みと同じですが、以前に書き込んだことのない列を指定します。ここでは、スマートフォンの行にオペレーティング システム名を追加します。
try {
long timestamp = (long) 1556713800 * 1000; // Timestamp of June 1, 2019 12:30
String rowKey = "phone#4c410523#20190501";
RowMutation rowMutation =
RowMutation.create(tableId, rowKey)
.setCell(COLUMN_FAMILY_NAME, "os_name", timestamp, "android");
dataClient.mutateRow(rowMutation);
} catch (Exception e) {
System.out.println("Error during update: \n" + e.toString());
}
セルを更新しています
ここでは、スマートフォンのモバイル接続状況に関する新しいデータを追加します。セルのバージョンを使用すると、時系列データの一部を簡単に保存できます。書き込みのタイムスタンプを指定するだけで、セルの新しいバージョンを追加できます。データをクリーンアップするには、ガベージ コレクションを使用して、一定数または一定時間が経過したバージョンを削除します。
try {
long timestamp = (long) 1556713800 * 1000; // Timestamp of June 1, 2019 12:30
String rowKey = "phone#4c410523#20190501";
ByteString zero = ByteString.copyFrom(new byte[] {0, 0, 0, 0, 0, 0, 0, 0});
RowMutation rowMutation =
RowMutation.create(tableId, rowKey)
.setCell(
COLUMN_FAMILY_NAME,
ByteString.copyFrom("connected_cell".getBytes()),
timestamp,
zero);
dataClient.mutateRow(rowMutation);
} catch (Exception e) {
System.out.println("Error during update2: \n" + e.toString());
}
6. 選択
次に、テーブルに書き込んだデータを取得します。CQL の SELECT ステートメントを移行するときは、列、WHERE 句によるフィルタ、group by などの制限および集計関数など、SELECT ステートメントのいくつかの側面を考慮する必要があります。ここでは、基本的な考え方を理解するために 2 つのシンプルな SELECT ステートメントを見ていきますが、選択の詳細についてはドキュメントをご覧ください。Cloud Bigtable には、Get と Scan という 2 種類の取得オペレーションがあります。Get は 1 行を取得し、Scan は行の範囲を取得します。
Cassandra
シングル
cqlsh:mykeyspace> SELECT * FROM mobileTimeSeries WHERE devicetype='phone' AND deviceid = '4c410523' AND date = '2019-09-04';
複数
cqlsh:mykeyspace> SELECT * FROM mobileTimeSeries WHERE devicetype='tablet' AND deviceid = 'a0b81f74' AND date >= '2019-09-04';
Cloud Bigtable
独身
行検索を使用すると、指定した日付における特定のスマートフォンのデータがすべて 1 行にまとめられます。これにより、タイムスタンプが付けられたバージョンの値が返されるため、connect_cell の 2 行でタイムスタンプの異なる値になったことを確認できます。
try {
String rowKey = "phone#4c410523#20190501";
Row row = dataClient.readRow(tableId, rowKey);
for (RowCell cell : row.getCells()) {
System.out.printf(
"Family: %s Qualifier: %s Value: %s Timestamp: %s%n",
cell.getFamily(),
cell.getQualifier().toStringUtf8(),
cell.getValue().toStringUtf8(),
cell.getTimestamp());
}
} catch (Exception e) {
System.out.println("Error during lookup: \n" + e.toString());
}
複数
レンジスキャンを使用すると、指定したモバイル タブレットの月別データが複数の行に分散して表示されます。フィルタを使って特定のバージョンのデータのみを取得したり、値でフィルタしたりできます。
try {
Query query = Query.create(tableId).range("tablet#a0b81f74#201905", "tablet#a0b81f74#201906");
ServerStream<Row> rowStream = dataClient.readRows(query);
for (Row row : rowStream) {
System.out.println("Row Key: " + row.getKey().toStringUtf8());
for (RowCell cell : row.getCells()) {
System.out.printf(
"Family: %s Qualifier: %s Value: %s Timestamp: %s%n",
cell.getFamily(),
cell.getQualifier().toStringUtf8(),
cell.getValue().toStringUtf8(),
cell.getTimestamp());
}
}
} catch (Exception e) {
System.out.println("Error during scan: \n" + e.toString());
}
7. 削除
ここで、テーブルに追加したデータを削除します。まず、個々の行を削除してから、複数の行を削除します。
Cassandra の CQL では、すべてのプライマリ列が指定されている場合に、単一行の削除と範囲の削除が可能です。Bigtable では、ある範囲をスキャンしてから行レベルの削除を実行することで、これを実現できます。結果は同じですが、各削除がそれぞれ独自のオペレーションとなるため、より多くのオペレーションが得られます。
Cassandra
シングル
cqlsh:mykeyspace> DELETE from mobileTimeSeries where devicetype='phone' and deviceid = '4c410523';
複数
cqlsh:mykeyspace> DELETE from mobileTimeSeries where devicetype='tablet' and deviceid = 'a0b81f74';
Cloud Bigtable
シングル
ここでは、特定のスマートフォンと日付のデータを削除します。行キーを使用して行を 1 つずつ削除します。
try {
String rowKey = "phone#4c410523#20190501";
RowMutation mutation = RowMutation.create(tableId, rowKey).deleteRow();
dataClient.mutateRow(mutation);
} catch (Exception e) {
System.out.println("Error during Delete: \n" + e.toString());
}
複数
ここで、特定のモバイル タブレットのデータをすべて削除します。複数の行を削除する CQL クエリを移行するには、スキャンを実行し、結果として得られる行キーのセットを使用して各行を削除する必要があります。
try {
Query query = Query.create(tableId).prefix("tablet#a0b81f7");
ServerStream<Row> rowStream = dataClient.readRows(query);
BulkMutation bulkMutation = BulkMutation.create(tableId);
for (Row row : rowStream) {
bulkMutation.add(row.getKey(), Mutation.create().deleteRow());
}
dataClient.bulkMutateRows(bulkMutation);
} catch (Exception e) {
System.out.println("Error during DeleteMultiple: \n" + e.toString());
}
8. 最終処理
クリーンアップ
Cassandra
この手順に沿う Cassandra クラスタを作成した場合は、通常どおりに自由に削除できます。
Cloud Bigtable
既存の Cloud Bigtable インスタンスにテーブルを作成した場合は、cbt コマンドを使用してテーブルを削除できます。
cbt deletetable mobile-time-series
エミュレータを使用した場合は、エミュレータを起動したターミナルで Ctrl+C キーを押すだけで、エミュレータを停止するだけですべての作業を消去できます。
次のステップ
- Cloud Bigtable の詳細については、ドキュメントをご覧ください。
- Cloud Bigtable Codelab の詳細を確認する
- GCP のその他の機能を試すには、チュートリアルをご覧ください。
- OpenTSDB インテグレーションを使用して時系列データをモニタリングする方法を学習する