什么是分布式事务,分布式事务的解决方案有哪些?
事务是指一组整体操作,这些操作要么全部成功,要么全部失败,以确保数据的一致性。例如,银行转账就是一个典型的事务:从A账户扣钱和给B账户加钱必须同时成功或同时失败。
而当这一组整体操作跨越多个独立的系统或服务,就是分布式事务
首先介绍两阶段提交的思想,因为XA,AT,TCC这三个解决方案都或多或少借鉴了这个思想。
两阶段提交(2PC)
两阶段提交是一种经典的分布式事务协议。它将事务的提交过程分为两个阶段:准备阶段和提交阶段。
- 准备阶段(Prepare): 协调者向所有参与者发送事务预提交请求。参与者收到请求后,会执行事务,但不会真正提交,而是将结果记录下来,并锁定相关资源,然后向协调者返回“同意”或“拒绝”。
- 提交阶段(Commit/Rollback): 协调者根据所有参与者的响应做出决定。
- 如果所有参与者都返回“同意”,协调者向所有参与者发送“提交”请求,参与者正式提交事务并释放资源。
- 如果有任何一个参与者返回“拒绝”,或者协调者在超时时间内未收到所有响应,协调者会向所有参与者发送“回滚”请求,参与者回滚事务并释放资源。
正常情况:
异常情况:
一阶段:
- 事务协调者通知每个事物参与者执行本地事务
- 本地事务执行完成后报告事务执行状态给事务协调者,此时事务不提交,继续持有数据库锁
二阶段:
- 事务协调者基于一阶段的报告来判断下一步操作
- 如果一阶段都成功,则通知所有事务参与者,提交事务
- 如果一阶段任意一个参与者失败,则通知所有事务参与者回滚事务
XA模式
Seata中的XA模式就是基于两阶段提交的实现:
TM(Transaction Manager,事务管理器)
RM(Resource Manager,资源管理器)
TC(Transaction Coordinator,事务协调器)
RM一阶段的工作:
① 注册分支事务到TC
② 执行分支业务sql但不提交
③ 报告执行状态到TC
TC二阶段的工作:
-
TC检测各分支事务执行状态
a.如果都成功,通知所有RM提交事务
b.如果有失败,通知所有RM回滚事务
RM二阶段的工作:
- 接收TC指令,提交或回滚事务
优点
- 强一致性,满足ACID原则。
- 常用数据库都支持,实现简单,并且没有代码侵入
缺点
- 因为一阶段需要锁定数据库资源,等待二阶段结束才释放(RM二阶段才提交或回滚事务),性能较差
- 依赖关系型数据库实现事务
AT模式
缺弥补了XA模型中资源锁定周期过长的缺陷。,Seata推出了AT模式牺牲了部分一致性,但是性能大大提高了
阶段一RM的工作:
- 注册分支事务
- 记录undo-log(数据快照)
- 执行业务sql并提交
- 报告事务状态
阶段二提交时RM的工作:
- 删除undo-log即可
阶段二回滚时RM的工作:
- 根据undo-log恢复数据到更新前
流程图:
AT模式的优点:
- 一阶段完成直接提交事务,释放数据库资源,性能比较好
- 利用全局锁实现读写隔离
- 没有代码侵入,框架自动完成回滚和提交
AT模式全局锁替代DB锁锁定数据库记录,虽然资源锁定时间不变,使得有冲突的事务的并发能力没变(即使有数据库连接剩余)
但是数据库资源得到高效利用(只有阶段1的本地事务和阶段2的回滚需要用到业务数据库的连接和数据库行级锁,如果发生回滚,那其实连接占用时长和XA一样,但是极少数情况下才回滚),数据库连接高效轮转,XA模式则是连接被"虚假占用",同样的一个全局事务AT可能要2个数据库连接,XA可能需要5个,可见AT模式的优点
数据库连接资源得到充分利用
无冲突事务的并发处理能力大幅提升(这是大部分场景)
系统瓶颈从连接池转移到了CPU/内存
AT模式的缺点:
- 两阶段之间属于软状态,属于最终一致
- 框架的快照功能会影响性能,但比XA模式要好很多
TCC模式
TCC 模式是一种补偿型事务,可以看作是应用层自己实现的两阶段提交
- Try: 尝试执行业务,检查并预留资源。
- Confirm: 如果所有参与者的 Try 操作都成功,则执行 Confirm 操作,完成资源操作业务。
- Cancel: 如果任何一个参与者的 Try 操作失败,则执行 Cancel 操作,释放预留的资源。可以理解为try的反向操作
优点
- 一阶段完成直接提交事务,释放数据库资源,性能好
- 相比AT模型,无需生成快照,无需使用全局锁,性能最强
- 不依赖数据库事务,而是依赖补偿操作,可以用于非事务型数据库
缺点
- 有代码侵入,需要人为编写try、Confirm和Cancel接口,太麻烦
- 软状态,事务是最终一致
- 需要考虑Confirm和Cancel的失败情况,做好幂等处理
在编写代码时要注意空回滚和事务悬挂问题
1)空回滚
当某分支事务的try阶段阻塞时,可能导致全局事务超时而触发二阶段的cancel操作。在未执行try操作时先执行了cancel操作,这时cancel不能做回滚,就是空回滚。
如图:
执行cancel操作时,应当判断try是否已经执行,如果尚未执行,则应该空回滚。
2)业务悬挂
对于已经空回滚的业务,之前被阻塞的try操作恢复,继续执行try,就永远不可能confirm或cancel ,事务一直处于中间状态,这就是业务悬挂。
执行try操作时,应当判断cancel是否已经执行过了,如果已经执行,应当阻止空回滚后的try操作,避免悬挂
Saga模式
在 Saga 模式下,分布式事务内有多个参与者,每一个参与者都是一个冲正补偿服务,需要用户根据业务场景实现其正向操作和逆向回滚操作。
分布式事务执行过程中,依次执行各参与者的正向操作,如果所有正向操作均执行成功,那么分布式事务提交。如果任何一个正向操作执行失败,那么分布式事务会去退回去执行前面各参与者的逆向回滚操作,回滚已提交的参与者,使分布式事务回到初始状态。
Saga也分为两个阶段:
- 一阶段:直接提交本地事务
- 二阶段:成功则什么都不做;失败则通过编写补偿业务来回滚
优点
- 无全局锁、不占用数据库长连接,适合跨服务/长流程场景
- 吞吐量高、可用性好,少受单点事务管理器影响
- 可灵活编排(Orchestrator 或 Choreography),易与异步消息集成
缺点
- 需为每一步手动编写补偿逻辑,开发成本高
- 只能保证最终一致,不支持隔离性与强一致
- 补偿操作本身也可能失败,需额外机制保证补偿可靠性
以上图片均出自虎哥教程