Bigtable の外部テーブルを作成する

このページでは、Bigtable に保存されているデータのクエリに使用できる BigQuery の外部の永続テーブルを作成する方法について説明します。Bigtable のデータのクエリは、すべての Bigtable のロケーションで使用できます。

始める前に

外部テーブルを作成する前に、情報を収集して、テーブルの作成権限があることを確認します。

必要なロール

Bigtable データのクエリに使用する外部テーブルを作成するには、ソーステーブルを含むインスタンスの Bigtable 管理者(roles/bigtable.admin)ロールのプリンシパルである必要があります。

また、bigquery.tables.create BigQuery Identity and Access Management(IAM)権限も必要です。

この権限は、次の Identity and Access Management 事前定義ロールに含まれています。

  • BigQuery データ編集者(roles/bigquery.dataEditor
  • BigQuery データオーナー(roles/bigquery.dataOwner
  • BigQuery 管理者(roles/bigquery.admin

これらのロールのいずれかのプリンシパルでない場合は、アクセス権の付与または外部テーブルの作成を管理者に依頼してください。

BigQuery での Identity and Access Management のロールと権限の詳細については、事前定義ロールと権限をご覧ください。Bigtable の権限の詳細については、Identity and Access Management によるアクセス制御をご覧ください。

データセットを作成または特定する

外部テーブルを作成する前に、その外部テーブルを含むデータセットを作成する必要があります。既存のプロジェクトを使用することもできます。

省略可: クラスタを指定または作成する

本番環境のアプリケーション用データと同じデータを頻繁にクエリする場合は、Bigtable インスタンスのクラスタを BigQuery 分析専用に指定することをおすすめします。これにより、トラフィックの読み取りと書き込みに使用するクラスタからトラフィックが分離されます。レプリケーションと、複数のクラスタを持つインスタンスの作成の詳細については、レプリケーションについてをご覧ください。

アプリ プロファイルを特定または作成する

外部テーブルを作成する前に、BigQuery がデータの読み取りに使用する Bigtable アプリ プロファイルを決定します。BigQuery 専用で使用するように指定したアプリ プロファイルを使用することをおすすめします。

Bigtable インスタンスに BigQuery アクセス専用のクラスタがある場合は、そのクラスタへの単一クラスタ ルーティングを使用するようにアプリ プロファイルを構成します。

Bigtable アプリ プロファイルの仕組みについては、アプリ プロファイルについてをご覧ください。新しいアプリ プロファイルの作成方法については、アプリ プロファイルの作成と構成をご覧ください。

Bigtable URI を取得する

Bigtable データソースの外部テーブルを作成するには、Bigtable URI が必要です。Bigtable URI を取得する手順は次のとおりです。

  1. コンソールで [Bigtable] ページを開きます。

    Bigtable に移動

  2. Bigtable データソースについて次の詳細を取得します。

    • プロジェクト ID
    • Bigtable インスタンス ID。
    • 使用する予定の Bigtable アプリ プロファイルの ID
    • Bigtable テーブルの名前
  3. 次の形式で Bigtable URI を作成します。

    • project_id は Bigtable インスタンスを含むプロジェクトです。
    • instance_id は Bigtable インスタンス ID です。
    • (省略可)app_profile は、使用するアプリ プロファイル ID です。
    • table_name はクエリ対象のテーブルの名前です。

    https://googleapis.com/bigtable/projects/project_id/instances/instance_id[/appProfiles/app_profile]/tables/table_name

外部の永続テーブルを作成する

Bigtable データソースにリンクされる外部の永続テーブルを BigQuery で作成する場合、外部テーブルの形式は次の 2 つの方法で指定できます。

  • API または bq コマンドライン ツールを使用している場合は、外部テーブルのスキーマとメタデータを定義するテーブル定義ファイルを作成します。
  • SQL を使用している場合は、CREATE EXTERNAL TABLE ステートメントの uri オプションを使用してデータを取得する Bigtable テーブルを指定し、bigtable_options オプションを使用してテーブル スキーマを指定します。

外部テーブルのデータは BigQuery テーブルに保存されません。テーブルは永続的であるため、データセット レベルでアクセス制御を使用することで、基礎となる Bigtable データソースにアクセスできる他のユーザーとテーブルを共有できます。

永続テーブルを作成するには、次のいずれかの方法を選択します。

SQL

永続外部テーブルを作成するには、CREATE EXTERNAL TABLE DDL ステートメントを実行します。テーブル スキーマは、ステートメント オプションの一部として明示的に指定する必要があります。

  1. Google Cloud コンソールで [BigQuery] ページに移動します。

    [BigQuery] に移動

  2. クエリエディタで次のステートメントを入力します。

    CREATE EXTERNAL TABLE DATASET.NEW_TABLE
    OPTIONS (
      format = 'CLOUD_BIGTABLE',
      uris = ['URI'],
      bigtable_options = BIGTABLE_OPTIONS );
    

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

    • DATASET: Bigtable 外部テーブルを作成するデータセット。
    • NEW_TABLE: Bigtable 外部テーブルの名前。
    • URI: データソースとして使用する Bigtable テーブルの URI。この URI は、Bigtable URI を取得するで説明されている形式に従う必要があります。
    • BIGTABLE_OPTIONS: JSON 形式の Bigtable テーブルのスキーマ。Bigtable テーブル定義オプションの一覧については、REST API リファレンスの BigtableOptions をご覧ください。

  3. [ 実行] をクリックします。

クエリの実行方法については、インタラクティブ クエリを実行するをご覧ください。

外部 Bigtable テーブルを作成するステートメントは次のようになります。

CREATE EXTERNAL TABLE mydataset.BigtableTable
OPTIONS (
  format = 'CLOUD_BIGTABLE',
  uris = ['https://googleapis.com/bigtable/projects/myproject/instances/myBigtableInstance/tables/table1'],
  bigtable_options =
    """
    {
      columnFamilies: [
        {
          "familyId": "familyId1",
          "type": "INTEGER",
          "encoding": "BINARY"
        }
      ],
      readRowkeyAsString: true
    }
    """
);

bq

bq コマンドライン ツールで bq mk コマンドを使用してテーブルを作成します。外部データソースにリンクされるテーブルを bq コマンドライン ツールを使用して作成する場合は、テーブル定義ファイルを使用してテーブルのスキーマを指定します。

  1. 永続テーブルの作成には、bq mk コマンドを使用します。

    bq mk \
    --external_table_definition=DEFINITION_FILE \
    DATASET.TABLE
    

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

    • DEFINITION_FILE: ローカルマシン上のテーブル定義ファイルへのパス。
    • DATASET: テーブルを含むデータセットの名前。
    • TABLE: 作成するテーブルの名前。

API

tables.insert API メソッドを使用して、渡す TableリソースExternalDataConfiguration を作成します。

Table リソースの sourceUris プロパティには、1 つの Bigtable URI のみを指定します。有効な HTTPS URL を入力してください。

sourceFormat プロパティに "BIGTABLE" を指定します。

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.TableId;
import com.google.cloud.bigquery.TableInfo;
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 permanent table
public class QueryExternalBigtablePerm {

  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 bigqueryDatasetName = "MY_DATASET_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);
    queryExternalBigtablePerm(bigqueryDatasetName, bigqueryTableName, sourceUri, query);
  }

  public static void queryExternalBigtablePerm(
      String datasetName, 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();

      TableId tableId = TableId.of(datasetName, tableName);
      // Create a permanent table linked to the Bigtable table
      ExternalTableDefinition externalTable =
          ExternalTableDefinition.newBuilder(sourceUri, options).build();
      bigquery.create(TableInfo.of(tableId, externalTable));

      // Example query
      TableResult results = bigquery.query(QueryJobConfiguration.of(query));

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

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

外部テーブルにクエリを実行する

詳細については、Bigtable データにクエリを実行するをご覧ください。

生成されたスキーマ

BigQuery はデフォルトで、列ファミリーの値を列の配列として公開します。値の配列は異なるタイムスタンプで書き込まれます。このスキーマにより、Bigtable ではデータが未加工のレイアウトで維持されますが、SQL クエリでは操作が難しくなる場合があります。親の列ファミリー内で列がサブフィールドになる場合や、各セルから最新の値だけが読み取られる場合があります。これにより、デフォルト スキーマでは、両方の配列がスカラー値として扱われます。

架空のソーシャル ネットワークにユーザー プロフィールを保存しています。このデータモデルの 1 つは profile 列ファミリーで、genderageemail という列が個別に存在します。

rowkey | profile:gender| profile:age| profile:email
-------| --------------| -----------| -------------
alice  | female        | 30         | alice@gmail.com

デフォルトのスキーマを使用する場合、30 歳を超えている男性ユーザーの数を確認する GoogleSQL クエリは次のようになります。

SELECT
  COUNT(1)
FROM
  `dataset.table`
OMIT
  RECORD IF NOT SOME(profile.column.name = "gender"
    AND profile.column.cell.value = "male")
  OR NOT SOME(profile.column.name = "age"
    AND INTEGER(profile.column.cell.value) > 30)

genderage がサブフィールドとして公開されていれば、クエリはそれほど難しいものではありません。これらをサブフィールドとして公開するには、テーブルを定義するときに、profile 列ファミリーで genderage を名前付きの列として設定します。また、この列ファミリーから最新の値を公開するように BigQuery に指示することもできます。通常は、最新の値(場合によっては唯一の値)だけが注目されるためです。

列をサブフィールドとして公開すると、30 歳を超える男性ユーザー数を確認する GoogleSQL SQL クエリは次のようになります。

SELECT
  COUNT(1)
FROM
  `dataset.table`
WHERE
  profile.gender.cell.value="male"
  AND profile.age.cell.value > 30

genderage はフィールドとして直接参照されています。この設定の JSON 構成は次のとおりです。

  "bigtableOptions": {
    "readRowkeyAsString": "true",
    "columnFamilies": [
      {
          "familyId": "profile",
          "onlyReadLatest": "true",
          "columns": [
              {
                  "qualifierString": "gender",
                  "type": "STRING"
              },
              {
                  "qualifierString": "age",
                  "type": "INTEGER"
              }
          ]
      }
    ]
  }

値のエンコード

Bigtable はデータを RAW バイトとして保存し、データのエンコードに依存しません。ただし、SQL クエリ分析でバイト値を使用する場合に制限があります。Bigtable には、2 種類の基本的なスカラー デコード(テキストと HBase バイナリ)があります。

テキスト形式は、すべての値が英数字のテキスト文字列で保存されていることを前提としています。たとえば、整数 768 は文字列 "768" として保存されます。バイナリ エンコードでは、データのエンコードに HBase の Bytes.toBytes クラスのメソッドが使用されていると想定し、適切なデコード方法が適用されます。

サポート対象リージョンとゾーン

Bigtable のデータのクエリは、サポートされているすべての Bigtable ゾーンで使用できます。ゾーンの一覧については、こちらをご覧ください。マルチクラスタ インスタンスの場合、BigQuery は Bigtable のアプリ プロファイルの設定に基づいてトラフィックをルーティングします。

制限事項

外部テーブルに適用される制限事項については、外部テーブルの制限事項をご覧ください。

Compute Engine インスタンスのスコープ

Compute Engine インスタンスを作成するときに、インスタンスに対するスコープのリストを指定できます。このスコープは、Bigtable を含む Google Cloud プロダクトに対するインスタンスのアクセスを制御します。VM 上で実行されるアプリケーションは、サービス アカウントを使用して Google Cloud API を呼び出します。

サービス アカウントとして実行するように Compute Engine インスタンスを設定した場合、Bigtable データソースにリンクされた外部テーブルにこのサービス アカウントからアクセスするには、Bigtable 読み取り専用データアクセス スコープ(https://www.googleapis.com/auth/bigtable.data.readonly)をインスタンスに追加する必要があります。詳細については、Bigtable 用の Compute Engine インスタンスの作成をご覧ください。

Compute Engine インスタンスへのスコープの適用方法については、インスタンスのサービス アカウントとアクセス スコープを変更するをご覧ください。Compute Engine サービス アカウントの詳細については、サービス アカウントをご覧ください。

次のステップ