2016年4月13日星期三

2PC和3PC一点理解

为啥不是1PC?

协调者直接发提交命令给每个参与者岂不很好?只能成功不能失败,如果参与者挂了等恢复的时候重新找协调者拉一下状态就好了。1pc主要是可能会碰到某些参与者因为条件不满足必须回滚,没有选择的机会。

BEST EFFORT 1PC

主要特点有两点
1.会把事务或者关键事务推到最后,先把容易出错的事情都做完,到了最后关头统一提交事务,这样就不太容易失败。  
2.在某些点失败了是要不断重试来保证一致性的。比如下单和扣款,下单逻辑走完后调支付接口,支付完成之后会不断回调下单系统(此时支付已经不能回滚),只有回调成功之后才会停止回调然后下单这边根据支付的结果决定提交还是回滚。

这种玩法限制还是很多的,比如一致性问题就比较需要业务方来保证,比较复杂的场景就不一定能保证。

2PC的问题

1.参与者timeout,第一阶段发生协调者直接rollback事务,第二阶段发生就必须等参与者恢复重新从协调者那边拿到状态做相应的操作。
2.协调者挂掉,第一阶段挂掉,部分参与者prepared,部分参与者初始状态,备用协调者启动以后可以继续发送prepared或者rollback。第二阶段挂掉,备用协调者启动后发现部分参与者是prepared,部分参与者是committed,继续发送commit。
3.协调者发送第一个commit的时候和接收的参与者同时挂掉,剩下的参与者全部是prepared状态,备用协调者启动以后不知道挂掉的参与者是什么状态,如果发送rollback可能参与者已经commit,commit可能不可逆,如果发送commit可能参与者已经被rollback,事务保存的数据已经丢失。问题的关键在于此时备用协调者无法知道之前的协调者作的决策,所以整个事务就处于hang住的状态只能等挂掉的参与者恢复才能继续。

3PC的方案

1.3pc最关键要解决的就是协调者和参与者同时挂掉的问题,所以加入了一个precommit状态标识(便于理解我把3pc的几个状态和2pc的对应成一样的,init,prepared,precommit,commit)。3pc在第一阶段协调者和参与者如果同时挂掉和2pc第一阶段一样,备用协调者看到的是prepared和init的状态或者全部是init或者全部是prepared状态,这个时候可以全部rollback。协调者如果在precommit的时候和第一个参与者同时挂掉,备用协调者看到的全是prepared的状态,可以选择rollback,挂掉的第一个参与者恢复以后如果是rollback自然ok,如果是precommit,也可以rollback,这是和2pc最大的不同。如果备用协调者看到了precommit的状态意味着之前的协调者做出的决策是precommit,可以把commit流程继续下去。
2.3pc的另一个关键是有timeout时间,所以无论是协调者或者参与者只要是活着的都知道怎么走下去。比如在发送precommit的过程中如果部分机器挂掉,部分机器是precommit的状态,部分机器是prepared的状态,协调者接收不到有些机器的响应就会发送rollback,那些precommit的机器就应该超大概率响应rollback,那些prepared的机器会超时rollback,最总达成状态一致。最后一个阶段即使部分机器接受不到commit命令最后也会在timeout以后commit达成一致。
3.3pc似乎怕断网,前面部分precommit部分prepared的状态,如果协调者发送rollback的时候precommit那些机器断网了那就状态不一致了,似乎只能报警人为干预了?

TCC

1.try,confirm,cancel,这是服务层而不是数据库的事务方案,需要在业务层面实现三个方法,需要在原有数据库表上加上一个冻结字段,在try的时候冻结,confirm的时候在目标字段上更新冻结字段,cancel的时候取消冻结
2.冻结字段的好处是不会被读到中间状态产生读不一致,缺点是侵入性强,实现三个方法复杂度大。

SAGA

1.和tcc一样是服务层的实现,有侵入性,但不需要加字段,只需要实现两个方法即正常的业务操作和反向业务操作即可。
2.和tcc比,优点是实现简单,缺点是可能会被读到中间状态类似数据库事务的读未提交。和2pc比没有2pc那样为了保证隔离性而造成的所有资源加锁。
3.做分布式事务的一种土办法是用消息系统+数据库实现软事务,业务1成功+消息发出 消息消费+业务2成功。saga可以看成这种土办法的自动化加强版本,这种状况下因为消息可能重投所以需要业务做幂等。
4.saga事务状态流转控制可以用集中控制器,也可以用基于event的分布式流转的方式。




1 条评论: