将数据导出到 Bigtable(反向 ETL)
本文档介绍如何设置从 BigQuery 到 Bigtable 的反向 ETL (RETL)。为此,您可以使用 EXPORT
DATA
语句将数据从 BigQuery 表导出到 Bigtable 表。
您可以将 RETL 工作流用于 Bigtable,将 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 标准版和按需计算。
- 只有具有
QUERY
分配的预留才支持导出到 Bigtable。
位置注意事项
- 如果 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 价格。