该文比较了Spanner与Calvin这两种分布式事务数据库的区别与特点。
该文作者丹尼尔·阿巴迪是耶鲁大学的副教授。他主要研究数据库系统架构和实现。他获得麻省理工学院和剑桥大学医学博士。
在2012年有两篇研究论文认为可以在不放弃一致性和事务支持的情况下构建极其可扩展的,地理位置复制的数据库系统。
第一篇是发表在SIGMOD 2012上的Calvin论文,几个月后,Google在OSDI 2012上发表了他们的Spanner论文。这两篇文章都被引用了数百次,并且影响了几个现代“NewSQL”的设计“系统,包括FaunaDB。
最近,谷歌发布了其Spanner的实施测试版,可供Google Cloud Platform的客户使用。google云用户有了可靠的、可扩展性和一致性的事务数据库系统。
CAP定理
Spanner和Calvin都可以跨地区复制数据,实现高可用性。而且Spanner和Calvin都是技术上来自CAP的CP系统:它们保证了整个系统的100%一致性(可串行化,线性化等)。
当存在网络分区时,这两个系统在可用性方面做了一些小小的妥协。
Spanner和Calvin都是技术上来自CAP的CP系统,但在实践中,这不意味着牺牲了可用性。
事务次序性
Calvin和Spanner最明显的区别:Spanner使用“TrueTime”,而Calvin使用“预处理”(或原始论文的“排序”)进行事务排序。事实上,Spanner和Calvin之间的大部分其他差异来自于这个根本不同。
可串行化系统提供事务性排序的概念。尽管许多事务可能在许多CPU和大型分布式系统中的许多服务器上并行执行,但最终状态(以及所有可观察的中间状态)必须像每个事务一个接一个地处理一样。如果这些事务没有触及相同的数据,并行处理它们是很简单的。
但是,如果事务读取对方写入的数据,那么它们必须相互排序,保证一个比另一个更早。
锁和日志
Spanner使用TrueTime实现事务顺序。Google在其所有地区使用GPS和原子钟的组合,以便将时间同步到已知的不确定性边界内。如果两个事务在没有重叠的时间段内实现处理,Spanner可以确定稍后的事务将会看到早期事务的所有写入。
在执行任何写入之前,在向数据副本中写入的所有数据之前Spanner将获取所有被复制服务器上的写入锁。如果它获得所需的所有锁,它将继续进行所有的写操作,然后在该事务的协调器服务器的末尾为事务分配一个时间戳。之后它一直等待,直到时间戳已经传递到系统中的所有服务器之后,才会释放锁并提交事务。后来的交易将获得较晚的时间戳,并查看此早期事务的所有写入。
因此,在Spanner中,每个事务都会根据其提交的实际时间收到时间戳,并使用此时间戳来排序事务。具有较晚时间戳的事务查看所有具有较早时间戳的事务的写入,锁是用于强制执行此保证的。
相比之下,Calvin使用预处理来排序事务。在处理之前,所有事务都将插入到分布式的复制日志中。客户端将事务提交到其本地区域的预处理层,然后通过跨区域共识复制过程(如Paxos)将这些事务提交到全局日志。这与传统的非分布式数据库(Oracle或MySQL)中的预写日志非常类似。日志中显示的顺序是事务的顺序。每个副本从这个复制日志的本地副本中读取,并以一种保证其最终状态等同于日志中的每个事务逐个执行的方式来处理事务。
Calvin使用预处理来排序事务,在处理之前,所有事务都将插入到分布式的复制日志中。
复制开销
TrueTime与预处理之间的设计差异直接导致系统执行复制的差异。在Calvin中预处理期间事务的复制是唯一需要的复制。Calvin使用确定性执行框架以避免在正常(非恢复模式)执行期间的所有除了预处理外的其他交叉复制通信。每个副本都看到相同的事务日志,不仅保证了一个等同于在该日志中逐个执行事务的最终状态,而且还保证了与每个其他副本相同的最终状态。
一旦事务中的所有代码都是确定性的,则副本可以安全地集中在正确的顺序处理日志中的事务,而不用担心与其他副本存在分歧。
相比之下,由于Spanner不进行任何事务预处理,因此只能在事务执行后执行复制。Spanner通过跨区域Paxos进程执行此复制。
两段成本
Spanner和Calvin之间的另一个主要区别是他们如何提交多分区事务。Calvin和Spanner都将数据分成分离的碎片,这些碎片可能存储在彼此独立计算机上。为了保证事务的原子性和持久性,访问多个分区中的数据的任何事务必须经过一个提交过程,确保每个分区成功地处理访问该分区中的数据的部分事务。
由于机器可能随时出现故障,包括在提交过程中,此过程通常在事务涉及的分区之间进行两轮通信。这种两轮提交协议称为“两阶段提交2PC”,这几乎适用于所有符合ACID标准的分布式数据库系统,包括Spanner。事务的实际处理时间远远小于通过网络发送和接收两轮消息所涉及的延迟。
在Spanner中,两阶段提交的成本特别高,因为协议涉及三个强制写入到彼此不能重叠的日志。在Spanner中,对日志的每次写入都涉及跨区域Paxos协议,因此Spanner中两阶段提交的延迟至少等于跨区域Paxos的延迟的三倍。
耐久性
与Spanner相反,Calvin利用确定性执行来避免两阶段提交。机器故障不会导致Calvin的事务中止。
Calvin会平衡确定性执行来避免两阶段提交。
如果失败后,Calvin中失败的计算机会从检查点重新读取输入的事务日志,并确定性地重播它以在失败时恢复其状态。那么可以继续从失败地方继续,就好像没有发生什么。因此,提交协议不需要担心协议期间发生机器故障,并且可以在单轮通信中执行。
性能影响
事务写入延迟方面:
Spanner和Calvin对于单分区事务具有大致相等的延迟,但是由于事务提交协议中的额外阶段,Spanner对于多分区事务的延迟比Calvin的延迟更差。
快照读取延迟方面:
Calvin和Spanner都可以保留旧版本的数据,并从本地副本的请求的较早时间戳读取数据,而不与其他副本进行任何Paxos通信。
因此,Calvin和Spanner都可以实现非常低的快照读取延迟。
事务读取延迟方面:
对于客户端物理上靠近leader服务器位置情况下的提交的只读事务,Spanner具有比Calvin更好的延迟。
可扩展性
Spanner和Calvin两者(理论上)对于事务工作负载都是大致线性可扩展的,并发事务难以访问相同的数据。然而,主要差异开始表现为并发事务之间的冲突率开始增加。
如本文所述,Spanner和Calvin都使用锁来防止并发事务相互干扰。然而,对于相同的事务来说,它们持有锁的时间量是显着不同的。两个系统都需要在提交协议期间保持锁定。然而,由于Calvin的提交协议比Spanner的提交协议要短,所以Calvin会在事务结束时减少锁定保持时间。另一方面,Calvin在事务开始时获取所需的所有锁,而Spanner在获取写入锁之前执行事务的所有读取。因此,Spanner可以减少事务开始时的锁定时间。
然而,Spanner的后一个优点通常被前一个缺点所覆盖,因为如上所述,Spanner中两阶段提交的延迟涉及跨区域Paxos的至少三次迭代。此外,在锁定时间内,Spanner还具有相对于Calvin的另一个主要缺点:Spanner还必须在复制期间保持锁定(如上所述,它也是跨区域Paxos进程)。区域越远,复制的延迟越大,因此,Spanner必须保持更长锁定。
Spanner在复制期间必须持有锁。区域越远,复制的延迟越大,因此,Spanner必须持有更长的锁。
相比之下,Calvin在预处理期间执行复制,因此在复制期间不需要持有锁。这导致Calvin持有比Spanner短得多的锁定时间,允许它并行处理更多冲突的并发事务
可能影响可伸缩性的第二个区别如下:Calvin仅需要一个Paxos组来复制输入日志。相比之下,Spanner需要每个分片一个独立的Paxos组,按比例有更高的开销。
对于并发事务访问相同数据的事务工作负载,Calvin具有比Spanner更高的吞吐量可扩展性。这种优势随着数据中心之间的距离而增加。
事务类型的限制
为了实现确定性事务处理,Calvin要求预处理器分析事务并潜在地“预执行”任何非确定性代码,以确保副本不分歧。这意味着预处理器需要一次提交整个事务。这突出了Calvin和Spanner之间的另一个区别 - 而Spanner理论上允许任意的客户端交互式交易(可能包括外部通信),Calvin支持更有限的事务模式。
特别地,SQL的客户端交互式事务模型(也称为会话事务)对于Calvin来说是不合适的。Spanner的公开版本支持客户端交互式事务,但必须明确引用每行的主键。
SQL的客户端交互式事务模型对于Calvin来说是不合适的。
在少数情况下,Calvin和Spanner之间存在一些微妙但有趣的区别,即每个分片的每个副本都不可用,或者除了一个不可用之外,还有这些差异超出了这个职位的范围。
结论
我偏偏于加尔文,但是经过这个比较,我发现很难找到理论上理想实现“Spanner”理想地胜过Calvin理想实现的案例。唯一可以发现的是,Spanner与Calvin相比具有明显的性能优势的地方是客户端提交的只读事务延迟比较低,这是因为客户端在物理上更靠近leader服务器的位置。但是由于任何复杂的事务都可能会触及多个分区,所以在现实环境中这种位置保证几乎不可能。
许多现实世界的工作处理不需要客户端交互式事务,只需要对写入的事务支持,并且满足对快照执行读取(毕竟这是许多SQL系统的默认隔离模型)。因此,在我看来,Calvin更适合现代应用。
FaunaDB是由Calvin启发的全局复制的、强一致性的数据库。