本文共 4450 字,大约阅读时间需要 14 分钟。
随着为分布式世界重建遗留RDBMS (ACID事务、关系、约束、规范化建模)的原语,数据库世界正在经历复兴。
这些新的系统从技术上来说是CP而不是AP,但是有很高的容错性。
这些系统大多数依赖于Google的Spanner或Percolator协议,使用两阶段锁,需要物理的同步时钟来保证一致性。
一些系统采用基于Calvin协议的预提交方法,比如FaunaDB,不需要特别的硬件就能提供更强的保障。
在这两种情况下,系统开发人员不再需要放弃事务来获得现实世界中的可伸缩性和可用性。
数据库复兴运动起源于NoSQL,大约是因为2008年新的一大波互联网公司(Twitter、Facebook、Google、Amazon)的技术需求远超传统的RDBMS能力。另外,像Oracle这样的RDBMS供应商的价格体系不能够支持这些新兴公司的较小单位经济效益,尤其是广告支持的公司。在这种情况下要怎么做呢?
一开始,公司会使用遗留数据库作为存储引擎的自定义、分布式数据分片服务,尤其是MySQL和InnoDB。像Memcache和之后的Redis这样的分布式缓存无处不在。然而,这些系统不是自运维的,它们仍然需要DBA的干预才能从失败中恢复。它们不是自动分片的,也不是自修复的。
当时,我在Twitter管理软件基础设施团队,对我们而言,完全有可能构建一个可以透明地大规模和灵活交付的系统,因此,连同业内的其他人,我们开始将其作为开源项目着手来做,一开始主要是寄希望于Apache Cassandra、Hadoop和其他存储平台。当你的业务规模变大的时候,任何会阻碍它前进的东西都需要放弃。
这些数据库系统的商业供应商号称他们的系统不能提供某些RDBMS传统功能,因为没人需要这些功能,但这显然是错误的。现在每个企业都需要扩大规模,然而我们为了使用NoSQL放弃了什么呢?我们能把放弃的东西拿回来吗?
CAP理论可以有效地概括为:当网络分片时会发生什么?系统会保证一致性(正确性)而暂时停一下,还是为了保证可用性,而尽最大努力保证它的运行?其实在实践中,一致性和可用性的选择是模棱两可的,真实世界的情况更加微妙。
第一代NoSQL系统是最终一致的,在分区事件发生之后,它们会在可变但有限的一段时间之内协调冲突,对数据进行概率投票。在现实世界中,很少有这么复杂的最终一致性系统,相反,它们使用基于本地系统时间的简单“最后一次写入获胜”机制。即使它再智能,对搭建应用程序也没有实质上的帮助。它需要系统设计人员将整个主机的冲突解决功能引入到应用程序层。
明显的交易失败模式如下所示:
Bob在ATM存了200美元。
数据库分片接受Bob的“最终一致”存款。他的账户余额200美金更新需要排队等待。
Bob又通过移动应用程序存了100美元。这次交易更新使用了另一个分片,并复制给其他的集群,但没有到原来的分片。
网络恢复,复制发生。
由于第二个交易发生的时间比较晚,所以Bob的余额根据第3步的分片被更新为100美元。
Bob现在损失了200美元现金。最终一致性能保证可用性。在这种情况下,我们存钱的数目多少并不能保证正确。
为了解决这个问题,人们想出了各种方案,特别是CRDT(conflict-free replicated data types),它能帮助有效地关闭所有的中间状态,保证最终值正确重建。在这种情况下,Bob的交易会被记录为借方和贷方,而不只是最终余额。最终余额的获得可以在数据库或应用程序层中完成。一种例子是区块链,它会记录集群上所有节点每时每刻的交易。
然而,在实际操作中,这不能解决所有的问题,因为数据库不是封闭的系统。任何数据读写的重点在于协调外部影响。如果Bob从ATM取出现金,即使之后CRDT显示当时他的余额不足,但是钱也被拿走了。
这并不是一个完全的理论问题。恶意使用ATM的漏洞是一种欺诈行为。因此,数据库需要“外部一致性”,就是俗称的ACID属性。
遗留数据库(通常是集中的RDBMS)通过非分布式来解决这个问题。其实说到底,就是一个有单个磁盘的单机,并且出于实际目的,该磁盘基于一个互斥锁进行更新,事实就是这么回事。对状态的更新是序列化的,这代表它们是根据单一、确定的顺序发生的,并且是外部一致的,对应着实际发生顺序。任何规模化都是出于单机的垂直扩展。
该模型很方便实现,但不能满足很多关键需求,特别是和可用性相关的问题。
因此我们迎来了主从复制系统,主节点可以被DBA手动替换为从节点,该从节点异步地复制了主节点的状态。这虽然不是很好,但是不像最终一致性分布式系统,不一致的范围是已知的:恰恰是最后一次事务复制到从节点,而主节点发生了外部可见的故障,这之间就出现了不一致情况。
这是互联网分片系统真实运行的情况:
对于Twitter和Facebook来说这勉强可行,但并不可取。比如说,如果在手机上得到的消息通知在网站上不能读,这是很奇怪的,但这并不是灾难性的。需要事务处理的产品特性(例如,用户名分配)在遗留的RDBMS中没有那么大的伸缩性。
但是随着产品变得越来越复杂,缺乏外部一致性的劣势也变得更加严重。
是Google对于这两个问题的解决方法。最初它只在Google内部的基础设施上使用,现在作为托管产品在Google Cloud上可以获得。
它完成了两件事:
这个方法在某种程度上很有效果。它保证了可串行化,能根据实时顺序对每个单独的分片进行更新。但它不能保证外部一致性,也不能保证分片之间的实时协调。为了解决这个问题,Spanner需要做这件事:
现在事务协调器就能放心地说:“分片们,我在T时间做了一个事务,如果你没有看到在我们共享时间误差范围内的其他任何更新,那就是不冲突的”。这引发了每次最终一致读写间的小延迟,因为每个分片都需要等待时钟模糊窗口。
除去延迟的影响,这对Google来说很可行。它们有资源来构建并管理自定义的原子时钟硬件和有限延迟的网络。然而,有许多新的数据库系统在没有原子钟的情况下实现类似的协议,但总是要付出代价的。
基于NTP时钟同步的数据库有更长的模糊窗口,通常需要几百毫秒。实践中,这些系统完全放弃等待,转而保证没有外部一致性的单记录可串行化。这可能会导致类似的交叉线模列和双借方效果。
它们还不提供快速地外部可串行化读取,通常从最后一个已知的Paxos leader这里读取。这可能违背了可串行化,因为leader可能不知道它已经被废弃,于是在选举窗口(通常是几秒钟)愉快地提供了已经废弃的值。要预防这个窗口的发生需要暂停。
最后,时钟是否会碰巧不同步,这也是Google花费心思需要预防的,因为在云中任何与时钟无关的事件,比如VM停顿,都会造成这个情况的发生,甚至写端的可串行化保证都会丢失。
另一类数据库查询单个物理时钟(Google Percolator的中称其为”clock oracle”)拥有类似共享时钟的往返互联网延迟的模糊窗口,更糟糕的是,还会遇到明显的单点故障。
这个模型类似于多处理器RDBMS,它也用了单个物理时钟,因为它是单机,但是系统总线被网络所取代。在实践中,这些系统放弃了多区域扩展,受限于单个数据中心。
很明显没有物理时钟同步,分布式外部一致性是不可能的,还是有其他方法?
如果你可以创造给所有的事务服务的逻辑clock oracle,不依赖于任何单个物理机器,还可以广泛分布,那怎么样?这就是完成的工作。Calvin是耶鲁大学实验室开发的。
John Calvin是法国新教改革者,他相信每个人都有最终的宿命,无论是上天堂还是下地狱,都是上帝在创造世界之前决定的。
这也是Calvin的机制。事务的顺序是预处理决定的,节点的子集作为分区的Oracle,给一系列流水线批处理中传入的事务分配外部一致的顺序。之后,批处理用10毫秒的时间提交到分布式提前写日志,可以用Paxos或Raft实现。
这种预处理有很多好处:
然而,它肯定会做出一些让步:
进行了一系列性能改进,实现了这个模型,是个关系型NoSQL系统,而不是NewSQL系统,但是没有什么特别阻碍FaunaDB最终实现SQL接口的东西。
尽管没有CP系统可以在完全任意分区事件中保持活动性,但是在实践中,我们能发现Spanner和FaunaDB云系统和AP系统的可用性类似。超过99.9%的可用性故障可能都来自于实现的问题,也可能来自于硬件和网络的故障,CP系统的一致性模型可以让它们比AP系统更容易地在故障下验证。
比如说,五个数据中心的FaunaDB集群可以承受丢失两个完整的数据中心,而不影响活动性。同时,类似FaunaDB的CP系统通常可以在较低的一致性级别下维持读的可用性(比如快照隔离的情况),甚至在分区事件的时候也能实现,这和AP系统一直提供的一致性级别相类似,甚至更好。
理论上来说,将可用性从99.999%提升到99.9999%是不值得的(一年几分钟的差别),特别是在暂停期间接受写的代价是永久的数据不一致。
分布式事务是计算机科学领域最难解决的问题之一。我们很幸运地生活在这些系统现成的世界里。任何的有界一致性都比最终一致性要好,甚至抛开其他遗留NoSQL的问题,比如持久性,以及遗留的RDBMS问题,比如运维开销等等。
然而,除了Calvin之外,其他产品并不能实现没有物理时钟的分布式外部一致性。FaunaDB是唯一实现了类似Calvin事务协议的产品。我推荐你注意你的数据库系统所提供的一致性保障,并尝试一下使用FaunaDB。
本文作者
Evan Weaver是Twitter的前任基础设施总监,他的团队负责扩展Twitter的后端。他拥有计算机科学硕士学位,之前在CNET和SAP工作过。他是FaunaDB的联合创始人。
查看英文原文:
转载地址:http://muvix.baihongyu.com/