创建 Bigtable 外部表

本页面介绍如何创建 BigQuery 永久外部表,该表可用于查询存储在 Bigtable 中的数据。您可以在所有 Bigtable 位置查询 Bigtable 中的数据。

准备工作

在创建外部表之前,请收集一些信息并确保您有权创建该表。

所需的角色

如需创建用于查询 Bigtable 数据的外部表,您必须是包含源表的实例的 Bigtable Admin (roles/bigtable.admin) 角色的主账号。

您还需要拥有 bigquery.tables.create BigQuery Identity and Access Management (IAM) 权限。

以下每个预定义的 Identity and Access Management 角色都具有此权限:

  • BigQuery Data Editor (roles/bigquery.dataEditor)
  • BigQuery Data Owner (roles/bigquery.dataOwner)
  • BigQuery Admin (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

创建永久外部表

在 BigQuery 中创建链接到 Bigtable 数据源的永久外部表时,您可以通过以下两个选项指定外部表的格式:

  • 如果您使用的是 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:Bigtable 表的架构(采用 JSON 格式)。如需查看 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 属性,请仅指定一个 Bigtable URI;并且该 URI 必须是有效的 HTTPS 网址。

对于 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 查询可能难度较大。可能的解决方案是将列提升为父级列族中的子字段,并且只读取每个单元格中的最新值。这会以标量值的形式表示默认架构中的两个数组。

示例

您要存储某个虚构社交网络的用户个人资料。一个数据模型可能是一个 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 作为子字段公开提供,则查询数据会较为容易。为了将这些字段作为子字段公开提供,可以在定义表时将 genderage 作为 profile 列族中的命名列列出。您还可以指示 BigQuery 公开此列族中的最新值,因为通常只有最新值(并且可能是唯一值)才是有用的。

将列作为子字段公开提供之后,用于计算 30 岁以上男性用户数量的 GoogleSQL 查询如下:

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 将数据存储为与数据编码无关的原始字节。但字节值在 SQL 查询分析中的用途有限。Bigtable 提供两种基本类型的标量解码:文本和 HBase 二进制。

文本格式假定所有值均存储为字母数字文本字符串。例如,整数 768 将存储为字符串“768”。二进制编码假定使用 HBase 的 Bytes.toBytes 方法类对数据进行编码,并应用适当的解码方法。

支持的区域和地区

您可以在支持 Bigtable 的所有可用区中进行 Bigtable 数据查询。您可以在此处找到可用区列表。对于多集群实例,BigQuery 会根据 Bigtable 应用配置文件设置路由流量。

限制

如需了解适用于外部表的限制,请参阅外部表限制

Compute Engine 实例的范围

创建 Compute Engine 实例时,您可以为该实例指定一个范围列表。这些范围用于控制实例对 Google Cloud 产品(包括 Bigtable)的访问权限。在虚拟机上运行的应用使用服务账号来调用 Google Cloud API。

如果您将某个 Compute Engine 实例设置为以服务账号身份运行,并且该服务账号访问一个链接到 Bigtable 数据源的外部表,则您必须为该实例添加 Bigtable 只读数据访问权限范围 (https://www.googleapis.com/auth/bigtable.data.readonly)。如需了解详情,请参阅为 Bigtable 创建 Compute Engine 实例

如需了解如何将范围应用于 Compute Engine 实例,请参阅更改实例的服务账号和访问权限范围。如需详细了解 Compute Engine 服务账号,请参阅服务账号

后续步骤