分布式事务提交的一些想法

Posted NOSQL追随者

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了分布式事务提交的一些想法相关的知识,希望对你有一定的参考价值。

随着人们对数据存储的需求越来越多,对数据库而言,扩展性变成了很重要的因素,近几年来,很多数据库通过分区技术解决了扩展性的问题,比如mysql的Shard或者BigTable的Partition。分区技术带来了一个熟知的难题,就是跨分区事务特性的高可用和性能。今天读了Jim Gray和Leslie Lamport的<Consensus on Transaction Commit>的文章,加上之前也思考过这一块,还是总结一下,就是下文。因为并没有真正做过分布式事务,所以这些都是纸上谈兵,出错之处,欢迎交流。微信:teaworldvc20


经典的二阶段提交(2PC)如图1[1]而言,主要的角色包括事务协调器(Transaction Manager/Coordinator, TM)以及事务参与者(Resource Manager/ Participant),主要步骤图中已经写得很清楚了,这些步骤可以分为两个阶段:

1. 第一阶段Prepare:TM发消息给各个RM,是否可以提交,各个RM根据自己的状态来判断,以转账为例,比如余额够不够,账号是否被锁定等,将判断结果写入stable storage(写本地盘或者共享存储),然后发消息给TM

2. 第二阶段Commit:如果各个RM都认为可以提交,则TM写下Commit到stable storage,此时这个事务就算提交了;然后TM发消息给各个RM,而RM则Apply,并允许接下来的事务看到这些事务的修改结果


 

图1:基本的2PC流程[图片链接见下,侵删]


上面的2PC流程面临最大的挑战是可用性问题,比如

  1. TM挂:此时没有人知道该TM上活跃事务的状态,唯有等待其恢复才能继续,而该TM上的事务可能还在相关的RM上锁住了资源,这很容易导致整个系统没法运转,带来极大的运维代价,记得Percolator里面就提到了有时候需要人工处理这类问题,而Percolator[2]是用于处理网页的,对实时性要求并不高,如果是交易系统,基本不可接受

  2. RM挂:此时影响的是这个RM相关事务,典型情况下,某些用户不能操作了,只要分区做的合理,影响可控,当然,大家会希望即使RM挂系统也能照常运行


解决上面问题的思路主要有两个大方向:

  1. 思路一,使用共享存储存储状态+周期监测相关角色状态:共享存储的意义是可用性本来就很高,等于站在巨人的肩膀上,比如Google的BigTable已经做到了很好的容错(机器A挂了之后,A上服务的分区会被很快调度到其他机器上),那么我们将TM状态写入到BigTable中,这样任何时候我们都可以认为TM的状态是可用的。除此之外,还要让TM-1跟一个调度器进程保持心跳。当调度器发现TM-1一段时间没有心跳,就再启动一个TM-2,TM-2启动后首先拿到排它锁(防止网络分裂TM-1还在服务,使用类似Chubby[3]分布式锁服务),然后从BigTable里面加载TM-1的状态,这样整个系统就可以正常运转,如果做的好,这个不可用时间大概可以做到10s以内(每个TM的活跃事务要分配好,不要倾斜太严重)。RM的可用性可以如法炮制。可以看到,上述思路沿用了传统存储方案的共享存储思路,通过保障共享存储来提高可用性

  2. 思路二,使用一致性协议保证每个角色的高可用:一致性协议是Paxos或者其变种,抽象理解就是某个角色的多个参与者组成了Paxos group,只要这个group中的大多数参与者是可用的,则这个角色就是可用的。这种方案又细分两个思路:

    1. 只有TM使用一致性协议:这样就保证了TM的可用性,RM如果挂了,因为只影响了部分,等待期恢复即可,这也是Lamport文中提到的方法,流程见图2。但是在繁忙的数据库中有时候RM几秒不可用也是不能接受的

    2. 文中思路推广开,所有TM、RM均使用一致性协议:这样保证了所有角色的高可用,这个猜测是Google Spanner[4]里面使用的策略,因为文中提到过可以通过增加参与者提高读性能,说明很可能RM使用了一致性协议。


 
图2:文中描述的Paxos Commit流程


最开始提到了,分布式事务要求事务系统的高可用和性能,上面两种思路三种做法各自的优劣如何呢?个人看法如下:

  1. 可用性天花板:思路1使用共享存储保证高可用相对容易实现,因为分布式存储的基础设施已经比较完备,各种理论和开源工程也很多。但是该方案在可用性方面有天花板,心跳检测是一个依赖时间的机制(心跳监测是一个比较复杂的话题,而不少系统心跳做的都不合理,典型的就是心跳还在但是不能服务),无论如何做,最好也就是做到几秒。思路2使用一致性协议,在碰到参与者挂的时候平均几百ms就可以完成错误切换。还有一个问题是当碰到网络延时抖动的时候,思路1的方案只能等待,思路2就可以迅速排除抖动网络所在的参与者(当然一致性协议实现要做的好,要让新的参与者迅速提供决策)

  2. 性能:主要考虑三个方面,发消息的次数、写盘次数以及commit的总时延,对比见图3,前面都是论文中的,最后一列是我加的

  3. 工程难度:思路2的工程实现复杂度比思路1大,思路2的第二种方案如果想实现的高效会更难,不过,如果让我做,我会选择第二种方案,可用性最好,且如果读压力大,可以灵活调节。


图2:文中描述的Paxos Commit流程


[1]. http://databaser.net/moniwiki/pds/2_2dPhaseCommit/two_phase_commit.jpg

[2]. http://research.google.com/pubs/archive/36726.pdf

[3]. http://research.google.com/archive/spanner-osdi2012.pdf

[4]. http://research.google.com/archive/chubby-osdi06.pdf


以上是关于分布式事务提交的一些想法的主要内容,如果未能解决你的问题,请参考以下文章

分布式事务如何工作(例如 MSDTC)?

分布式事务 两阶段提交及JTA

分布式事务两阶段提交协议三阶提交协议

分布式事务——三阶段提交

MySQL binlog 组提交与 XA(分布式事务两阶段提交)

Mysql—Mysql日志的两阶段提交分布式事务以及多事务组提交