最近在看分布式事务,于是网上搜索相关资料,霎时间,对方给我扔来了一大堆概念,什么二阶段提交、三阶段提交、TCC补偿机制、CAP理论、ACID、BASE等等等,它们被零散地分散到各种各样的文章里。这里我根据自己所学习到的,以及所理解的方式,将这些概念放到一起谈一谈,一方面做个记录,一方面也希望可以帮助像鄙人一样的新手将这些概念有机地串连起来
话不多说,不再说那么多关于单机系统到分布式系统的介绍了,在分布式系统的设计中,有位大佬提出了CAP理论,根据CAP,也延伸出了ACID、BASE这些理论
C、A、P分别代表了3个特性,这个理论是说这个3个特性不能共存,只能共存2个,为什么呢?
先假设一个分布式系统如下图所示
接下来说说C、A、P各个特性
写之后的读操作必须读到最新值
如:S1、S2上有a=0,然后client向S1修改了a=1,此时client再去S2读取a也要等于1
就是S1更改完值后要立即同步到S2
不同角度看:
根据一致性程度分:
只要用户发起请求,就要立即给出反应,不能有操作失败 / 请求超时的情况
某个服务故障了,整体依旧可以对外提供服务,这是设计分布式系统的基础
所以P在分布式系统中一般是必备的,不然就和单机系统没区别了
先假设P存在,则C(一致性)和A(可用性)就会矛盾了
注意
:这里的一致性其实是针对强一致性,弱一致性和可用性还是可以共存的
分区容错下,多个服务间通信一定会有网络问题
- 满足强一致性时,一定会有数据同步的阻塞时间,导致客户端访问被阻塞,就达不到可用性了
- 满足可用性时,客户端访问要立即返回,这时数据还没同步好,得到的是不一致的数据
那假设P不存在,则CA可以共存,这就类似一个单机应用,没了分布式可言
这个大家都比较熟悉了,是关系型数据库事务的特性
其实它也是满足CA的情况
这是CAP的延伸,权衡了可用性和一致性而提出的理论,相当于满足PA的情况
为什么说相当于呢,因为并没有完全舍弃C,只是舍弃强一致性,保留了最终一致性
下面说说BASE的各个特性
允许系统中的某些数据处于中间状态,且这些中间状态不影响整体的可用性,即允许各个节点之间的数据同步存在延迟
不能保证每时每刻访问的都是最新的数据,但保证一段时间后,最终数据都是一致的
所以CAP、ACID、BASE说的都是分布式系统设计的理论,还没具体地说到分布式事务的相关理论
从上面看来,貌似ACID、BASE是不能共存,不过在实际的系统设计中,根据各个功能的要求,有机地结合ACID、BASE,寻找最优的协调方案才是最合适的
据我所知,现在实现分布式事务的设计方案应该有3种,分别就是二阶段提交、三阶段提交和TCC
他们都有2种重要的角色【事务协调者】【参与者,也就是各个服务】
准备阶段
1.3反馈准备提交或回滚 存在多种情况
- 所有参与者都反馈可以提交
- 有参与者反馈回滚(不管多少)
提交阶段
2.1根据1.3的情况
- 所有参与者都反馈可以提交–》通知全部提交
- 有参与者反馈回滚–》通知全部回滚
- 等待反馈超时–》通知全部回滚
2.2根据2.1
- 通知全部提交–》提交
- 通知全部回滚–》回滚
- 一直没收到请求–》阻塞住
可能出现问题:
● 同步阻塞:参与者反馈1.3后是处于阻塞状态等待2.1,如果网络问题或协调者宕机了,接受不到2.1,那么会一直阻塞
● 数据不一致:部分参与者接受不到2.1,阻塞中,而接受到的就进行提交或回滚了,造成数据不一致
改进二阶段提交的缺点问题,得到了三阶段提交
预判断
1.2反馈情况
- 所有参与者都反馈可以
- 有参与者反馈不能成功执行(不管多少)
准备提交
2.1根据1.2的情况
- 所有参与者都反馈可以–》通知全部准备提交
- 有参与者反馈不能成功执行–》通知全部 abort 通知
- 等待反馈超时–》通知全部 abort 通知
协调者发起abort通知后就会进入结束状态了,不再进行后续
2.2根据2.1
- 通知全部准备提交–》执行事务,不提交
- 通知全部 abort 通知–》会中断事务的操作
- 等待超时–》会中断事务的操作
提交阶段
3.1根据2.3的情况
- 所有参与者都反馈体可以提交–》通知全部提交
- 有参与者反馈回滚或中断事务–》通知全部回滚
- 等待超时–》通知全部回滚
3.2根据3.1
- 通知全部提交–》提交
- 通知全部回滚–》回滚
- 等待超时–》提交
为什么第三阶段等待超时就会自动提交呢?
因为经过了前面两阶段的判断,第三阶段可以提交的概率会大于回滚的概率
三阶段的参与者是有超时机制的,等待请求超时会进行事务中断,或事务提交
而二阶段不会超时,只会阻塞
数据库表需要存多两个字段【可更新数】【冻结数】
将各个服务的事务执行分成3个步骤
此时表示业务正常完成了
如果try出错了,那各个服务就执行cancel还原数据,相当于回滚
如果是confirm或cancel出错了,一般会去重复执行
因为过了try,可以认为confirm是一定可以执行成功的,除非重复执行次数达到阈值,就落地成日志,让人工处理
使用此方案需要我们在业务服务上实现【try】【confirm】【cancel】这3个接口
注意:
这里事务回滚的方式不像我们认为的那样 — 数据库直接给我们处理好
而是通过【cancel】将数据补回去,所以TCC也叫【补偿机制】
二阶段提交、三阶段提交、TCC分别是三种实现分布式事务的方案
至于具体的实现框架
二阶段有mysql的XA事务,TCC有seata、tcc-transaction
我们看TCC会涉及到服务与服务之间的接口调用,因为网络问题,极有可能出现重复调用的情况,所以【confirm】【cancel】这些接口应该要实现幂等
当然服务间的通信除了通过【同步的rpc】,也可以通过【异步的MQ】来实现,所以引出了接下来的基于MQ最终一致性方案
使用异步mq来辅助服务间通信,在服务发消息到消息进入MQ这个过程中,有可能会出现网络不稳定的情况,导致问题
根据事务执行和发消息的先后顺序,可分如下:
所以为了实现【事务提交】【消息发送成功】这两个操作是原子的,提出2种基于MQ最终一致性方案:
注意:可能会发生同个消息发送多次的情况,因此要求消费者在消费消息时做好幂等
需要为增加一张辅助表【事务记录表】,记录已提交的事务是否成功将消息发送到MQ
还要加入一个定时程序,到期去扫描【事务记录表】,看看哪些事务提交了但还没发消息到MQ,便去处理发送
除了上面的做法,还可以直接使用已经实现了事务模型的MQ,如RocketMQ
下面说说RocketMQ
1.half消息:在消息队列上开启事务,生产者给消息队列发送【半消息】(包含了完整的消息内容,只是对消费者还不可见)
2.执行事务
3.将事务的执行结果发送给消息队列
4.1.如果消息队列等不到第3步的反馈,那么自己会定期去检查事务的执行情况
4.2.根据事务执行情况决定【半消息】的去留
5.【半消息】可见后即可被消费者消费
以上就是鄙人对分布式事务中涉及的一些概念,通过自己的理解将他们放一起描述出来,如有错误,欢迎指正✧(≖ ◡ ≖✿)