[论文阅读]在MongoDB中实现集群范围内的逻辑时钟和因果一致性(三)
开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第24天,点击查看活动详情
这篇没有太多东西,描述了生产HLC的代价和性能模型,以及缓存签名来优化CPU。 下图展示了效果:非持久写(为了刨去IO的影响)随着线程增加而scale,所以优化挺成功。
7 性能优化
工程团队对一个新版本的性能测量的关键测试之一是确保新功能没有导致不可接受的系统性能减慢。具体来说,我们想确认传统工作负载和关闭了因果一致性的工作负载的性能不会因为测量签名生成和验证的消息处理开销而受到影响。为了隔离这些代码路径,我们使用带有指令计数器的代码来直接量化性能影响。
7.1 测量和优化签名生成和验证的开销
生成一个签名来保护ClusterTime不被非授权用户改变,每条信息都会增加性能开销。测量是为了确定这个开销的确切值。测试是在MongoDB 3.6.0的3个节点副本集上进行的,测量了通过rdtscp CPU指令生成签名的耗时:
作为对比,这是单纯插入的cost:
因此,签名的生成可能占到工作负荷的10%。为了减轻开销,我们实施了一些优化措施。第一条路径是在不需要的情况下禁用签名和验证:在没有不受信任的客户端的情况下--也就是说,实现在认证边界内。对于这些情况,我们增加了一个系统特权 advanceCluserTime。与拥有此权限的用户往来的信息可以通过使用假签名跳过签名和验证过程。
第二条优化路径是签署一个时间范围,所以不是每个增量都需要验证。由于集群时间是线性增长的,我们总是可以把表示中最不重要的比特屏蔽为'1',签署它,并缓存签名。有可能的是,下一个消息的ClusterTime值会递增1,从而得到相同的屏蔽值。然后,mongod可以重新使用缓存的签名。
在我们的实验中,这种方法将签名生成和验证所需的CPU负载降低了几百倍。
表3:带缓存的签名生成
7.2 结果讨论
结果表明,因果一致性签名的生成和验证开销对现有的应用来说是可以忽略不计的,这些应用主要是通过从主要的主机模式进行读写,提供可能的因果操作顺序。
8 功能测试
除了可以修改信息的恶意用户,系统还必须在自动发生的故障切换、滚动升级、数据平衡和加密密钥旋转的情况下提供正确的结果。失败情况必须在产品出厂的每个平台上进行验证,由于MongoDB每天都会收到大量的代码修改,所以必须定期重复测试。这些功能测试是通过MongoDB开源持续集成系统Evergreen[30]运行的。MongoDB已经实现了数千个测试,以验证多年来开发的功能,运行因果一致性预计不会改变其输出。因此,我们已经在因果一致性会话中运行了相关的测试子集,而读取则被迫在次要环境中进行。测试基础设施允许在不同环境中运行相同的测试脚本。特别是我们有一个测试变体,在连续故障切换期间运行测试。新的功能,如加密密钥自动轮换,有一个单独的测试脚本,并包括在常青运行中。
建立测试基线的好处之一是能够在早期运行测试:在进行概念验证的时候。这样,我们就能够在设计阶段验证设计决定。
9 性能评估
本节介绍了在不同条件下使用因果一致性的性能成本评估。首先,我们在一个特定的工作负载上建立了一个系统性能的基线,然后结合其他设置来衡量启用因果一致性的影响,显示这对性能和可扩展性的影响。最后,我们展示了用户如何通过增加其应用程序的吞吐量
集群中的节点,并在启用因果一致性的情况下从第二级节点安全地读取。
9.1 实施和实验设置
对于我们的硬件,我们通过MongoDB Atlas[29]在AWS us-east-1区域配置了一个单副本集。每个节点有15.25GB的内存,2个vCPUs和极低延迟的基于NVMe的SSD存储。虽然所有的节点都在同一个地理区域,但它们都在不同的可用区。我们选择了相对较小的实例规模,以便为任何想复制我们的结果的人保持较低的成本,并使其容易看到工作负载设置中相对较小的变化所产生的影响。我们在复制集中的节点数量从3到5不等。对于我们的客户,我们在同一个AWS区域使用了一个r4.8xlarge实例(32个vCPU和244GB的内存)。
9.2 workload
为了测试因果一致性,我们必须创建一个工作负载,在一个逻辑会话中执行多个连续操作,而不是独立的点查询,因此我们使用了一个松散地基于TPC-C基准中使用的商业相关操作类型的工作负载。
[37] 这涉及到每个客户在一个会话中进行读和写的混合(订单、交付、付款)或只是一系列的读(库存水平检查、订单状态检查)。性能测试是在不启用MongoDB事务的情况下进行的,但使用了逻辑会话,并对因果一致性、读取偏好(主要或最近)、写入关注以及客户端线程数量进行了不同的设置。
9.3 基准
首先,数据库被预先填充了由100个仓库的TPC-C基准产生的数据,在磁盘上产生了大约8.5GB的压缩数据,转化为内存中的12.5GB的数据。然后,我们运行了各种操作,包括更典型的TPC-C读写操作的混合,我们通过记录TpmC来测量吞吐量,以及只读操作,我们测量每秒的读数。每个配置参数的组合都运行了5分钟,线程数量不断增加,直到吞吐量开始下降或延迟变得非常高。除了比较因果一致性的开与关,我们还想了解我们的用户为他们的应用程序选择的典型设置的相对性能。
9.4 与非持久性写作的因果一致性
许多要求低延迟写入的应用无法等待每个写入的复制确认,而使用w:1写关注,这意味着如果出现故障切换,一些写可能会被回滚。他们同样需要低延迟的读取,所以将读取请求发送到最近的节点,不管是主节点还是次节点,都是他们想要的配置,尽管这可能导致应用程序看到不一致的数据视图。我们比较了在这种配置中打开因果一致性的影响。我们的实验表明,虽然总体操作数量较少,但在线程数量较多的情况下,其影响并不明显。
图3.按线程非持久性写入的扩展。
9.6 持久写入的因果一致性
另一方面,不能容忍不一致的数据视图的应用更有可能要求数据在大多数副本集上是持久的,然后才认为是成功的写入。多数写关注设置的较高延迟可以通过增加线程来缓解,但是从没有因果一致性的第二层读取数据仍然可以返回不一致的数据。我们的实验表明,在多数人写的情况下,启用因果一致性对整体吞吐量的影响很小,对许多用户来说,这可能是可以接受的设置。
图4.随着线程scale的持久写
9.7 按节点数量扩展
我们想测试一下,系统能够处理的读取数量会随着集群中可用的读取节点数量的增加而增加,所以我们对只读工作负载进行了类似的测试,正如预期的那样,每秒的总体读取量随着副本集中的总节点数量和读取偏好的增加而几乎呈线性增长。
图5.通过二级节点进行扩展。
10 未来的工作
全集群逻辑时钟的实现为许多需要全集群数据排序的功能提供了基础。例如,变化流[33]的可恢复性是基于ClusterTime的。从长远来看,我们的团队致力于建立多文档ACID事务。集群时间允许在所有集群操作中建立一个一致的逻辑时间。这反过来又需要建立集群范围内的快照隔离,这是实现分布式交易交付的重要一步。
随着云供应商为同步进程墙时钟暴露出更高质量的时间源[3],未来工作的另一个有趣的途径是利用更好的同步时钟来消除因果一致性实现中的停滞和多余的写入。这些同步服务在多大程度上允许第三方应用开发者实现类似于谷歌TrueTime[14]的系统,也是实践中一个有趣的问题。
11.总结
MongoDB将研究界的想法结合到一个安全的生产级实现中,支持因果一致性。该实现增加了一个线性处理开销,容忍可能的操作错误,并在一个多托管、复制的分布式数据库中提供部分因果事件排序。
我们设计的一个方面很突出:通过仅在服务器上增加clusterTime并使用安全密钥进行签名,来防止非信任客户的攻击。该实现通过在主服务器上生成一个noop写来推进逻辑时钟的持久值来保证协议的有效性。