将数据导出到 Bigtable(反向 ETL)
本文档介绍如何设置从 BigQuery 到 Bigtable 的反向 ETL (RETL)。为此,您可以使用 EXPORT
DATA
语句将数据从 BigQuery 表导出到 Bigtable 表。
您可以使用到 Bigtable 的 RETL 工作流,将 BigQuery 的分析功能与 Bigtable 的低延迟和高吞吐量相结合。该工作流可让您向应用用户提供数据,而不会耗尽 BigQuery 的配额和限制。
Bigtable 表的特性
Bigtable 表与 BigQuery 表的不同之处体现在以下几个方面:
- Bigtable 表和 BigQuery 表均由行组成,但 Bigtable 行是由行关键字和列族组成的,这些列族具有属于同一列族的任意数量的列。
- 给定表的列族在创建表时创建,但也可以稍后添加或移除。创建列族时,无需指定属于它的列。
- Bigtable 列无需提前定义,并可用于以其名称(也称为限定符)存储在表中的数据大小限制内的数据。
- Bigtable 列可以具有在表中的数据大小限制内的任何二进制值。
- Bigtable 列始终具有时间维度(也称为版本)。只要时间戳不相同,可以将任意数量的行存储在同一列的任何行中。
- Bigtable 时间戳以微秒为单位,从 Unix 纪元时间开始计算 - 例如,0 表示 1970-01-01T00:00:00 UTC。时间戳必须是毫秒粒度的非负微秒数(只接受 1000μs 的倍数)。默认 Bigtable 时间戳为 0。
- 在 Bigtable 中,数据通过行键、多个行键、行键范围或使用过滤条件进行读取。除全表扫描外,所有类型的读取请求至少需要一个行键或行键范围。
如需了解如何准备 BigQuery 结果以导出到 Bigtable,请参阅准备查询结果以进行导出。
准备工作
您必须创建 Bigtable 实例和 Bigtable 表来接收导出的数据。
授予为用户提供执行本文档中的每个任务所需权限的 Identity and Access Management (IAM) 角色。
所需的角色
如需获得将 BigQuery 数据导出到 Bigtable 所需的权限,请让管理员向您授予项目的以下 IAM 角色:
-
从 BigQuery 表导出数据:BigQuery Data Viewer (
roles/bigquery.dataViewer
) -
运行导出作业:BigQuery User (
roles/bigquery.user
) - 将数据写入 Bigtable 表:Bigtable User (
roles/bigtable.user
)
如需详细了解如何授予角色,请参阅管理对项目、文件夹和组织的访问权限。
限制
- 编码仅限于
BINARY
和TEXT
。 - 目标 Bigtable 应用配置文件必须配置为使用单集群路由和低请求优先级。
- Bigtable 应用配置文件必须配置为将数据路由到与 BigQuery 数据集位于同一位置的 Bigtable 集群。如需了解详情,请参阅位置注意事项。
- 只有 BigQuery 企业版或企业 Plus 版支持导出到 Bigtable。不支持 BigQuery 标准版和按需计算。
位置注意事项
- 如果 BigQuery 数据集位于多区域,则 Bigtable 应用配置文件必须配置为将数据路由到该多区域内的 Bigtable 集群。例如,如果 BigQuery 数据集位于
US
多区域,则 Bigtable 集群可以位于美国境内的us-west1
(俄勒冈)区域。 - 如果 BigQuery 数据集位于单个区域,则 Bigtable 应用配置文件必须配置为将数据路由到同一区域中的 Bigtable 集群。例如,如果 BigQuery 数据集位于
asia-northeast1
(东京)区域,则 Bigtable 集群也必须位于asia-northeast1
(东京)区域。
如需了解详情,请参阅 Bigtable 位置。
受支持的 BigQuery 类型
以下类型的数据写入 Bigtable 时受支持:
BigQuery 类型 | 写入的 Bigtable 值 |
---|---|
BYTES |
按原样导出。 |
STRING |
转换为 BYTES 。 |
INTEGER |
如果 bigtable_options.column_families.encoding 设置为 BINARY ,则该值采用 8 字节的大端字节序格式(最高有效字节优先)写入。如果 bigtable_options.column_families.encoding 设置为 TEXT ,则该值会写为表示数字的人类可读字符串。 |
FLOAT |
以 IEEE 754 8 字节输出格式写入值。 |
BOOLEAN |
如果 bigtable_options.column_families.encoding 设置为 BINARY ,则该值将写入为 1 字节值(false = 0x00 或 true = 0x01)。如果 bigtable_options.column_families.encoding 设置为 TEXT ,则该值将写入为文本("true" 或 "false" )。 |
JSON |
导出的
JSON 类型的列将被解释为属于特定 Bigtable 列族的一组列。JSON 对象的成员被解释为列,其值将写入 Bigtable。您可以使用 bigtable_options 配置调整要写入的列的名称。例如:
JSON '{"FIELD1": "VALUE1", "FIELD2": "VALUE2"}' as MY_COLUMN_FAMILY 其中,VALUE1 和 VALUE2 值作为 FIELD1 和 FIELD2 列写入 Bigtable 列族 MY_COLUMN_FAMILY。
|
STRUCT |
导出的
STRUCT 类型的列将被解释为属于特定 Bigtable 列族的一组列。结构体的成员会被解释为列,其值将写入 Bigtable。您可以使用 bigtable_options 配置调整要写入的列的名称。例如:
STRUCT<FIELD1 STRING, FIELD2 INTEGER> as MY_COLUMN_FAMILY 其中,FIELD1 和 FIELD2 值作为 FIELD1 和 FIELD2 列写入 Bigtable 列族 MY_COLUMN_FAMILY。
|
这些支持的数据类型类似于从 BigQuery 的外部 Bigtable 表中读取数据。
Bigtable 中的 NULL
值
Bigtable 中的 NULL
值具有以下限制:
Bigtable 没有与
NULL
值对应的模拟。如果在 Bigtable 中导出给定列族和列的NULL
值,则会从 Bigtable 行中删除当前值。如果导出之前不存在具有给定行键、列族、列限定符和时间戳的 Bigtable 值,则导出的
NULL
值对 Bigtable 行没有影响。导出
STRUCT
或JSON
类型的NULL
值时,属于受影响行的所有列族的所有列值都会被删除。您应将NULL
值转换为STRUCT
或JSON
类型,以便 SQL 引擎为其附加正确的类型。以下查询会删除具有一组指定行键的列族column_family1
中的所有数据:EXPORT DATA OPTIONS (...) AS SELECT rowkey, CAST(NULL as STRUCT<INT64>) AS column_family1 FROM T
在导出期间,系统会跳过具有
NULL
行键的行。在导出统计信息中,跳过的行数会返回给调用方。
使用 bigtable_options
配置导出
您可以在导出期间使用 bigtable_options
配置来弥合 BigQuery 和 Bigtable 存储模型之间的差异。配置以 JSON 字符串的形式表示,如以下示例所示:
EXPORT DATA OPTIONS( uri="https://bigtable.googleapis.com/projects/PROJECT_ID
/instances/INSTANCE_ID
/appProfiles/APP_PROFILE_ID
/tables/TABLE
", bigtable_options = """{ "columnFamilies": [{ "familyId": "COLUMN_FAMILY_NAME
", "encoding": "ENCODING_VALUE
", "columns": [ { "qualifierString": "BIGTABLE_COLUMN_QUALIFIER
", ["qualifierEncoded": "BASE_64_ENCODED_VALUE
",] "fieldName": "BIGQUERY_RESULT_FIELD_NAME
" } ] }] }""" )
下表介绍了 bigtable_options
配置中使用的可能字段:
字段名称 | 说明 |
---|---|
columnFamilies |
列族描述符的数组。 |
columnFamilies.familyId |
Bigtable 列族的标识符。 |
columnFamilies.encoding |
值可以设置为 BINARY 或 TEXT 。如需了解如何对类型进行编码,请参阅受支持的 BigQuery 类型。 |
columnFamilies.columns |
Bigtable 列映射的数组。 |
columnFamilies.columns.qualifierString |
可选:Bigtable 列限定符。如果列限定符没有非 UTF-8 代码,请指定此值。qualifierString 和 qualifierEncoding 字段是互斥的。如果既未指定 qualifierString ,也未指定 qualifierEncoded ,则使用 fieldName 作为列限定符。 |
columnFamilies.columns.qualifierEncoded |
可选:Base64 编码的列限定符。与 qualifierString 类似,前提是列限定符必须具有非 UTF-8 代码。 |
columnFamilies.columns.fieldName |
必需:BigQuery 结果集字段名称。在某些情况下,可为空字符串。如需查看如何将空 fieldName 值与简单类型字段结合使用的示例,请参阅准备查询结果以进行导出。 |
准备查询结果以进行导出
如需将查询结果导出到 Bigtable,结果必须满足以下要求:
- 结果集必须包含类型为
STRING
或BYTES
的列rowkey
。 - 行键、列限定符、值和时间戳不得超过 Bigtable 表中的数据大小限制。
- 结果集中除了
rowkey
之外必须至少还有一列。 - 每个结果集列都必须是受支持的 BigQuery 类型之一。必须先将任何不受支持的列类型转换为某种受支持的类型,然后再导出到 Bigtable。
Bigtable 不要求列限定符是有效的 BigQuery 列名称,并且 Bigtable 支持使用任何字节。如需了解如何替换导出的目标列限定符,请参阅使用 bigtable_options
配置导出。
如果您将导出的值与 Bigtable API(例如 ReadModifyWriteRow
)搭配使用,则任何数值都必须使用正确的二进制编码。
默认情况下,STRUCT
或 JSON
以外类型的独立结果列会被解释为目标列族的值等于结果列名称,列限定符等于空字符串。
为了演示如何写入这些数据类型,请考虑以下 SQL 示例,其中 column
和 column2
是独立的结果列:
SELECT
x as column1, y as column2
FROM table
在此示例查询中,在处理除 JSON
或 STRUCT
之外的类型时,SELECT x as column1
将数据写入 Bigtable 中的 column1
列族和 ''
(空字符串)列限定符下。
您可以使用 bigtable_options
配置来更改在导出中写入这些类型的方式,如以下示例所示:
EXPORT DATA OPTIONS ( … bigtable_options="""{ "columnFamilies" : [ { "familyId": "ordered_at", "columns": [ {"qualifierString": "order_time", "fieldName": ""} ] } ] }""" ) AS SELECT order_id as rowkey, STRUCT(product, amount) AS sales_info, EXTRACT (MILLISECOND FROM order_timestamp AT TIME ZONE "UTC") AS ordered_at FROM T
在此示例中,BigQuery 表 T
包含以下行:
order_id |
order_timestamp |
product |
amount |
---|---|---|---|
101 | 2023-03-28T10:40:54Z | 操纵杆 | 2 |
如果将上述 bigtable_options
配置与表 T
搭配使用,系统会将以下数据写入 Bigtable:
rowkey |
sales_info (列族) |
ordered_at (列族) |
|||
---|---|---|---|---|---|
101 | 产品 | 金额 | order_time | ||
1970-01-01T00:00:00Z | 操纵杆 | 1970-01-01T00:00:00Z | 2 | 1680000054000 |
1680000054000
表示 2023-03-28T10:40:54Z
,以毫秒为单位,从世界协调时间 (UTC) 时区从 Unix 纪元时间算起。
使用 _CHANGE_TIMESTAMP
为行中的所有单元格设置时间戳
您可以将 TIMESTAMP
类型的 _CHANGE_TIMESTAMP
列添加到要导出的结果中。写入 Bigtable 的每个单元格都会使用导出结果行的 _CHANGE_TIMESTAMP
中的时间戳值。
Bigtable 不支持 Unix 纪元 (1970-01-01T00:00:00Z) 之前的时间戳。如果 _CHANGE_TIMESTAMP
值为 NULL
,则将 Unix 纪元时间 0
用作默认时间戳值。
以下查询为表 T
的 order_timestamp
列中指定的时间戳写入 product
和 amount
列的单元。
EXPORT DATA OPTIONS (...) AS SELECT rowkey, STRUCT(product, amount) AS sales_info, order_timestamp as _CHANGE_TIMESTAMP FROM T
持续导出
如果您想持续处理导出查询,可以将其配置为持续查询。
导出具有相同 rowkey
值的多个结果
当您导出包含具有相同 rowkey
值的多个行的结果时,写入 Bigtable 的值最终将位于同一个 Bigtable 行中。
您可以使用此方法在同一行中生成多个列值版本。在此示例中,BigQuery 中的 orders
表包含以下数据:
id |
customer |
order_timestamp |
amount_spent |
---|---|---|---|
100 | 小博 | 2023-01-01T10:10:54Z | 10.99 |
101 | Alice | 2023-01-02T12:10:50Z | 102.7 |
102 | 小博 | 2023-01-04T15:17:01Z | 11.1 |
然后,用户执行以下 EXPORT DATA
语句:
EXPORT DATA OPTIONS (
uri="https://bigtable.googleapis.com/projects/PROJECT-ID/instances/INSTANCE-ID/appProfiles/APP_PROFILE_ID
/tables/TABLE",
format="CLOUD_BIGTABLE"
) AS
SELECT customer as rowkey, STRUCT(amount_spent) as orders_column_family, order_timestamp as _CHANGE_TIMESTAMP
FROM orders
将此语句与 BigQuery orders
表搭配使用会将以下数据写入 Bigtable:
orders_column_family | ||
---|---|---|
行键 | amount_spent | |
Alice | 2023-01-02T12:10:50Z | 102.7 |
小博 | 2023-01-01T10:10:54Z | 10.99 |
2023-01-04T15:17:01Z | 11.1 |
导出到 Bigtable 会将新值合并到表中,而不是替换整行。如果 Bigtable 中已存在行键的值,则新值可以部分或完全替换前面的值,具体取决于写入的单元格的列族、列名称和时间戳。
将多个列导出为协议缓冲区 (Protobuf) 值
协议缓冲区为序列化结构化数据提供了一种灵活而高效的机制。考虑到 BigQuery 和 Bigtable 之间处理不同类型的方式不同,导出为 Protobuf 会很有帮助。您可以使用 BigQuery 用户定义的函数 (UDF) 将数据作为 Protobuf 二进制值导出到 Bigtable。如需了解详情,请参阅将数据导出为 Protobuf 列。
导出优化
您可以通过修改 Bigtable 目标集群中的节点数来更改将记录从 BigQuery 导出到 Bigtable 的吞吐量。吞吐量(每秒写入的行数)会随着目标集群中的节点数线性扩缩。例如,如果您将目标集群中的节点数增加一倍,则导出吞吐量大致会增加一倍。
价格
当您从标准查询中导出数据时,您需要按照数据提取价格付费。当您从持续查询中导出数据时,您需要按照 BigQuery 容量计算价格付费。如需运行持续查询,您必须拥有使用企业版或企业 Plus 版的预留以及使用 CONTINUOUS
作业类型的预留分配。
导出数据后,如果您将数据存储在 Bigtable 中,则需要为此付费。如需了解详情,请参阅 Bigtable 价格。