著名的分布式事务数据库谷歌Spanner设计有坑!

CAP定理指出,在网络分区的情况下,不可能同时保证一致性和可用性。由于网络分区在可扩展的分布式系统中理论上是可行的,因此现代可扩展数据库系统的架构师分为两大阵营:优先考虑可用性的阵营(NoSQL阵营)和优先考虑一致性的阵营(NewSQL阵营)。

有一段时间,NoSQL阵营显然是两者中的主导者 - 在“永远在线”的互联网世界中,停机时间是不可接受的,开发人员被迫降低一致性水平。

在过去的十年中,应用开发人员发现,在不保证一致性的数据库系统上构建无错误的应用程序非常困难。这导致了惊人转变的动力,许多最近发布的系统都声称保证一致性(并且是来自CAP的CP)。包括在新的系统中是:Spanner(和 Cloud Spanner),FaunaDB,CockroachDB和YugaByte。在这篇文章中,我们将更深入地研究这四个系统(以及类似系统)的一致性声明,并注意到虽然有些确实保证了一致性,但是其实并不能完全保证一致性。我们将追踪故障失败,以验证Spanner所做的一致性设计决策,而这在其他系统中已被悲惨地和不完美地模仿。

什么是一致性?
一致性,也称为“原子一致性”或“线性化”,保证一旦写入完成,所有未来的读取都将是立即是刚写入的值。例如,假设我们有一个名为X的变量,其值当前为4.如果我们运行以下代码:
X = 10; Y = X + 8;

在一致的系统中,运行此代码后Y只有一个可能的值(假设第二个语句在第一个语句完成后运行):18。

在一个不保证一致性的系统中,运行此代码后Y的值也可能是18.但是有可能它是12(因为X的原始值是4)。即使系统返回一个明确的消息:“我已经完成了X = 10语句”,但仍然有可能后续读取X将反映旧值(4)而Y将最终为12。

因此,应用程序开发人员必须知道Y不是18的非零可能性,并且必须在后续代码中处理Y的所有可能值。这非常复杂,超出了应用程序开发人员的能力。

[附注:“一致性”的另一个名称是“强一致性”。这个替代名称是为了区分完整一致性保证和较弱的一致性级别而区分的,这些一致性级别在其名称中也使用了“一致性”一词(尽管没有提供完整的一致性保证)。实际上,这些较弱的一致性级别中有一些例如“因果一致性”,“会话一致性”和“有界陈旧性一致性”提供了有用的保证,可以在一定程度上降低应用程序开发人员的复杂性。尽管如此,避免在应用程序中存在错误可能性的最佳方法是在强一致性系统之上构建,以保证完整,强大的一致性。

为什么要放弃一致性?
一致性是基础,如果没有就几乎做不了事,那么为什么大多数NoSQL系统都无法保证一致性呢?他们责怪CAP定理,(例如,启发了许多NoSQL系统的Amazon Dynamo论文,在“设计考虑因素”一节的第一段中提到了可用性与一致性的权衡,他们提出著名的“最终一致”架构。)在不保证一致性的系统上构建应用程序是非常困难的,但并非不可能。

但CAP定理表明,在存在网络分区时保证100%可用性,保证一致性的系统是不可能的。因此,你只能选择一个,那么选择可用性是有意义的。

正如我们上面所说的,一旦系统无法保证一致性,在没有丑陋的案例错误的情况下开发应用程序是非常具有挑战性的,并且通常需要能够处理这种开发环境的知识严谨的高技能应用程序开发人员。尽管如此,这些熟练的开发人员确实存在,并且这是避免CAP定理100%可用性的不可能性证明的唯一方法。

前一段的推理虽然可能经过深思熟虑和令人信服,但从根本上是有缺陷的。CAP定理存在于理论世界中,其中存在100%可用性。在现实世界中,没有100%的可用性。高度可用的系统以“9s”定义。你有99.9%可用吗?或99.99%可用?9s越多越好。可用性从根本上说是一种不完美的追求。没有系统可以保证100%可用性。

当考虑CAP定理所声称的可用性与一致性权衡时,这一事实具有重大影响。如果我们保证一致性,我们就不能放弃可用性的保证。可是我们从来没有保证100%可用性!相反,保证一致性反而更会降低我们已经不完善的可用性。

因此,问题变成:当我们保证一致性时,可用性会丢失多少?在实践中,答案是会丢失很少。

保证一致性的系统仅在网络分区的情况下肯定会经历一些可用性降低。随着网络变得越来越冗余(banq注:数据复制到位),分区变得越来越少见,即使存在分区,仍然可以使多数分区可用的策略(法定人数)。只有少数分区就只能判断为不可用。

因此,除非存在一个客户端:在存在网络分区情况下正在与少数分区(而不是多数分区)中节点进行通信,这个客户端可用性就非常低。但是这种情况通常比系统其他不可用原因更罕见。

因此,同时保证高可用性和一致性在现实中是可能的。


一致性NEWSQL系统的辉煌回归
上面的论点实际上导致现代系统选择CP而不是AP(即选择可用性的一致性)的3个不同原因:
(1)不能保证一致性的系统会导致复杂,昂贵且经常出错的应用程序代码。
(2)由于保证一致性而导致的可用性降低是微不足道的,并且对于许多部署而言几乎不可察觉。
(3)CAP定理从根本上说是不对称的。CP系统可以保证一致性;AP系统却不一定保证100%可用性(没有系统可以保证100%的可用性)。

因此,只有CAP定理的一面为任何有用的保证打开了大门。

我认为,上述三点导致分布式事务数据库系统的惊人复兴 - 其中许多已在过去几年中已商业化 - 选择CP而不是AP。对于AP系统及其相关的NoSQL实现,仍然有一席之地。但对于大多数开发人员来说,建立在CP系统之上是一个更安全的选择。

但是,当我说CP系统是更安全的赌注时,我打算参考实际上保证一致性的一些CP系统,不幸的是,与他们声称的正好相反,这些现代NewSQL系统中有太多不能保证一致性。一旦这种保证被删除,就会出现个别案件的错误、复杂性和成本回报。

Spanner是问题的根源

我在之前的帖子中已经讨论过,有很多方法可以保证分布式系统的一致性。最流行的机制是以最低的可用性成本保证一致性,就是使用Paxos或Raft共识协议来强制跨数据的多个副本实现一致性。

在简化的层面上,这些协议通过多数表决机制起作用。对数据的任何更改都需要大多数副本同意更改。这允许少数复制品停机或不可用,并且系统仍然可以继续读取或写入数据。

大多数NewSQL系统使用这种共识协议来实现强一致性。但是,它们在如何使用这些协议方面存在显着差异。我将NewSQL系统分为两个类别:
第一类,如Calvin(来自我的研究小组)和FaunaDB等系统所体现的,每个数据库使用一个单一的全局共识协议。每个事务都参与相同的全局协议。

第二类,如Spanner,CockroachDB和YugaByte等系统所体现,将数据划分为“分片”,并为每个分片应用单独的共识协议。

第一类的主要缺点是可扩展性。服务器可以每秒处理固定数量的消息。如果系统中的每个事务都参与相同的共识协议,则同一组服务器对每个事务进行投票。由于投票需要通信,因此每秒的投票数受每个服务器可以处理的消息数量的限制。这限制了系统可以处理的每秒事务总量。

Calvin和FaunaDB通过批处理来绕过这个缺点。他们不是单独对每笔交易进行投票,而是对批量交易进行投票。每个服务器在固定时间段(例如,10毫秒)内批处理它接收到的所有事务,然后立即启动对整个批处理的投票。

凭借每10毫秒的一个批次处理,Calvin能够实现每秒超过500,000次交易的吞吐量。相比之下,即使在峰值工作负载期间,Amazon.com和NASDAQ也可能每秒处理不超过10,000个订单/交易(事务)。

第二类的主要缺点是,通过在每个分片的基础上本地化共识,在触及多个分片中的数据的事务时保证一致性就变得非常重要。

典型的例子是有人在一个照片共享app上执行两个动作的序列(1)删除她的父母,让他们无权查看她的照片(2)发布她的照片。即使从用户的有利位置有明确的这些操作序列,如果权限数据和照片数据这两种位于单独的分片中,并且分片单独执行共识,则父母仍然可能有能力查看用户最近上传的照片。

Spanner使用他们的TrueTime API解决了这个问题。所有事务都会收到一个基于实际(挂钟)当前时间的时间戳。这使得对于两个不同的事务,即使是由完全不相交的服务器集处理的事务,存在“之前”和“之后”的概念。具有较低时间戳的事务是具有较高时间戳的事务“之前”。显然,不同服务器的时钟可能存在少量偏差。因此,Spanner利用“不确定性”窗口的概念,该窗口基于系统中服务器上时钟的最大可能时间偏差。在完成写入之后,事务会等到这个不确定性窗口过去之后才允许任何客户端查看他们写入的数据。

Spanner因此面临着可能不舒服的权衡。期望不确定性窗口应该尽可能小,因为随着它变大,事务的等待时间增加,并且系统的整体并发性降低。

另一方面,它需要100%确定时钟偏差永远不会大于不确定性窗口(因为否则将不再保证一致性),因此较大的窗口比较小的窗口更安全。

Spanner通过使用GPS和原子钟的专用硬件解决方案来处理这种权衡,以确保服务器上的时钟偏差最小。该解决方案允许系统保持不确定性窗口相对窄,同时保持不正确的不确定性窗口估计(和相应的一致性违反)的概率非常小。事实上,概率是如此之小,以至于Spanner的架构师感到很自在,声称Spanner“保证”一致性。

[值得注意的是,使用全球共识的系统完全避免了这个问题。如果每个事务都通过相同的协议,那么所有事务的自然顺序就会出现 - 顺序只是协议期间事务的投票顺序。当使用批次而不是事务时,它是在协议期间顺序的批次,并且通过将批次标识符与批次中的序列号组合来全局排序事务。不需要使用时钟时间来创建之前或之后的概念。相反,共识协议本身可用于优雅地创建全局顺序。]

(banq注:如何保证顺序是设计分布式事务的关键之关键!Spanner败在顺序设计有漏洞上,区块链为了保证顺序,直接一个块链到上一个块,这种极端线性设计确实保证了顺序,如同幼儿园小朋友手牵手,顺序不会乱,人也不会丢,但是走起路就不方便,如同过独木桥,吞吐量不够,效率低!)

Spanner衍生品

Spanner是一个美观和创新的系统。它也是由谷歌发明并在那里广泛使用的。无论是前者还是后者(或两者),它都具有极大的影响力,许多系统(例如,CockroachDB和YugaByte)都受到了Spanner的架构决策的启发。不幸的是,这些衍生系统只是纯软件,这意味着他们只继承了软件创新,而没有Spanner那样依赖谷歌的硬件和基础设施。

鉴于Spanner决定每个分片具有单独的共识协议,那些软件衍生产品就会极其危险。与Spanner一样,这些系统依靠现实世界的时间来实现一致性--CockroachDB的HLC(混合逻辑时钟)和 YugaByte混合时间。与Spanner一样,这些系统依赖于了解服务器之间的最大时钟偏差,以避免一致性违规。但与Spanner不同,这些系统缺乏硬件和基础设施支持,以最小化和测量时钟偏差不确定性。

值得称道的是,CockroachDB已经承认,通过仅结合Spanner的软件创新,系统无法保证CAP的一致性(如上所述,它是线性化的)。

然而,YugaByte继续声称自己是保证一致性的。我建议人们不要相信这种说法。YugaByte凭借其Spanner根源,当服务器上的本地时钟突然超出偏斜不确定性窗口时,会遇到一致性违规。

这可能发生在各种情况下,例如运行YugaByte的VM冻结或迁移到其他计算机时。即使没有突然跳跃,YugaByte的免费版依赖于用户设置有关最大时钟偏差的假设。代表用户的任何错误假设都可能导致一致性违规。

与CockroachDB和YugaByte相比,FaunaDB的灵感来自Calvin而不是Spanner。[历史记录:Calvin和Spanner的论文均于2012年出版]。

因此,FaunaDB具有单一、优雅的全球共识协议,并且在时钟偏差假设方面不需要小的假设修补。因此,FaunaDB能够保证修改数据库中任何数据的事务的一致性,而不用担心可能困扰Spanner风格系统的纯软件衍生品的个别案例违规。

我在过去谈到的 Calvin风格的系统和Spanner风格的系统之间还存在其他差异。在这篇文章中,我们关注的可能是最重要的差异:全局共识与分区共识,与任何架构决策一样,需要在这两个选项之间进行权衡,对于绝大多数应用程序来说,每秒超过500,000次交易超出了他们最大的梦想,如果是这样,全球共识可能是更好的选择。

DBMS Musings: NewSQL database systems are failing