DynamoDB已经很久没有发表论文了。几天前,我读了它最新发表的一篇文章,“Amazon DynamoDB:一个可伸缩的、可预测的性能和完全管理的NoSQL数据库服务”,我认为从工程角度来看,这是近年来针对大规模分布式系统最实用的论文之一。它甚至没有解释太多的架构,因为它不需要解释。无共享系统看起来很相似,作者很清楚这篇论文的读者是谁_。更重要的是,这篇论文写得很简单,没有数学!
毕竟,DynamoDB不需要尝试“证明”任何东西。在过去的十年中,它已经在规模和稳定性方面充分证明了自己。我发现这篇论文相对较新,也没有太多博客谈论它,所以我为什么不写点什么呢?
可预测的性能>高性能
我一直相信稳定的慢比不稳定的快好。99延迟比平均延迟更能反映系统设计技能。
我不知道这是否是有意的,但它也在DynamoDB 2022论文的第一部分中展示了其重要性。
DynamoDB希望提供可预测的性能。第一步是对工作负载进行抽象,引入了读容量单元(RCU)和写容量单元(WCU)概念。事实上,RCU和WCU非常接近传统意义上的每秒查询量(QPS):只要添加目标项的大小,就可以进行相对准确的工作负载规划。例如,1 WCU = 1 KB项目的1个QPS。当用户可以用RCU和WCU来描述工作负载时,实现可预测性的第一步就完成了。DynamoDB的调度器可以做很多事情,比如预分区和预分配资源,因为不同模型的硬件功能被抽象为WCUs和RCUs的组合。
一旦您知道了每个分区的配额,对于调度来说,这可能是一个背包问题。DynamoDB考虑同一台机器上的分区的配额之和。这个总和应该小于机器在分配分区配额时所能提供的总rcu或wcu,这大约是本文给出的示例的20%-30%。在现实世界中,没有经验的开发人员或系统管理员通常会把最后一点CPU和I/O从机器中挤出来,以追求“终极”性能。在满足它们之前,它们必须看到100%的CPU使用率。然而,在这种情况下,机器已经处于非常不健康的状态。请求的长尾延迟将变得非常高,即使吞吐量可能会增加,但由于这种不稳定的长尾,从用户的角度观察到的性能将是“不可预测的”。在生产环境中,出于同样的原因,我们建议过度配置大约30%的机器。
Burst是一个简单的想法。在分配配额时,DynamoDB为每个分区保留一些容量。当短期内业务量激增时,使用预留的容量。自适应容量在用户的工作负载发生倾斜时动态调整不同分区的配额(但总配额不能超过表的总配额)。
需要注意的是,突发容量和自适应容量是基于以下假设的:用户的工作负载变化不大,流控制集中在分区级别(几乎在存储节点级别),即本地调度。
DynamoDB论文的这部分内容有些模糊。我对该策略的理解是,请求路由器定期向全局允许控制(GAC)应用请求配额。广汽保持全球分配政策;如果一个分区已经过载,相应的请求路由器可以直接拒绝向客户提供服务,以保护系统的其余部分。分区级别的流控制也被保留为节点级别的最后一道防线。
对于无共享存储系统,向外扩展的关键是分片策略。DynamoDB选择动态分片策略。它也使用范围分割,但区别在于DynamoDB的分区概念使用大小作为默认的分割阈值。DynamoDB直接采用基于负载的拆分。分区有一个默认吞吐量阈值,当吞吐量阈值超过该值时,将对其进行分割。一旦开始监视键范围内的负载分布状态,就很容易获得最佳分割点(并不总是中点)。
另一个重要的分裂概念是什么时候避开它。本文提到的:
- 单排热点
- 访问模式是键的顺序(类似于Key上的迭代)。在这种情况下,DynamoDB避免分裂。
总结一下,DynamoDB可预测性能的核心是:
-
使用更精确的工作负载(WCU和RCU)抽象,而不是简单的每秒事务数(TPS)、QPS和数据大小。
-
预分配配额给分区,严格限制流量。
-
在节点级上留下空白,作为突发场景(Burst)的临时资源池。
-
使用全局信息进行流量调度和控制所有级别的流量。
以尽可能小的可观察影响进行故障转移
让我们首先讨论预写日志(WAL)。DynamoDB通过分布式共识算法(DynamoDB的Multi-Paxos)将日志复制到多个副本(本文暗示默认为三个)。然而,DynamoDB还定期将wal(它不是DB快照)同步到S3,以获得更高的持久性。我知道这不仅是为了更高的耐久性,也是为了时间点恢复(PITR)。
关于故障转移的另一个有趣的细节是,当DynamoDB复制组中的一个节点发生故障时(例如,如果三个副本中的一个副本发生故障),组组长立即向复制组添加一个日志副本。日志副本实际上就是我们通常所说的目击者。我还将使用下面的见证,而不是日志副本。仅存储日志,不对外提供服务。这是一种非常聪明的方法,因为在上述情况下,尽管它也符合大多数人的要求,但这个系统此时非常脆弱。要完全恢复一个新成员,时间通常更长(首先复制DB快照,然后应用最近的日志),特别是如果分区快照相对较大。复制快照的过程可能会给现有的对等体带来额外的压力。
增加一个证人的成本很低。文中提到的时间以秒为单位。至少可以保证数据恢复过程中日志的安全性。此外,对于跨可用分区(AZ)部署场景,这种优化还可以减少故障转移阶段的写延迟。例如,我们有两个az,其中一个是主az。我们称之为AZ-A,它承载着主要的写流量。另一个AZ AZ- b是备用的,用于容灾(或服务一些本地读流量)。当AZ-A中的一个节点挂起时,该节点数据的Raft组不停止写入(leader改选后)。但根据经典Raft,为了满足多数人的要求,它必须向AZ-B中的对等体询问,并确保日志成功保存。然后它可以将成功返回给客户端,这意味着从客户端观察到性能抖动(直到AZ-A中添加了一个新的对等体)。当我们检测到一个节点故障时,立即在AZ-A中找到一个健康的节点作为见证,并将其加入到这个不健康的组中,对AZ-A的写仍然可以达到多数,从而节省了将日志同步到AZ-B的副本的延迟。从客户的角度来看,系统没有显示出明显的抖动。

为了减少故障转移期间可观察到的影响,DynamoDB还改进了复制组的Leader选举。对于大型系统,网络抖动或网络分区是常见的。例如,复制组中有一个名为peer X的对等体。根据传统的选举协议,一旦X无法连接到组长,它就会轻率地发起一个投票期限更大的新选举。其他同龄人停止投票。从用户端观察,这部分数据是不可用的(为了数据一致性,服务将在选举期间停止),但旧的leader可能仍然活着。
这是一个常见的问题,但解决方案非常简单。当一个同僚想发起选举时,它首先询问其他同僚是否会投票,确认年长的领袖是否在世或是否有空。如果没有,那么议员可以发起新的选举。DynamoDB论文也提到了类似的机制。在一个节点发起新的选举之前,它会询问其他节点是否也认为旧的leader断开了连接,如果不是,就意味着这是候选人自己的问题,不影响正常的节点。

大型系统最严重的故障是级联故障。2015年的DynamoDB故障就是一个典型的例子。本文中提到的元数据服务的改进让我想起了这个案例。(我猜可能就是因为这个案子,才有了这些改进。)这个解决方案非常聪明,所以我将稍微解释一下。
DynamoDB观察到,级联故障的根本原因之一是短时间内的流量突变。导致流量突变的常见原因之一是缓存失败。虽然我们大多认为缓存命中率越高越好(文中提到分区路由器表的缓存命中率约为99.75%),但如此高的缓存命中率意味着,当出现缓存故障(或大量新节点加入时的缓存预热阶段)时,元数据服务必须能够承载400倍的流量激增(最坏情况下,0.25%→100%)。DynamoDB解决这个问题的方法如下:
-
DynamoDB在请求路由器和元数据服务中间添加了一层分布式内存缓存MemDS。当请求路由器的本地缓存丢失后,它不会直接访问元服务,而是先访问MemDS。然后MemDS访问后台的元数据服务来填充数据。为峰值剃除添加一层缓存相当于添加另一层保险,这是一种常见的方法。
-
第二种方法非常聪明。我刚刚提到,当请求没有到达MemDS中的缓存时,请求路由器通过MemDS获取元数据。这很容易理解。但真正聪明的是,即使缓存命中,MemDS也会异步访问元数据服务。的原因是:
-
它确保MemDS中现有的缓存被尽快更新。
-
它为元数据服务带来“稳定的”流量(尽管它可能更大)。
- 基于有限令牌(容量)的GAC减少了级联故障的影响。
此外,对于云服务来说,一个很大的优势是更新比传统企业软件发布得更快。然而,新版本的部署通常是系统最脆弱的时候,而DynamoDB作为一个大型系统,不太可能进行离线更新。对于滚动更新,更新过程中节点的新旧版本将同时存在。因此,新版本需要与旧版本的节点进行通信,待所有节点部署新版本后,再切换到新版本协议。
DynamoDB在稳定性和故障转移方面的工作可以总结为一句话:最小化可观察的客户端影响,我认为这是“可预测的”性能的一部分。
数据库≠数据库即服务
我想说十年前报纸上描述的发电机更像DB(笑)。这个DynamoDB实际上是一个准确的数据库即服务(DBaaS)。你可能想知道有什么不同。我认为构建DBaaS不仅仅是将多个数据库实例部署到云上并托管它们。从用户的角度来看,DBaaS提供的端点可以充当数据库实例,但在底层,实现可能没有那么简单。举一个极端的例子:假设DBaaS将SQLite作为服务提供。我认为它不太可能真正为每个用户创建一个新容器,提供环境,然后启动一个SQLite进程公开它。它很可能是一个共享服务,只是在外部表现与SQLite相同,以便更好地利用资源。
因此,要构建DBaaS,首先要考虑的是多租户。必须重新设计DynamoDB的原因是旧的Dynamo不支持多租户。这对于云服务来说是不可接受的。在向云迁移时,我们了解到原生云并不是简单地将数据库迁移到云上并为不同的用户部署它。对于云提供的功能和环境,它需要从内核到控制平台的深度转换。
DBaaS与数据库的另一个显著区别是,DBaaS通常很难在本地部署。事实上,现代DBaaS是用许多微服务构建的,或者严重依赖云供应商提供的特性(特别是存储和安全相关的服务)。您还可以在DynamoDB论文中看到,请求路由器是一种负责来自不同租户的连接的服务;GAC是一种服务;认证系统是一种服务;元数据是一种服务,存储是一种服务。这还不包括对S3/IAM等其他服务的依赖。有趣的是,这篇论文没有提到任何EC2或EBS,这让我猜测DynamoDB的硬件基础设施可能是由它自己维护的;也就是说,它运行在一个裸金属机器上。
最后,以下是我从这篇论文中得出的结论:
-
您对工作负载的抽象理解得越多,构建可预测的系统就越好。工作负载的度量越细,您就有更多赚钱(或节省成本)的空间。
-
从一开始和全局的角度考虑系统的多租户。
-
在云中,访问层非常重要。我们可以在访问层做很多事情来提高系统的可预测性,包括流控制、高可用性、租户隔离和不间断更新。
-
对于抽象调度,在不同层次上进行流量控制。
-
使用微服务来构建实现多租户的平台。
-
使用云基础设施将节省大量工作,例如S3。
在阅读了这篇论文之后,我觉得它有很多东西没有解决,包括无服务器架构、GC策略和分布式事务(ACID),但这些都不能阻止这篇论文成为经典。我从这篇论文中学到了很多。
原文标题:Key Takeaways After Reading the Latest Amazon DynamoDB Paper
原文作者: Ed Huang
原文地址:https://dzone.com/articles/key-takeaways-after-reading-latest-amazon-dynamodb-paper




