杰普森驳斥MongoDB的数据一致性主张

2020-05-24 09:08:07

在一篇题为“MongoDB和Jepsen”的文章中,MongoDB声称他们的数据库通过了“业界最严格的数据安全性、正确性和一致性测试”。作为回应,Jepsen发表了一篇文章,声称MongoDB 3.6.4实际上没有通过他们的测试;而更新的MongoDB 4.2.6有更多的问题,包括“回溯因果事务”,即事务颠倒顺序,以便读取可以看到未来写入的结果。

杰普森有限责任公司(Jepsen LLC)的回应始于他们在官方Twitter上对Maxime Beugnet的回复:

我不得不承认,当我看到那个网页时,我感到惊讶。在该报告中,MongoDB在默认情况下丢失了数据并违反了因果规则。不知何故,这成为当今所有可用的数据库中数据一致性、正确性和安全性保证最强的数据库之一!

这份报告被Kit Patella命名为MongoDB 3.6.4。凯尔·金斯伯里(Kyle Kingsbury)的这份新报告对此进行了扩展:

类似地,MongoDB的默认读取关注度允许中止读取:读取器可以观察到未完全提交的状态,并且在将来可能会被丢弃。正如读隔离一致性文档所指出的,“未提交的读操作是默认的隔离级别”。

我们发现,由于这些弱缺省值,MongoDB的因果会话在缺省情况下不能保持因果一致性:用户需要同时指定写和读关注点多数(或更高)才能实际获得因果一致性。MongoDB解决了这个问题,说它正在按设计工作,并更新了他们的隔离文档,指出即使MongoDB提供了“客户端会话中的因果一致性”,但除非用户小心地同时使用读和写关注点多数,否则这一保证是站不住脚的。现在有一个详细的表显示了较弱的读写关注点提供的属性。

近年来,MongoDB一直在大力推广其事务功能。但正如杰普森发现的那样,默认情况下,事务支持不起作用。在一个测试中,使用事务将值追加到文档。他们发现,即使数据库/集合级别的写关注占大多数,当在事务级别使用默认的写关注时,“事务似乎会丢失确认的写入”。(这可以通过在事务级别显式指定写入关注点来解决。)。

客户端观察到一个单调增长的元素列表,直到[1 2 3 4 5 6 7],此时列表重置为[],并从[8]重新开始。这可能是MongoDB回滚的一个例子,这是“数据丢失”的一种花哨的说法。

这很糟糕,但一个更微妙的问题出现了:我们到底为什么能够读取这些值?毕竟,Read Concerns Linizable应该只显示大多数确认的(即持久的)写入。答案是一个令人惊讶但有文档记录的MongoDB设计选择:

事务中的操作使用事务级别的读取关注点。也就是说,在集合和数据库级别设置的任何读取关注点在事务内都会被忽略。

这实际上意味着“没有显式读取关注点的事务将数据库或集合级别的任何请求的读取关注点降级为缺省的本地级别”,从而允许事务读取可能稍后回滚的未提交数据。

反之亦然。根据文档,“如果事务没有对提交使用写关注点”多数“,则快照读取关注点不能保证读取操作使用了提交多数的数据的快照。”换言之,在不设置写关注点的情况下,有效地忽略了读关注点“快照”。同样,这必须在事务级别完成,因为事务会忽略集合和数据库级别的设置。

即使使用快照隔离,也有许多场景会产生意想不到的结果。它们中的大多数都太复杂了,无法在这里概括,但其中有一个确实很突出。

在一项测试中,杰普森的研究人员告诉客户阅读一份文档,然后给它附加一个值。在测试开始时,文档包含序列[2,3,4]。读取该值后,文档被更改为[1,2,3,4]。

这通常是有效的,但在四个事务中,客户端从数据库读取[1,2,3,4]。

当然,这是不可能的:我们的测试严格按照顺序提交每个事务的操作,除非MongoDB构建了时间机器,否则它不能返回它还不知道将被写入的值。这表明回溯因果事务实际上运行了两次,在第二次运行时,观察到了它自己先前执行的效果。这可能是不适当的重试机制造成的另一个后果。

我们发现网络分区可能会导致MongoDB复制事务的影响。尽管从未两次将相同的值附加到一个数组中,但我们反复观察到具有同一元素的多个副本的数组。

为了更好地理解这些行为,研究人员试图禁用自动重试,结果却发现“MongoDB事务忽略了retryWrites设置,不管怎样都重试”。

除了向开发者提供如何更安全地使用MongoDB的建议外,Jepsen还建议“MongoDB可能希望修改他们的营销语言,使用‘快照隔离’而不是‘ACID’”。

编辑注意:本文的前一个版本暗示在使用事务时总是会发生数据丢失。此特定问题仅在使用事务的默认写入关注点时发生。但是,使用写入关注点多数的事务检测到了其他异常。

InfoQ上上周内容的综述每周二都会发布。加入一个超过25万名高级开发人员的社区。查看示例。

选择您的国家/地区我同意InfoQ.com按照本隐私声明中的说明处理我的数据。