DynamoDB 为何能保证个位数毫秒延迟 - 分区分割与请求路由器的内部架构

从分区分割算法、请求路由器、存储节点的三层架构解析 DynamoDB 如何在任意规模下维持个位数毫秒延迟。

个位数毫秒承诺的含义

DynamoDB 官方宣称「无论表大小或请求量如何,均可在个位数毫秒延迟内完成读写」。这一承诺对 1KB 的表和 100TB 的表、每秒 10 个请求和每秒 1000 万个请求同样成立。在关系型数据库中,随着表规模增大,索引深度增加,磁盘 I/O 增多,延迟会劣化。DynamoDB 能避免这一问题,是因为其数据存储方式和访问路径从根本上不同。DynamoDB 将数据分割为称为分区的小单元,并将各分区放置在独立的存储节点上。请求始终针对单个分区执行,因此表的整体大小不会影响单个请求的延迟。这就是「与规模无关的恒定延迟」的核心。

分区分割 - 自动水平分散数据的机制

DynamoDB 的分区分割完全自动化,用户无需关注。每个分区最多存储 10GB 数据,可处理最多 3,000 RCU(Read Capacity Units)或 1,000 WCU(Write Capacity Units)的吞吐量。当表的数据量或吞吐量达到上限时,DynamoDB 会自动分割分区。数据根据分区键的哈希值分配到各分区。关键在于分区键的设计。如果所有请求集中在同一分区键,该分区会成为热点,达到吞吐量上限后触发限流。2019 年引入的自适应容量可自动重新分配热分区的吞吐量,但根本解决方案是通过分区键设计均匀分散访问。例如,以日期作为分区键会导致「今天」的分区访问集中。通过在日期后添加随机后缀的「写入分片」模式来分散是标准做法。

请求路由器 - 瞬间定位正确分区

DynamoDB 的请求路由器接收客户端请求,根据分区键的哈希值确定目标分区,并将请求转发到持有该分区的存储节点。AWS 在 2022 年的论文「Amazon DynamoDB: A Scalable, Predictably Performant, and Fully Managed NoSQL Database Service」中公开了请求路由器的内部结构。请求路由器在内存中缓存分区映射(哪个分区键范围位于哪个存储节点),路由判断在微秒级完成。当分区分割或迁移发生时,分区映射异步更新。如果临时引用了旧映射,存储节点会返回重定向响应,请求路由器更新映射后重试。这一机制确保分区重新配置期间请求处理不会中断。请求路由器有多个实例并行运行,不存在单点故障。

存储节点与复制 - 三重写入机制

每个分区的数据复制到 3 个存储节点。写入请求首先由领导者节点接收,然后复制到 2 个跟随者节点。当 3 个节点中的 2 个(领导者 + 1 个跟随者)写入完成时,向客户端返回成功响应。这就是「2 of 3 仲裁写入」。读取有两种模式。最终一致性读取(Eventually Consistent Read)从 3 个节点中的任意一个读取。可能未反映最新写入,但延迟最低。强一致性读取(Strongly Consistent Read)从领导者节点读取,始终返回最新数据,但由于负载集中在领导者节点,吞吐量为最终一致性读取的一半。2023 年发表的 DynamoDB 论文揭示存储节点使用基于 B-tree 的存储引擎。选择 B-tree 而非 LSM-tree(Log-Structured Merge-tree)是为了优先保证读取延迟的可预测性。

Global Tables 与 DAX - 进一步缩短个位数毫秒延迟

DynamoDB 的个位数毫秒是同一区域内的延迟。对于全球部署的应用,需要在离用户最近的区域读写数据。Global Tables 自动在多个区域创建表的副本,提供任意区域可读写的多主复制。东京区域的写入在数秒内传播到弗吉尼亚和法兰克福的副本。副本间为最终一致性,但同一区域内的读写与普通 DynamoDB 相同,保持个位数毫秒延迟。如需更低延迟,可使用 DAX(DynamoDB Accelerator)。DAX 是部署在 DynamoDB 前端的内存缓存,将读取延迟缩短到微秒级(数百微秒)。DAX 与 DynamoDB API 兼容,应用代码只需更换端点即可。但 DAX 是缓存,写入后立即读取可能返回旧数据。需要考虑缓存 TTL 设置和写后读取模式的设计。要深入理解 NoSQL 数据库设计模式,专业书籍 (Amazon)可供参考。