分布式事务解决方案探讨
一、分布式事务特性
Consistency:一致性
Availability:可用性
Partition tolerance:分区容忍性
二、分布式事务存在问题
在分布式数据库中,如果期望达到数据的强一致性,那么服务基本没有可用性可言,这也是为什么许多分布式数据库提供了跨库事务,但也只是个摆设的原因,在实际应用中我们更多追求的是数据的弱一致性或最终一致性,为了强一致性而丢弃可用性是不可取的。如果确实存在数据不一致可以用人工补偿
三、两段式提交
添加一个事务协调器,让事务协调器,先预走一下流程,如果都能走通就说明可以执行。实现原理是当发起请求时 并没有正真得提交而是占用了资源。
缺点:长期占用资源开销大、效率低、不能100%保证事务。CAP原理(三者不可兼得)。
四、三阶段提交
概述:针对两阶段提交存在的问题,三阶段提交协议通过引入一个“预询盘”阶段,以及超时策略来减少整个集群的阻塞时间,提升系统性能。三阶段提交的三个阶段分别为:can_commit,pre_commit,do_commit。
优势:使用成本低,适用于有很多服务件间事务的处理。
第一阶段(can_commit)
该阶段协调者会去询问各个参与者是否能够正常执行事务,参与者根据自身情况回复一个预估值,相对于真正的执行事务,这个过程是轻量的,具体步骤如下:
- 协调者向各个参与者发送事务询问通知,询问是否可以执行事务操作,并等待回复
- 各个参与者依据自身状况回复一个预估值,如果预估自己能够正常执行事务就返回确定信息,并进入预备状态,否则返回否定信息
第二阶段(pre_commit)
- 本阶段协调者会根据第一阶段的询盘结果采取相应操作,询盘结果主要有三种:
- 所有的参与者都返回确定信息
- 一个或多个参与者返回否定信息
- 协调者等待超时
- 针对第一种情况,协调者会向所有参与者发送事务执行请求,具体步骤如下:
- 协调者向所有的事务参与者发送事务执行通知
- 参与者收到通知后,执行事务,但不提交
- 参与者将事务执行情况返回给客户端
- 在上面的步骤中,如果参与者等待超时,则会中断事务。 针对第二、三种情况,协调者认为事务无法正常执行,于是向各个参与者发出abort通知,请求退出预备状态,具体步骤如下:
- 协调者向所有事务参与者发送abort通知
- 参与者收到通知后,中断事务
第三阶段(do_commit)
如果第二阶段事务未中断,那么本阶段协调者将会依据事务执行返回的结果来决定提交或回滚事务,分为三种情况:
- 所有的参与者都能正常执行事务
- 一个或多个参与者执行事务失败
- 协调者等待超时
针对第一种情况,协调者向各个参与者发起事务提交请求,具体步骤如下:
- 协调者向所有参与者发送事务commit通知
- 所有参与者在收到通知之后执行commit操作,并释放占有的资源
- 参与者向协调者反馈事务提交结果
针对第二、三种情况,协调者认为事务无法正常执行,于是向各个参与者发送事务回滚请求,具体步骤如下:
- 协调者向所有参与者发送事务rollback通知
- 所有参与者在收到通知之后执行rollback操作,并释放占有的资源
- 参与者向协调者反馈事务提交结果
在本阶段如果因为协调者或网络问题,导致参与者迟迟不能收到来自协调者的commit或rollback请求,那么参与者将不会如两阶段提交中那样陷入阻塞,而是等待超时后继续commit。相对于两阶段提交虽然降低了同步阻塞,但仍然无法避免数据的不一致性。
五、TCC(Try Confirm Cancel)解决方案
- Try:请求来了后 直接操作数据落库。
- Confirm:选择性得执行一些操作。
- Cancel:当请求出现异常就,逆向执行Try中得一些操作。达到回滚的效果。
问题1:如果A给B转账10元 在Try中执行完以上操作后,可能会出现当事务后面还未执行成功就宕机了将导致数据无法回滚。解决办法在try中线减掉A的10元,然后再Confirm中最后一步再给B加上10元
可以根据业务。
问题2:cancel执行失败了,设置重试次数,如果重试多次任未成功,此时就需要人工介入了。
使用【MQ + 定时器 + 本地事件表】分布式事务
- 实现流程:
B服务 发起请求,执行请求,执行完成后存入到一个事件表中,然后再写一个定时器监听事件表数据,如果有新建就发MQ到消息队列,发送完成后改变事件表状态为已发送(以上逻辑再一个服务中实现可保证事务一致性)。然后B服务开启消费者,监听消息如果有消息就拿过来执行,并返回ACK到MQ。在B服务也建立一个事件表存放消费的消息,同时也可以该对应的状态。 - 使用场景:
当有两个不同服务间需要保证事务一致性时可以使用此方案。 - 优势:
吞吐量高、逻辑实现清晰、便于排错。 - 缺点:
搭建太复杂
5.此分布事务的类型
属于属于最终一致。
可靠性消息服务
通过上面分布式事务模型演变,事务可靠性处理拆分处理单独做一个服务。当请求到服务A,请求可靠性服务创建一条消息,然后返回结果;服务A继续执行到db操作,执行完后再向可靠性服务发送一条消息,修改服务A创建的消息状态,(此处有个疑问:如果状态修改失败,该如何处理?设置消息每个状态的超时时间,如果超时还没有改状态就反查服务A看是否真的执行成功)修改完成后就发送消息到MQ,消费者收到消息后就执行实际操作,执行完成后再次调用可靠性服务,再次对服务状态进行修改。