进阶阅读约 3 分钟

DynamoDB 强一致读取与最终一致读取

你更新一个项,立刻把它读回来,却拿到了旧值。写入是成功的——片刻之后同一次读取就返回了新值。没有任何东西坏掉:你撞上了 DynamoDB 默认的最终一致读取,而你可以按请求选择退出它。

这是 DynamoDB 直接交到你手里的少数几个正确性旋钮之一,而它带着一个实实在在的代价。用对它意味着要懂每种模式保证什么、它花费多少,以及在哪里根本没有强一致读取可用。

DynamoDB 中的强一致读取与最终一致读取有什么区别?

最终一致读取(默认)由任意一个副本提供服务,因此在一次写入之后它可能短暂返回陈旧数据,但成本只有一半。强一致读取用 ConsistentRead=true 按请求选择启用,会被路由到分区的主导节点,并且总是反映每一次已提交的写入——代价是 2 倍的读取容量。

  • 最终一致(默认)——在一次写入之后可能短暂返回陈旧数据。最便宜的读取模式。
  • 强一致——总是反映读取之前已提交的每一次写入。用 ConsistentRead=true 按请求选择启用。
  • 强一致读取的成本是最终一致的 2 倍。 对于相同的数据,一次强一致读取消耗的读取容量是一次最终一致读取的两倍。
  • 并非处处可用。 你能在基础表和本地二级索引上拿到强一致读取。全局二级索引只支持最终一致——无法选择启用。
  • 默认用最终一致。 只有在读取你自己刚刚写入的数据、且差一瞬间的陈旧就会出错时,才去用强一致。

问题:一次看不到最新写入的读取

假设你运营用户账户。一个用户修改了他的通知邮箱,你的应用写入了这次更新,确认页面立刻重新读取资料以显示新地址。在默认读取模式下,那次重新读取可能落在一个还没收到变更的副本上——于是用户看到的是他的邮箱,并以为保存失败了。

这个窗口很小(通常远不到一秒)而且会自行关闭。但对于一次写后立即读的确认来说,"通常正确"还不够好。这正是强一致存在的理由。

为什么会出现最终一致

DynamoDB 把每个分区存储在三个存储节点上——一个主节点和两个副本——分布在不同的可用区。一次写入在落到主节点和一个副本上时就被确认;随后它异步地传播到第三个节点。

为了分摊负载,读取可以由这三个节点中的任意一个来服务。一次最终一致读取可能命中一个还没收到你最近写入的节点——于是它返回一个略微陈旧的值。一次强一致读取会被路由到该分区的主导节点,那里总是持有最新已提交的数据,因此它绝不会返回陈旧结果。

同步,已确认异步,短暂滞后可能命中滞后的节点写入:新邮箱主节点副本 1副本 2强一致读取最终一致读取

那个复制延迟就是全部差别所在。它也解释了 2 倍成本:强一致读取无法像最终一致读取那样在副本间做负载均衡,所以 DynamoDB 按两倍的容量给它定价。

把成本说具体

读取以读取容量单元(RCU)计量,每个 RCU 覆盖最大 4 KB。对于一个 4 KB 的项,一个 RCU 可买一次强一致读取两次最终一致读取。所以在一个热读取路径上翻开 ConsistentRead=true 会让它的读取成本翻倍——在一个高流量端点上,那是一笔你会注意到的开销条目。

在把强一致读取设为默认之前,用 DynamoDB 定价计算器针对你自己的项大小和请求速率把差别建模出来——全面付双倍代价很少值得。

强一致读取在哪里可用(在哪里不可用)

读取对象是否强一致?
基础表是——用 ConsistentRead=true 选择启用
本地二级索引(LSI)是——与基础表相同的选择启用方式
全局二级索引(GSI)——仅最终一致,无法覆盖

一个 GSI 维护它自己的一份数据副本,从基础表异步复制而来,所以它永远无法提供强一致读取。如果某个访问模式确实需要写后立即读,而你原本打算用一个 GSI 来服务它,那就是一个信号——应该改用基础表或一个 LSI 来服务它。

陷阱与后续步骤

  • 别把强一致读取设为默认。 大多数读取能容忍一个亚秒级的陈旧窗口;处处付 2 倍是浪费的开销。
  • 别指望从 GSI 做写后立即读。 它在设计上就是最终一致的——参见为什么 GSI 是最终一致的
  • 事务是强一致读取。 TransactGetItems 始终是强一致的——参见 DynamoDB 事务
  • 一致性与容量相互影响。 那个 2 倍乘数直接关联到按需与预置的成本规划。

想在不写 API 调用的情况下探索你的 DynamoDB 表和索引?下载 DynoTable 直接检视你的数据。

工作 RCU 比较

对基表上同一个 6 KB 项目的两次读取:

模式4 KB 块RCU 消耗何时使用
强烈一致2(6 KB 向上舍入)2 RCU您自己编写后的确认屏幕
最终一致21 RCU仪表板、列表、分析

如果每秒读取 1,000 次,强模式的成本大约是按需模式的两倍读取最终模式的支出 — 对增量进行建模 pricing calculator 翻热之前全局到 ConsistentRead=true 的路径。

BatchGetItem consistency

BatchGetItem中的每个表项可以独立设置ConsistentRead。一个加载用户配置文件(强)和相关设置(最终)的仪表板可以在一次批量调用中混合标志——仍然受到每表强读的影响可用性规则(GSI 不强)。

应用程序代码中的先读后写

配置文件更新确认模式:

  1. UpdateItem 新电子邮件。
  2. 直接 GetItemConsistentRead: true 位于基表上。步骤 2 的成本是最终读取的 RCU 的 2 倍,但保证了确认屏幕与写入相符。跳过对后台聚合的强读取容忍亚秒级的延迟。

DynoTable defaults

DynoTable 中的探索性读取使用最终一致的基表查询除非您在高级设置中选择更强的语义 - 匹配大多数仪表板用例。暂存写入后,项目编辑器刷新显示来自成功响应的承诺值,无需单独的一致性切换公共路径。使用 query builder 发出样本读取 ConsistentRead 在将 SDK 代码复制到需要的服务时显式设置先写后读保证。

全局表注释

全局表跨区域异步复制。强一致性适用 在一个区域的副本内,而不是全球范围内。写入us-east-1不是在 eu-west-1 中立即具有很强的可读性 - 相应地规划跨区域用户体验。有关复制延迟预期,请参阅global tables

更新于