面向 DynamoDB 用户的 Bigtable 简介

本文档适用于想要迁移或设计应用(以数据存储区形式)使用 Bigtable 的 DynamoDB 开发者和数据库管理员。在阅读本文档之前,请先阅读 Bigtable 概览

Bigtable 和 DynamoDB 是分布式键值对存储区,可以支持每秒数百万的查询次数 (QPS),提供可扩缩到 PB 级数据的存储空间,并且容忍节点故障。

虽然这些数据库服务的特征集相似,但它们的基础架构和交互细节在对于理解很重要的方面有所不同,因此在开始迁移之前,请务必了解这些差异。本文档重点介绍了两种数据库系统之间的异同。

控制平面

在 DynamoDB 和 Bigtable 中,您可以通过控制平面配置容量,以及设置和管理资源。DynamoDB 是一款无服务器产品,与 DynamoDB 的最高级别互动是表级别。在预配容量模式下,您可以在此处预配读写请求单元、选择区域和复制方式,以及管理备份。Bigtable 不是无服务器产品;您必须创建包含一个或多个集群的实例,其容量取决于集群中的节点数量。如需详细了解这些资源,请参阅实例、集群和节点

下表比较了 DynamoDB 和 Bigtable 的控制平面资源。

DynamoDB Bigtable
:具有已定义主键的项集合。表具有备份复制容量设置。 实例:发生复制和连接路由的不同 Google Cloud 可用区或区域中的一组 Bigtable 集群。复制政策是在实例级设置的。

集群:一组位于同一地理位置的 Google Cloud 可用区中的节点,最好与应用服务器共存,以减少延迟时间并提高复制效率。容量是通过调整每个集群中的节点数量来管理的。

:按行键编入索引的值的逻辑组织。

备份是在表级控制的。
读取容量单位 (RCU) 和写入容量单位 (WCU) :允许每秒进行固定载荷大小的读取或写入的单位。对于载荷大小较大的每项操作,系统都会收取读取或写入单元的费用。

UpdateItem 操作会消耗用于更新项的最大大小(更新前或更新后)的写入容量,即使更新涉及项的一部分属性也是如此。
节点:一种负责读取和写入数据的虚拟计算资源。集群的节点数量会转化为读取、写入和扫描的吞吐量限制。您可以根据延迟时间目标值、请求数和载荷大小的组合来调整节点数量。

SSD 节点的读写吞吐量相同,这与 RCU 和 WCU 之间存在明显差异不同。如需了解详情,请参阅典型工作负载下的性能
分区 :一组连续的行,由与节点共处的固态硬盘 (SSD) 提供支持。

每个分区都有 1,000 个 WCU、3,000 个 RCU 和 10 GB 数据的硬限制。
平板: 由所选存储介质(SSD 或 HDD)支持的一组连续行。

表会分片为多个“片”,以平衡工作负载。片未存储在 Bigtable 的节点上,而是存储在 Google 的分布式文件系统中,这使得在伸缩时能够快速重新分配数据,并通过维护多个副本来提供额外的持久性。
全局表 :通过自动将数据更改传播到多个区域,提高数据的可用性和耐用性。 复制: 一种通过自动将数据更改传播到多个区域或同一区域内的多个可用区来提高数据可用性和耐用性的方法。
不适用 (N/A) 应用配置文件 :指示 Bigtable 如何将客户端 API 调用路由到实例中的相应集群的设置。您还可以将应用配置文件用作标记,以便对指标进行细分以进行归因。

地理位置复制

复制功能用于满足客户对以下方面的要求:

  • 高可用性,以便在发生可用区或区域级故障时确保业务连续性。
  • 将您的服务数据放置在靠近最终用户的位置,以便在他们位于世界各地时提供低延迟服务。
  • 当您需要在一个集群上实现批处理工作负载,并依赖于复制到服务集群时,请使用工作负载隔离。

Bigtable 支持在最多 8 个提供 Bigtable 的 Google Cloud 区域中的多个可用区中部署复制集群。大多数区域有三个可用区。如需了解详情,请参阅区域和地区

Bigtable 会在多主集群拓扑中自动跨集群复制数据,这意味着您可以向任何集群读写数据。Bigtable 复制具有最终一致性。如需了解详情,请参阅复制概览

DynamoDB 提供全局表,以支持跨多个区域复制表。全局表具有多个主副本,并会自动跨区域复制。复制操作具有最终一致性。

下表列出了复制概念,并介绍了这些概念在 DynamoDB 和 Bigtable 中的可用性。

属性 DynamoDB Bigtable
多主副复制 可以。

您可以读取和写入任何全局表。
可以。

您可以读取和写入任何 Bigtable 集群。
一致性模型 最终一致。

针对全球表在区域级实现读己所写一致性。
最终一致。

所有表在集群级别的读己所写一致性(前提是您将读取和写入操作发送到同一集群)。
复制延迟时间 无服务等级协议 (SLA)。

无 SLA。

配置粒度 表级。 实例级。

一个实例可以包含多个表。
实现 创建一个全局表,并在每个所选区域中为其创建表副本。

区域级。

通过将表转换为全局表,在副本之间自动复制。

表必须启用 DynamoDB 流,并且流中同时包含商品的新图片和旧图片。

删除某个区域可移除该区域中的全局表。
创建包含多个集群的实例。
系统会自动在该实例中的集群之间复制数据。

可用区级。

向 Bigtable 实例添加和移除集群。
复制选项 每个表。 每个实例。
流量路由和可用性 将流量路由到最近的地理位置副本。

在发生故障时,您可以应用自定义业务逻辑来确定何时将请求重定向到其他区域。
使用应用配置文件配置集群流量路由政策。

使用多集群路由将流量自动路由到最近的健康集群。

在发生故障时,Bigtable 支持在集群之间自动故障切换以实现高可用性。
扩缩 复制写入请求单元 (R-WRU) 中的写入容量会在各个副本之间同步。

复制读取容量单位 (R-RCU) 中的读取容量是指每个副本的读取容量。
您可以根据需要从每个复制集群中添加或移除节点,从而独立扩缩集群。
费用 R-WRU 的费用比常规 WRU 高出 50%。 您需要为每个集群的节点和存储空间付费。
跨可用区进行区域级复制不会产生网络复制费用。
跨区域或跨洲复制时,系统会收取费用。
服务等级协议 (SLA) 99.999% 99.999%

数据平面

下表比较了 DynamoDB 和 Bigtable 的数据模型概念。表中的每一行都描述了类似的功能。例如,DynamoDB 中的项类似于 Bigtable 中的行。

DynamoDB Bigtable
:一组属性,可通过其主键在所有其他项中进行唯一标识。允许的大小上限为 400 KB。 :由行键标识的单个实体。允许的大小上限为 256 MB。
不适用 列族:用户指定的命名空间,用于对列进行分组。
属性 :名称和值的组合。属性值可以是标量、集或文档类型。属性大小本身没有明确限制。不过,由于每个项的大小上限为 400 KB,因此对于只有一个属性的项,该属性的大小上限为 400 KB 减去属性名称占用的大小。 列限定符 :列族中列的唯一标识符。列的完整标识符表示为 column-family:column-qualifier。列限定符在列族中按字典顺序排序。

列限定符的大小上限为 16 KB。


单元格:单元格用于存储给定行、列和时间戳的数据。单元格包含一个值,该值不得超过 100 MB。
主键 :表中项的唯一标识符。它可以是分区键或复合键。

分区键:一个由单个属性组成的简单主键。这决定了内容所在的物理分区。允许的最大大小为 2 KB。

排序键:用于确定分区中行排序方式的键。允许的大小上限为 1 KB。

复合键:由两个属性(分区键和排序键或范围属性)组成的主键。
行键 :表中项的唯一标识符。通常由值和分隔符的串联表示。允许的大小上限为 4 KB。

列限定符可用于实现与 DynamoDB 的排序键等效的行为。

可以使用串联的行键和列限定符构建复合键

如需了解详情,请参阅本文档“架构设计”部分中的架构转换示例。

存活时间 :每个项的时间戳决定了该项何时不再需要。在指定时间戳的日期和时间之后,系统会从表中删除该项,而不会消耗任何写入吞吐量。 垃圾回收 :每个单元格的时间戳用于确定何时不再需要某个项。垃圾回收会在一个名为“压缩”的后台进程中删除过期项。垃圾回收政策是在列族级设置的,不仅可以根据内容的保留期限删除内容,还可以根据用户想要保留的版本数量删除内容。在调整集群大小时,您无需考虑压缩容量。

运维

借助数据平面操作,您可以对表中的数据执行创建、读取、更新和删除 (CRUD) 操作。下表比较了 DynamoDB 和 Bigtable 的类似数据平面操作。

DynamoDB Bigtable
CreateTable CreateTable
PutItem
BatchWriteItem
MutateRow
MutateRows
Bigtable 会将写入操作视为更新/插入操作。
UpdateItem
  • 有条件写入
  • 增量和减量

Bigtable 会将写入操作视为更新/插入操作。
GetItem
BatchGetItemQueryScan
`ReadRow`
`ReadRows`(范围、前缀、向后扫描)
Bigtable 支持按行键前缀、正则表达式模式或向前或向后扫描行键范围高效扫描。

数据类型

Bigtable 和 DynamoDB 均为无架构数据库。您可以在写入时定义列,而无需对列存在性或数据类型进行任何表级强制执行。同样,给定列或属性的数据类型可能会因行或项而异。不过,DynamoDB API 和 Bigtable API 处理数据类型的方式不同。

每个 DynamoDB 写入请求都包含每个属性的类型定义,该定义会随读取请求的响应一起返回。

Bigtable 会将所有内容都视为字节,并希望客户端代码知道类型和编码,以便客户端能够正确解析响应。增量操作是一个例外,它会将值解读为 64 位大端序有符号整数。

下表比较了 DynamoDB 和 Bigtable 的数据类型之间的差异。

DynamoDB Bigtable
标量类型 :在服务器响应中作为数据类型描述符令牌返回。 字节 :字节会在客户端应用中转换为预期类型。

增量会将值解读为 64 位大端序有符号整数
:由不重复元素组成的未排序集合。 列族 :您可以将列限定符用作集成员名称,并为每个成员提供一个 0 字节作为单元格值。集合成员在其列族中按字典顺序排序。
映射 :包含具有唯一键的键值对的未排序集合。 列族
将列限定符用作映射键,将单元格值用作值。映射键按字典顺序排序。
列表 :项的有序集合。 列限定符
使用插入时间戳可实现与 list_append 行为等效的操作,即与用于 prepend 的插入时间戳相反。

架构设计

架构设计中的一个重要考虑因素是数据的存储方式。Bigtable 和 DynamoDB 之间的主要区别在于它们处理以下各项的方式:

  • 对单个值的更新
  • 数据排序
  • 数据版本控制
  • 存储大值

对单个值的更新

DynamoDB 中的 UpdateItem 操作会消耗“之前”和“之后”项大小中较大的写入容量,即使更新涉及项的部分属性也是如此。这意味着,在 DynamoDB 中,您可以将经常更新的列放在单独的行中,即使在逻辑上它们与其他列属于同一行也是如此。

无论单元格是给定行中的唯一列,还是成千上万个列中的一列,Bigtable 都能同样高效地更新该单元格。如需了解详情,请参阅简单写入

数据排序

DynamoDB 会对分区键进行哈希处理并随机分发,而 Bigtable 会按行键的字典顺序存储行,并将所有哈希处理工作交给用户。

随机密钥分发并不适用于所有访问模式。这会降低热行范围的风险,但会使涉及跨分区边界扫描的访问模式变得昂贵且低效。这些无界扫描很常见,尤其是对于具有时间维度的用例。

若要处理此类访问模式(跨分区边界的扫描),DynamoDB 需要二级索引,但 Bigtable 无需二级索引即可处理此类访问模式。同样,在 DynamoDB 中,查询和扫描操作的扫描数据量上限为 1 MB,超出此上限时需要分页。Bigtable 没有此类限制。

尽管分区键是随机分布的,但如果所选分区键未均匀分布流量,从而对吞吐量产生不利影响,DynamoDB 可能仍会出现热分区。为解决此问题,DynamoDB 建议进行写入分片,即在多个逻辑分区键值之间随机拆分写入。

如需应用此设计模式,您需要从固定集合(例如 1 到 10)中创建一个随机数字,然后将此数字用作逻辑分区键。由于您对分区键进行了随机化处理,因此对表的写入会均匀分布在所有分区键值中。

Bigtable 将此过程称为键盐加密,这是一种有效避免出现热点的做法。

数据版本控制

每个 Bigtable 单元格都有一个时间戳,并且最新时间戳始终是任何给定列的默认值。时间戳的一个常见用例是版本控制,即将新单元写入某个列,该单元通过时间戳与该行和列的先前数据版本区分开来。

DynamoDB 没有此类概念,并且需要复杂的架构设计才能支持版本控制。此方法涉及为每个项创建两个副本:一个副本的排序键开头的版本号前缀为零(例如 v0_),另一个副本的版本号前缀为 1(例如 v1_)。每次更新项时,您都应在更新后的版本的排序键中使用下一个更高的版本前缀,并将更新后的内容复制到版本前缀为零的项中。这样可确保使用零前缀找到任何项的最新版本。这种策略不仅需要维护应用端逻辑,还会导致数据写入非常耗费资源且速度缓慢,因为每次写入都需要读取上一个值,然后再执行两次写入。

多行事务与大行容量

Bigtable 不支持多行事务。不过,由于它允许您存储比 DynamoDB 中项大得多的,因此您通常可以通过设计架构来将相关项按共享行键分组,从而实现预期的事务性。如需查看演示此方法的示例,请参阅单表设计模式

存储大型值

由于 DynamoDB 项(类似于 Bigtable 行)的大小限制为 400 KB,因此若要存储大值,您需要将该值拆分到多个项中,或者将其存储在 S3 等其他媒体中。这两种方法都会增加应用的复杂性。相比之下,Bigtable 单元格最多可存储 100 MB,Bigtable 行最多可支持 256 MB。

架构转换示例

本部分的示例会将架构从 DynamoDB 转换为 Bigtable,同时注意键架构设计方面的差异。

迁移基本架构

商品目录是演示基本键值对模式的一个很好的示例。在 DynamoDB 中,此类架构可能如下所示。

主键 属性
分区键 排序键 说明 价格 缩略图
帽子 fedoras#brandA 采用优质羊毛… 30 https://storage…
帽子 fedoras#brandB 耐用防水帆布,可承受各种严苛环境。 28 https://storage…
帽子 newsboy#brandB 为日常造型增添一抹复古魅力。 25 https://storage…
鞋子 sneakers#brandA 搭配以下设备,时尚出行,舒适自在 40 https://storage…
鞋子 sneakers#brandB 经典设计与现代材质相结合… 50 https://storage…

对于此表,从 DynamoDB 映射到 Bigtable 非常简单:您只需将 DynamoDB 的复合主键转换为复合 Bigtable 行键。您可以创建一个包含同一组列的列族 (SKU)。

SKU
行键 说明 价格 缩略图
帽子#费多拉帽#品牌 A 采用优质羊毛… 30 https://storage…
帽子#浅顶软呢帽#品牌 B 耐用防水帆布,可承受各种严苛环境。 28 https://storage…
帽子#报童帽#品牌 B 为日常造型增添一抹复古魅力。 25 https://storage…
鞋子#运动鞋#品牌 A 搭配以下设备,时尚出行,舒适自在 40 https://storage…
shoes#sneakers#brandB 经典设计与现代材质相结合… 50 https://storage…

单表设计模式

单表设计模式可将关系型数据库中的多个表合并到 DynamoDB 中的单个表中。您可以采用前面的示例中的方法,并在 Bigtable 中照搬此架构。不过,最好在此过程中解决架构问题。

在此架构中,分区键包含视频的唯一 ID,这有助于将与该视频相关的所有属性放置在一起,以便更快地访问。鉴于 DynamoDB 的项大小限制,您无法在单行中放置无限数量的自由文本注释。因此,使用模式为 VideoComment#reverse-timestamp 的排序键可将每个评论作为分区中的单独行,并按倒序排序。

假设此视频有 500 条评论,并且所有者想移除该视频。 这意味着,所有评论和视频属性也需要一并删除。如需在 DynamoDB 中执行此操作,您需要扫描此分区中的所有键,然后发出多个删除请求,并对每个请求进行迭代。DynamoDB 支持多行事务,但此删除请求太大,无法在单个事务中执行。

主键 属性
分区键 排序键 UploadDate 格式
0123 视频 2023-09-10T15:21:48 {"480": "https://storage…", "720": "https://storage…", "1080p": "https://storage…"}
VideoComment#98765481 内容
我非常喜欢这个。特效很棒。
VideoComment#86751345 内容
1:05 处似乎有音频故障。
VideoStatsLikes 计数
3
VideoStatsViews 计数
156
0124 视频 2023-09-10T17:03:21 {"480": "https://storage…", "720": "https://storage…"}
VideoComment#97531849 内容
我已与所有好友分享了此信息。
VideoComment#87616471 内容
这种风格让我想起了一位电影导演,但我说不出是谁。
VideoStats ViewCount
45

在迁移过程中修改此架构,以简化代码并加快数据请求速度并降低费用。Bigtable 行比 DynamoDB 项的容量大得多,并且可以处理大量评论。如需处理视频收到数百万条评论的情况,您可以设置垃圾回收政策,以便仅保留固定数量的最新评论。

由于计数器可以更新,而无需更新整个行,因此您也不必对其进行拆分。您无需使用“UploadDate”列或计算反向时间戳并将其用作排序键,因为 Bigtable 时间戳会自动为您按时间倒序显示评论。这大大简化了架构,如果移除某个视频,您可以通过单个请求以事务方式移除视频的行(包括所有评论)。

最后,由于 Bigtable 中的列按字典顺序排序,因此为了进行优化,您可以重命名列,以便在单个读取请求中快速扫描范围(从视频属性到前 N 条最新评论),这正是您在加载视频时想要执行的操作。之后,您可以随着观看者滚动浏览,翻页查看其余评论。

属性
行键 格式 赞的次数 视图 UserComments
0123 {"480": "https://storage…", "720": "https://storage…", "1080p": "https://storage…"} @2023-09-10T15:21:48 3 156 我非常喜欢这个。特效很棒。@ 2023-09-10T19:01:15
1:05 处似乎有音频故障。@ 2023-09-10T16:30:42
0124 {"480": "https://storage…", "720":"https://storage…"} @2023-09-10T17:03:21 45 这种风格让我想起了一位电影导演,但我说不出是谁。@2023-10-12T07:08:51

邻接列表设计模式

不妨考虑一下此设计的略有不同的版本,DynamoDB 通常将其称为邻接列表设计模式。

主键 属性
分区键 排序键 DateCreated 详细信息
Invoice-0123 Invoice-0123 2023-09-10T15:21:48 {"discount": 0.10,
"sales_tax_usd":"8",
"due_date":"2023-10-03.."}
Payment-0680 2023-09-10T15:21:40 {"amount_usd": 120, "bill_to":"John…",
"address":"123 Abc St…"}
Payment-0789 2023-09-10T15:21:31 {"amount_usd": 120, "bill_to":"Jane…",
"address":"13 Xyz St…"}
Invoice-0124 Invoice-0124 2023-09-09T10:11:28 {"discount": 0.20,
"sales_tax_usd":"11",
"due_date":"2023-10-03.."}
Payment-0327 2023-09-09T10:11:10 {"amount_usd": 180, "bill_to":"Bob…",
"address":"321 Cba St…"}
Payment-0275 2023-09-09T10:11:03 {"amount_usd": 70, "bill_to":"Kate…",
"address":"21 Zyx St…"}

在此表中,排序键不是基于时间,而是基于付款 ID,因此您可以使用其他宽列模式,并在 Bigtable 中将这些 ID 作为单独的列,这样做的好处与上例类似。

账单 付款
行键 详细信息 0680 0789
0123 {"discount": 0.10,
"sales_tax_usd":"8",
"due_date":"2023-10-03.."}
@ 2023-09-10T15:21:48
{"amount_usd": 120, "bill_to":"John…",
"address":"123 Abc St…"} @ 2023-09-10T15:21:40
{"amount_usd": 120, "bill_to":"Jane…",
"address":"13 Xyz St…"}
@ 2023-09-10T15:21:31
行键 详细信息 0275 0327
0124 {"discount": 0.20,
"sales_tax_usd":"11",
"due_date":"2023-10-03.."}
@ 2023-09-09T10:11:28
{"amount_usd": 70, "bill_to":"Kate…",
"address":"21 Zyx St…"}
@ 2023-09-09T10:11:03
{"amount_usd": 180, "bill_to":"Bob…",
"address":"321 Cba St…"}
@ 2023-09-09T10:11:10

如前面的示例所示,通过正确的架构设计,Bigtable 的宽列模型可以非常强大,并支持许多在其他数据库中需要昂贵的多行事务、次级索引或删除时级联行为的用例。

后续步骤