关于2PC、3PC和Percolator的一些对比和理解
Contents
目录
结论
2PC、3PC和Percolator都是一种实现分布式事务(原子性)的协议。2PC是最基本的协议,3PC和Percolator都是基于2PC进行优化。
本文旨在对比这三种方法,分析他们的区别和优缺点,而不讲述他们的基本原理。适合对他们有基本理解的人阅读。
先说结论,后面是分析:
2PC对协调者有依赖性,2PC的协调者必须保存状态,记录各个参与者的信息,宕机后只能恢复,而不能直接更换。它是有状态的。2PC的第二阶段完全依赖于协调者。
3PC可以缓解2pc过度依赖于协调者的问题。但3PC并没有完全解决,而且性能还更差。
可以通过raft这种一致性协议,来保证2pc协调者的高可用,避免单点问题。这样当部分2PC协调者宕机后,通过raft的leader选举选出新的leader来成为这个事务新的协调者。
percolator解决了对协调者的依赖,并且有很大的性能提升。
percolator的特点在于,因为参与者不依赖于协调者,因此他的协调者可以是无状态的(例如TiDB Server)。协调者宕机之后只要有一个新的协调者就OK。它不依赖于特定的协调者,不需要等待旧的协调者重启。因此不需要raft这种协议来保持协调者的多副本。
所以percolator和2PC相比,除了有性能差距,还有对协调者依赖的差距。
性能提升在于primary key commit成功就可以返回,其它进行异步提交。而2PC必须等全部参与者都commit。受短板效应的影响,受限于每次最慢的那个参与者的完成时间。
percolator把2PC对协调者依赖的部分下沉到了参与者(TiKV,primary key),因此它的协调者变成了无状态的。
对比表格
需要从下面几个维度去对比:
协议 | 对协调者的依赖性 | 协调者状态 | 性能 | 可用性 |
---|---|---|---|---|
Percolator | 不依赖 | 无状态 | 高性能 | 参与者依赖raft(或其它高可用机制) |
2PC | 完全依赖 | 有状态 | 中性能 | 协调者和参与者依赖raft |
3PC | 部分依赖 | 有状态 | 低新能 | 协调者和参与者依赖raft |
2PC和Percolator对比
percolator将第二阶段改成了primary和secondary两种,secondary keys可以异步提交,primary的状态作为整个过程的状态,而primary可以是原子的。
因为在进行prewrite之后,整个事务就可以执行了,至于是否执行成功,percolator将这个状态保存到primary key上,它成功了(原子的),那么其他的secondary key也可以看作是成功了,就可以直接向客户端返回成功。即使后面有secondary key失败了,也可以重试。
传统的2PC要等所有的key在第二阶段都返回成功才能向客户端,因为它没有primary,所以无法保存状态,这导致当第二阶段协调者crash之后,事务就会失败。而percolator等primary key成功了或失败了就可以马上返回,其它keys异步提交,加快了速度。
percolator用primary key巧妙地优化了这个问题。
从下面2PC和3PC的对比可以看到,2PC高度依赖于协调者。
而percolator的第二阶段不再依赖于协调者。因为第二阶段的primary key的提交或回滚是原子性的,标志着第二阶段是否成功。如果primary key commit了然后协调者宕机了,那么其它keys lock住了,在其它事务执行的时候检测到这点,然后去检查lock对应的primary key,就可以rollback或者commit。
2PC和3PC对比
这个讲的不错:http://dengchengchao.com/?p=1425 他讲的通知聚会的例子很好。
2PC的问题
如果参与者在2PC
期间发生失败:
- 第一阶段宕机:协调者会发现其中某个参与者超时,则第二阶段会发起回退请求。
- 第二阶段宕机:协调者会一直重试直到此参与者返回成功。
如果协调者2PC
期间宕机:
不管哪个阶段宕机,由于协调者每次在发起请求前会保存状态,因此他可以在恢复后发起询问。
2PC
的优点就是简单,但是缺点也很明显:
参与者高度依赖协调者,因此当协调者发生故障的时候,参与者只能一直阻塞直到协调者恢复,因此故障风险大,并且会影响系统的效率。
3PC
3PC
相对于2PC
而言增加了一个缓冲阶段(canCommit),这个阶段不会修改数据,这个缓冲阶段主要就是为了解决2PC
对协调者强依赖的问题(变成了更弱的依赖,但依然是依赖,没有从根本上解决问题)。
相对于2PC
而言,3PC
增加了一个缓冲阶段:CanCommit
,通过缓冲阶段能够让所有参与者预先准备事务,一般来说,如果事务能够进行到第二阶段,则表示大部分情况下,事务应该都能正常运行。
可以看到,3PC
通过增加一个阶段,便减弱了参与者对协调者的依赖,比如在第一个阶段之后小A
没有收到X
的信息,那么小A
便可以不去,因为他并不知道后面的人情况,而在第二个阶段,相当于做了一个约定,当小A
收到此消息后,便默认第三阶段是commit
,如果此后X
宕机,那么小A
在等待超时后,除非收到第三阶段回退的消息,否则小A
依然会去参加同学聚会,因为一般在第一阶段答应过X
之后,其他参与者都会尽力的去提交此次事务。
虽然3PC
减弱了对协调者的依赖,但是带来的问题便是极端情况下数据可能不一致,因为3PC
第三阶段如果超时,其他参与者便会默认提交,那么如果在某些特殊情况下,使得X
发送的是abort
消息,那么就会出现数据不一致的问题。
我对2PC和3PC对于协调者依赖的理解
3PC最大的特点在于,第一阶段发生冲突之后不需要rollback。这样在第二阶段宕机的话,参与者可以在超时后继续正常执行事务,而不依赖于协调者了。
如果第一阶段没有发生冲突,就需要在第二阶段发送pre_commit指令,这时会修改数据。如果所有的参与者都收到了pre_commit,那么如果这时协调者宕机了,他们可以在超时后自动执行第三阶段。不依赖与协调者。
如果只有一部分收到了pre_commit,那么这时还需要等待协调者恢复之后才能知道自己是commit还是rollback。因此这种情况依赖于协调者。
综上所述,对于上面三种情况,3PC依赖协调者的情况只有:第一阶段无冲突,第二阶段pre_commit时协调者宕机。
而2PC依赖协调者的情况:整个第一阶段,第二阶段。属于完全依赖。
因此,3PC缓解了对协调者的依赖,但是没有完全解决。
而Percolator是不依赖协调者的。不管是哪个阶段都不依赖。
3PC缓解了对协调者的依赖,但是没有完全解决。
并且3PC的代价是流程更长,性能更差,因此会更容易发生事务冲突。
总结
我认为:
2PC对协调者有依赖性,2PC的协调者必须保存状态,记录各个参与者的信息,宕机后只能恢复,而不能直接更换。它是有状态的。2PC的第二阶段完全依赖于协调者。
3PC可以缓解2pc过度依赖于协调者的问题。但3PC并没有完全解决,而且性能还更差。
可以通过raft这种一致性协议,来保证2pc协调者的高可用,避免单点问题。这样当部分2PC协调者宕机后,通过raft的leader选举选出新的leader来成为这个事务新的协调者。
percolator解决了对协调者的依赖,并且有很大的性能提升。
percolator的特点在于,因为参与者不依赖于协调者,因此他的协调者可以是无状态的(例如TiDB Server)。协调者宕机之后只要有一个新的协调者就OK。它不依赖于特定的协调者,不需要等待旧的协调者重启。因此不需要raft这种协议来保持协调者的多副本。
所以percolator和2PC相比,除了有性能差距,还有对协调者依赖的差距。
性能提升在于primary key commit成功就可以返回,其它进行异步提交。而2PC必须等全部参与者都commit。受短板效应的影响,受限于每次最慢的那个参与者的完成时间。
percolator把2PC对协调者依赖的部分下沉到了参与者(TiKV,primary key),因此它的协调者变成了无状态的。
为什么 percolator 的事务两阶段提交不需要 Coordinator? - hellocode的回答 - 知乎 (PS. 问题是错的)https://www.zhihu.com/question/300050882/answer/518833781
可用性
我认为,Percolator的协调者是无状态的,因此协调者不需要raft维护多副本,可以直接更换一个新的。就像TiDB的TiDB Server一样。
2PC和3PC的协调者都是有状态的,因此需要多副本,如使用raft。如果没有多副本,只能等待它恢复。如果它恢复不了,可能就凉凉了。
而不管是Percolator还是2PC还是3PC,他们的参与者都需要有多副本机制如raft。否则一个参与者宕机了或者数据损坏了,那么系统就不可用了。因此需要raft来支持他们的高可用。
因此可以总结为:
Percolator只有参与者依赖raft(或其它高可用机制),而2PC和3PC的参与者都依赖raft。
Author 姬小野
LastMod 2022-11-07