Bigtable データに対するクエリを実行する

このドキュメントでは、Bigtable 外部テーブルに保存されているデータに対してクエリを行う方法について説明します。

Bigtable は Google の分散型 NoSQL データベースで、数十億行、数千列規模に拡張し、数ペタバイトのデータを格納できます。Bigtable データモデルの詳細については、ストレージ モデルをご覧ください。

外部の永続テーブルに対してクエリを行う

始める前に、ユーザーまたは組織内の他のユーザーが使用できるように外部テーブルを作成する必要があります。詳細と必要な権限については、BigQuery の外部テーブルを作成するをご覧ください。

必要なロール

Bigtable の外部の永続テーブルに対してクエリを行うには、次のロールが必要です。

  • BigQuery データ閲覧者(roles/bigquery.dataViewer
  • BigQuery ユーザー(roles/bigquery.user
  • Bigtable Reader(roles/bigtable.reader

権限に応じて、これらのロールを自身に付与するか、これらのロールを付与するよう管理者に依頼します。ロールの付与の詳細については、リソースに対して付与可能なロールの表示をご覧ください。

外部テーブルのクエリに必要な BigQuery 権限を正確に確認するには、[必要な権限] セクションを開きます。

必要な権限

カスタムロールや他の事前定義ロールを使用して、これらの権限を取得することもできます。

テーブルに対してクエリを実行する

標準の BigQuery テーブルとまったく同じように、外部の永続 Bigtable テーブルに対してクエリを実行できます。ただし、外部データソースに関する制限事項は適用されます。詳細については、インタラクティブ クエリとバッチクエリの実行をご覧ください。

外部の一時テーブルに対してクエリを行う

外部データに対する 1 回限りのアドホック クエリを行う場合、または抽出、変換、読み込み(ETL)プロセスを行う場合は、一時テーブルを使用して外部データソースのクエリを行うと便利です。

永続テーブルを作成せずに外部データソースに対してクエリを実行するには、一時テーブルに対してテーブル定義を指定し、コマンドまたは呼び出しでそのテーブル定義を使用して一時テーブルに対してクエリを実行します。テーブル定義は次のいずれかの方法で指定できます。

テーブル定義ファイルまたは指定したスキーマを使用して一時外部テーブルが作成され、そのテーブルに対してクエリが実行されます。

外部の一時テーブルを使用する場合は、BigQuery データセット内にテーブルが作成されません。テーブルはデータセットに永続的に保存されないため、このテーブルを他のユーザーと共有することはできません。

外部の永続テーブルの代わりに外部の一時テーブルを使用するには、次のようないくつかの制限があります。

  • Bigtable 管理者(roles/bigtable.admin)のロールが必要です。
  • このアプローチでは、Google Cloud コンソールを使用して Bigtable テーブルのスキーマを推測し、テーブル定義を自動的に作成することはできません。テーブル定義はご自身で作成する必要があります。

必要なロール

Bigtable の外部の一時テーブルに対してクエリを行うには、次のロールが必要です。

  • BigQuery データ閲覧者(roles/bigquery.dataViewer
  • BigQuery ユーザー(roles/bigquery.user
  • Bigtable 管理者(roles/bigtable.admin

権限に応じて、これらのロールを自身に付与するか、これらのロールを付与するよう管理者に依頼します。ロールの付与の詳細については、リソースに対して付与可能なロールの表示をご覧ください。

外部テーブルのクエリに必要な BigQuery 権限を正確に確認するには、[必要な権限] セクションを開きます。

必要な権限

カスタムロールや他の事前定義ロールを使用して、これらの権限を取得することもできます。

テーブルを作成してクエリを行う

外部の一時テーブルを使用して Bigtable データに対してクエリを実行するには、次の操作を行います。

外部の一時テーブルの作成とクエリの実行は bp コマンドライン ツールと API でサポートされています。

bq

テーブル定義ファイルを使用して一時テーブルに対してクエリを実行するには、--external_table_definition フラグを指定して bq query コマンドを入力します。

(省略可)--location フラグを指定して、その値をロケーションに設定します。

bq --location=LOCATION query \
--use_legacy_sql=false \
--external_table_definition=TABLE::DEFINITION_FILE \
'QUERY'

次のように置き換えます。

  • LOCATION: ロケーション の名前。--location フラグは省略可能です。
  • TABLE: 作成する一時テーブルの名前。
  • DEFINITION_FILE: ローカルマシン上のテーブル定義ファイルへのパス。
  • QUERY: 一時テーブルに送信するクエリ。

たとえば、次のコマンドを実行すると、follows_def というテーブル定義ファイルを使用して follows という一時テーブルが作成され、クエリが実行されます。

bq query \
--use_legacy_sql=false \
--external_table_definition=follows::/tmp/follows_def \
'SELECT
  COUNT(rowkey)
 FROM
   follows'

API

  • クエリを作成します。クエリジョブの作成方法については、データのクエリをご覧ください。

  • (省略可)ジョブリソースjobReference セクションにある location プロパティでロケーションを指定します。

  • テーブル リソースExternalDataConfiguration を設定して、外部データソースのプロパティを指定します。

Java

このサンプルを試す前に、クライアント ライブラリを使用した BigQuery クイックスタートJava の手順に沿って設定を行ってください。詳細については、BigQuery Java API のリファレンス ドキュメントをご覧ください。

BigQuery に対する認証を行うには、アプリケーションのデフォルト認証情報を設定します。詳細については、ローカル開発環境の認証を設定するをご覧ください。

import com.google.cloud.bigquery.BigQuery;
import com.google.cloud.bigquery.BigQueryException;
import com.google.cloud.bigquery.BigQueryOptions;
import com.google.cloud.bigquery.BigtableColumn;
import com.google.cloud.bigquery.BigtableColumnFamily;
import com.google.cloud.bigquery.BigtableOptions;
import com.google.cloud.bigquery.ExternalTableDefinition;
import com.google.cloud.bigquery.QueryJobConfiguration;
import com.google.cloud.bigquery.TableResult;
import com.google.common.collect.ImmutableList;
import org.apache.commons.codec.binary.Base64;

// Sample to queries an external bigtable data source using a temporary table
public class QueryExternalBigtableTemp {

  public static void main(String[] args) {
    // TODO(developer): Replace these variables before running the sample.
    String projectId = "MY_PROJECT_ID";
    String bigtableInstanceId = "MY_INSTANCE_ID";
    String bigtableTableName = "MY_BIGTABLE_NAME";
    String bigqueryTableName = "MY_TABLE_NAME";
    String sourceUri =
        String.format(
            "https://googleapis.com/bigtable/projects/%s/instances/%s/tables/%s",
            projectId, bigtableInstanceId, bigtableTableName);
    String query = String.format("SELECT * FROM %s ", bigqueryTableName);
    queryExternalBigtableTemp(bigqueryTableName, sourceUri, query);
  }

  public static void queryExternalBigtableTemp(String tableName, String sourceUri, String query) {
    try {
      // Initialize client that will be used to send requests. This client only needs to be created
      // once, and can be reused for multiple requests.
      BigQuery bigquery = BigQueryOptions.getDefaultInstance().getService();

      BigtableColumnFamily.Builder statsSummary = BigtableColumnFamily.newBuilder();

      // Configuring Columns
      BigtableColumn connectedCell =
          BigtableColumn.newBuilder()
              .setQualifierEncoded(Base64.encodeBase64String("connected_cell".getBytes()))
              .setFieldName("connected_cell")
              .setType("STRING")
              .setEncoding("TEXT")
              .build();
      BigtableColumn connectedWifi =
          BigtableColumn.newBuilder()
              .setQualifierEncoded(Base64.encodeBase64String("connected_wifi".getBytes()))
              .setFieldName("connected_wifi")
              .setType("STRING")
              .setEncoding("TEXT")
              .build();
      BigtableColumn osBuild =
          BigtableColumn.newBuilder()
              .setQualifierEncoded(Base64.encodeBase64String("os_build".getBytes()))
              .setFieldName("os_build")
              .setType("STRING")
              .setEncoding("TEXT")
              .build();

      // Configuring column family and columns
      statsSummary
          .setColumns(ImmutableList.of(connectedCell, connectedWifi, osBuild))
          .setFamilyID("stats_summary")
          .setOnlyReadLatest(true)
          .setEncoding("TEXT")
          .setType("STRING")
          .build();

      // Configuring BigtableOptions is optional.
      BigtableOptions options =
          BigtableOptions.newBuilder()
              .setIgnoreUnspecifiedColumnFamilies(true)
              .setReadRowkeyAsString(true)
              .setColumnFamilies(ImmutableList.of(statsSummary.build()))
              .build();

      // Configure the external data source and query job.
      ExternalTableDefinition externalTable =
          ExternalTableDefinition.newBuilder(sourceUri, options).build();
      QueryJobConfiguration queryConfig =
          QueryJobConfiguration.newBuilder(query)
              .addTableDefinition(tableName, externalTable)
              .build();

      // Example query
      TableResult results = bigquery.query(queryConfig);

      results
          .iterateAll()
          .forEach(row -> row.forEach(val -> System.out.printf("%s,", val.toString())));

      System.out.println("Query on external temporary table performed successfully.");
    } catch (BigQueryException | InterruptedException e) {
      System.out.println("Query not performed \n" + e.toString());
    }
  }
}

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

Bigtable 外部データソースに対するクエリのパフォーマンスは、次の 3 つの要素の影響を受けます。

  • 行数
  • 読み取るデータの量
  • 同時読み込みの範囲

BigQuery は、データの読み取り量をできる限り少なくする目的で、クエリで参照される列ファミリーだけを読み取ります。Bigtable クラスタに存在するノード数やテーブルの分割数によって、同時読み込みの範囲が変わります。

Bigtable は、負荷に応じて分割を自動的に統合します。テーブルの読み取り頻度が少ない場合、分割数も少なくなり、クエリのパフォーマンスは徐々に低下します。行キーでテーブルを分割する方法については、テーブルの管理をご覧ください。

BigQuery から Bigtable に対してクエリを実行すると、Bigtable の CPU サイクルが消費されます。BigQuery による CPU の消費は、リアルタイムのユーザー トラフィックの処理など、他の同時リクエストのレイテンシとスループットに影響を及ぼす可能性があります。たとえば、Bigtable の CPU 使用率が高くなると、ロングテール クエリに影響を及ぼし、レイテンシが 99 パーセンタイルに上昇します。

Bigtable の CPU 使用率をモニタリングし、Google Cloud コンソールの Bigtable モニタリング ダッシュボードに表示される推奨範囲内にあることを確認する必要があります。インスタンスのノード数を増やすと、BigQuery トラフィックと他の同時リクエストのトラフィックの両方を処理できるようになります。

クエリフィルタ

行のクエリは、特定の行のみを読み込むフィルタと同じ機能になります。たとえば、GoogleSQL 構文では次のようになります。

SELECT
  COUNT(follows.column.name)
FROM
  `dataset.table`
WHERE
  rowkey = "alice";

rowkey > '1'rowkey < '8' などの範囲フィルタも使用できます。ただし、使用できるのは、readRowkeyAsString オプションで rowkey が文字列として読み取られる場合に限ります。