Files
MIT6.824/lecture-12-distributed-transaction/12.5-zong-jie.md
2022-01-25 02:41:31 +00:00

6.6 KiB
Raw Blame History

12.5 总结

这就是两阶段提交,它实现了原子提交。两阶段提交在大量的将数据分割在多个服务器上的分片数据库或者存储系统中都有使用。两阶段提交可以支持读写多条记录,一些更特殊的存储系统不允许你在多条记录上支持事务。对于这些不支持事务中包含多条数据的系统,你就不需要两阶段提交。但是如果你需要在事务中支持多条数据,并且你将数据分片在多台服务器之上,那么你必须支持两阶段提交。

然而两阶段提交有着极差的名声。其中一个原因是因为有多轮消息的存在它非常的慢。在上面的图中各个组成部分之间着大量的交互。另一个原因是这里有大量的写磁盘操作比如说B在回复Yes给Prepare消息之后不仅要向磁盘写入数据还需要等待磁盘写入结束如果你使用一个机械硬盘这会花费10毫秒来完成Log数据的写入这决定了事务的参与者能够以多快的速度处理事务。10毫秒完成Log写磁盘那么最快就是每秒处理100个事务这是一个非常慢的结果。同时事务协调者也需要写磁盘在收到所有Prepare消息的Yes回复之后它也需要将Log写入磁盘并等待磁盘写入结束。之后它才能发送Commit消息这里又有了10毫秒。在这两个10毫秒内锁都被参与者持有者其他使用相关数据的事务都会被阻塞。

这里我持续的在介绍性能,但是它的确非常重要,因为在一个繁忙的事务处理系统中,存在大量的事务,许多事务都会等待相同的数据,我们希望不要在一个长时间内持有锁。但是两阶段提交迫使我们在各个阶段都做等待。

进一步的问题是如果任何地方出错了消息丢了某台机器崩溃了如果你不够幸运进入到Block区间参与者需要在持有锁的状态下等待一段长时间。

因此你只会在一个小的环境中看到两阶段提交比如说在一个组织的一个机房里面。你不会在不同的银行之间转账看到它你或许可以在银行内部的系统中看见两阶段提交但是你永远也不会在物理分隔的不同组织之间看见两阶段提交因为它可能会陷入到Block区间中。你不会想将你的数据库的命运寄托在其他的数据库不在错误的时间崩溃从而使得你的数据库被迫在很长一段时间持有锁。

因为两阶段提交很慢,有很多很多的研究都是关于如何让它变得更快,比如以各种方式放松这里的规则进而使得它变得更快,又比如对于一些特定的场景做一些定制化从而避免一些消息,我们在这门课中会看到很多这种定制。

两阶段提交的架构中本质上是有一个Leader事务协调者将消息发送给Follower事务参与者Leader只能在收到了足够多Follower的回复之后才能继续执行。这与Raft非常像但是这里协议的属性与Raft又非常的不一样。这两个协议解决的是完全不同的问题。

使用Raft可以通过将数据复制到多个参与者得到高可用。Raft的意义在于即使部分参与的服务器故障了或者不可达系统仍然能工作。Raft能做到这一点是因为所有的服务器都在做相同的事情所以我们不需要所有的服务器都参与我们只需要过半服务器参与。然而两阶段提交参与者完全没有在做相同的事情每个参与者都在做事务中的不同部分比如A可能在对X加1B可能在对Y减1。所以在两阶段提交中所有的参与者都在做不同的事情。所有的参与者都必须完成自己那部分工作这样事务才能结束所以这里需要等待所有的参与者。

所以Raft通过复制可以不用每一个参与者都在线而两阶段提交每个参与者都做了不同的工作并且每个参与者的工作都必须完成所以两阶段提交对于可用性没有任何帮助。Raft完全就是可用性而两阶段提交完全不是高可用的系统中的任何一个部分出错了系统都有可能等待直到这个部分修复。比如事务协调者在错误的时间崩溃了我们需要等待它上线并读取它的Log再重发Commit消息。如果一个参与者在错误的时间崩溃了如果我们足够幸运我们只需要Abort事务。所以实际上两阶段提交的可用性非常低因为任何一个部分崩溃都有可能阻止整个系统的运行。Raft并不需要确保所有的参与者执行操作它只需要过半服务器执行操作或许少数的服务器完全没有执行操作也没关系。这里的原因是Raft系统中所有的参与者都在做相同的事情我们不必等待所有的参与者。这就是为什么Raft有更高的可用性。所以这是两个完全不同的协议。

然而是有可能结合这两种协议的。两阶段提交对于故障来说是非常脆弱的在故障时它可以有正确的结果但是不具备可用性。所以这里的问题是是否可以构建一个合并的系统同时具备Raft的高可用性但同时又有两阶段提交的能力将事务分包给不同的参与者。这里的结构实际上是通过Raft或者Paxos或者其他协议来复制两阶段提交协议里的每一个组成部分。

所以在前面的例子中我们会有三个不同的集群事务协调器会是一个复制的服务包含了三个服务器我们在这3个服务器上运行Raft

其中一个服务器会被选为Leader它们会有复制的状态它们有Log来帮助它们复制我们只需要等待过半服务器响应就可以执行事务协调器的指令。事务协调器还是会执行两阶段提交里面的各个步骤并将这些步骤记录在自己的Raft集群的Log中。

每个事务参与者也同样是一个Raft集群。

最终,消息会在这些集群之间传递。

不得不承认这里很复杂但是它展示了你可以结合两种思想来同时获得高可用和原子提交。在Lab4我们会构建一个类似的系统实际上就是个分片的数据库每个分片以这种形式进行复制同时还有一个配置管理器来允许将分片的数据从一个Raft集群移到另一个Raft集群。除此之外我们还会读一篇论文叫做Spanner它描述了Google使用的一种数据库Spanner也使用了这里的结构来实现事务写。