分布式事务
Posted Linus1
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了分布式事务相关的知识,希望对你有一定的参考价值。
分布式之分布式事务
2. 分布式事务
假设多个服务要触发一系列连续的操作,每个操作涉及到不同的数据库,且这一套操作要么全部成功,要么全部失败,那么分布式事务就是保证这一套发生于不同服务的、涉及不同数据库的操作是一个事务操作,构成一个全局事务
假设你要创建一个申请购买电脑的工单,存在工单处理、资产审批、资产购买这三个微服务,且对应三个库,那么就必须使得下面一套流程遵循事务特性:修改审批状态为通过->修改资产购买状态为完成->修改工单状态为结束
2.1 两阶段提交(2 Phase Commit)
2.1.1 定义
通过引入单点协调者来协调多个需要一次完成的多个事务,通过协调者保证这一套事务的成功提交
2.1.2 流程
-
投票阶段
- 协调者向所有参与者发出投票请求
- 参与者收到后开始执行事务,但不提交,如果
- 执行成功,则返回可以提交
- 执行失败,则返回需要回滚
-
提交阶段
协调者如果
-
收到所有反馈,且
- 全为成功,则发出提交请求至参与者,参与者收到后进行commit,并返回ack
- 不全为成功,则发出回滚请求至参与者,参与者收到后进行rollback,并返回ack
-
在规定时间内未收到所有反馈
向所有参与者发出回滚请求,参与者收到后进行rollback,并返回ack
-
2.1.3 故障分析
-
只有参与者宕机
-
在投票阶段宕机(非阻塞)
协调者会超时,之后让其他协调者rollback
-
在提交阶段宕机(非阻塞)
由于协调者会将二阶段做出的决策写入日志,当此参与者恢复时,可以通过询问协调者得到此决策,来决定时回滚还是提交,注意,这里协调者不会阻塞等待参与者恢复
-
-
只有协调者宕机
-
在投票阶段宕机(非阻塞)
所有参与者会推选出新的协调者
-
在提交阶段宕机
-
如果没有任何参与者收到协调者的决策
所有参与者推选出新的协调者来重启2PC
-
如果有参与者收到了协调者的决策(非阻塞)
收到信息的参与者可以将决策传播给其他参与者,完成提交或回滚
-
-
-
参与者和协调者均宕机
-
均在第一阶段宕机(非阻塞)
剩余的参与者推选新的协调者
-
参与者第一阶段宕机&协调者第二阶段宕机
-
如果没有任何剩余活跃参与者收到协调者的决策(非阻塞)
所有活跃参与者推选出新的协调者来重启2PC
-
如果有剩余活跃参与者收到了协调者的决策(非阻塞)
收到信息的参与者可以将决策传播给其他参与者,完成提交或回滚
-
-
参与者第二阶段宕机&协调者第一阶段宕机
不存在
-
均在第二阶段宕机(阻塞!!!)
这里考虑下面时序:
协调者向参与者A发出提交请求 -> 协调者宕机 -> 参与者A收到提交请求并写盘 -> 参与者A宕机
那么此时如果剩余的活跃的参与者推选出新的协调者,决定回滚, 之后原协调者恢复发出提交,这样两者就产生冲突!
上面时序尽管苛刻,但是对于参与者和协调者均在第二阶段宕机这个情况,从剩余活跃的参与者角度来看,并不清楚
- 是否协调者发出提交/回滚请求
- 是否宕机的参与者收到了提交/回滚请求
这样,剩余的参与者只能等待协调者恢复
-
2.1.4 缺点
-
单点故障
如果协调者单点出现问题,那么整个事务就失去了原子性保证,可以通过推选新的协调者来重新执行一次2PC流程
-
同步阻塞
- 2PC整个阶段,协调者和参与者都要保持同步
- 故障分析中最后一个例子,会使得所有活跃的参与者陷入阻塞状态
2.2 三阶段提交(3 Phase Commit)
2.2.1 定义
同样使用单点协调者,相较于2PC引入了多了一个阶段,用于解决2PC中协调者第二阶段宕机,所有参与者阻塞的问题
2.2.2 流程
-
CanCommit阶段
-
协调者发出CanCommit请求
-
参与者收到后,尝试获取数据库锁,检查执行事务所需资源是否就绪,如果
-
就绪
返回ack
-
未就绪
返回nack
-
-
-
PreCommit阶段
-
协调者如果收到所有ack,则发出预提交请求
-
参与者收到后,执行事务,但不提交,如果
-
执行成功
返回ack
-
执行失败
返回nack
-
-
-
DoCommit阶段
-
协调者如果收到事务执行结果均为成功
发出commit请求,所有参与者收到后commit,并ack
-
存在失败
发出rollback请求,所有参与者收到后rollback,并ack
-
2.2.3 故障分析
这里仅对协调者分析
-
CanCommit阶段宕机
参与者重选协调者
-
PreCommit阶段宕机
代表所有参与者状态正常,且没有或只有一部分参与者执行了事务,这时参与者会推选新的协调者接着此阶段继续执行
-
doCommit阶段宕机
所有参与者此时处于执行事务但未提交状态,只要有任意参与者收到commit则进行提交,如果没有参与者收到,则超时后进行提交
2.2.4 为何3PC可以解决2PC问题
2PC的问题在于,参与者不清楚协调者的状态,只有协调者直到所有参与者的状态。而3PC通过将2PC的第一阶段拆分成CanCommit和PreCommit,之后由CanCommit保证协调者预备状态良好,由PreCommit告知参与者协调者意欲完成这次提交,这样就可以让参与者遇到协调者
- 在第三阶段宕机时可以有充分保证在超时状态下提交
- 在第二阶段宕机时,此时未进行提交,可以重选协调者继续流程
2.2.5 缺点
- 在协调者宕机时,由于网络分片问题,可以会推举出多个新的协调者,当时数据不一致
- 多了一个阶段,使得网络延迟增大,使得全局事务阻塞时间也增大
2.3 TCC(Try Confirm Cancel)
这里摘抄周志明老师的原文
"整体流程类似于2PC,但 TCC 是位于用户代码层面,而不是在基础设施层面,这为它的实现带来了较高的灵活性,可以根据需要设计资源锁定的粒度"
—————分布式事务 | 凤凰架构 (icyfenix.cn)
2.4 其他方案
还有本地消息表、基于消息队列和尽最大努力通知三个手段可以参见
面试必问:分布式事务六种解决方案 - 知乎 (zhihu.com)
# 参考
分布式事务:两阶段提交与三阶段提交 - SegmentFault 思否
Two-phase commit protocol - Wikipedia
终于有人把“TCC分布式事务”实现原理讲明白了! - JaJian - 博客园 (cnblogs.com)
Lecture - 25 Basic 2-Phase and 3-phase commit protocol - YouTube
以上是关于分布式事务的主要内容,如果未能解决你的问题,请参考以下文章