分布式事务的详细介绍
一、引言
在微服务架构日益普及的今天,分布式事务已成为后端开发中不可回避的技术挑战。当业务逻辑跨越多个服务、多个数据库时,如何保证数据的一致性和事务的完整性,成为了架构师和开发者必须深入理解和掌握的核心技能。
本文将从理论基础到实战应用,全面解析分布式事务的核心概念、实现原理和最佳实践,帮助你构建完整的分布式事务知识体系。
二、分布式事务的基本概念
分布式事务是指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于不同的分布式系统的不同节点之上。简单来说,就是一次大的操作由不同的小操作组成,这些小操作分布在不同的服务器上,且属于不同的应用,分布式事务需要保证这些小操作要么全部成功,要么全部失败。
为什么需要分布式事务?
在微服务架构中,业务操作通常需要跨多个服务来完成,这就产生了分布式事务的问题。传统的单机事务无法满足跨服务的数据一致性需求。例 如:
跨服务转账:银行A向银行B转账,必须同时成功或失败。
订单支付:创建订单+扣减库存+支付,多系统状态必须一致。
物流跟踪:订单状态更新+物流记录创建,数据必须同步变更。
三、分布式事务的核心挑战
分布式事务面临的主要挑战包括:
网络不确定性:分布式系统中的网络延迟、分区、消息丢失等问题。
性能瓶颈:全局锁和同步阻塞导致系统吞吐量下降。
可用性降低:参与节点越多,整体可用性越低(如三个99.9%可用性的服务组合后可用性降为99.7%)。
复杂性增加:需要处理各种异常情况和恢复机制。
3.1 挑战的理论根源:CAP不可能三角
所有挑战都围绕着CAP定理展开。该定理指出,在存在网络分区(P)的分布式系统中,一致性(C)和可用性(A)无法同时被完美满足。
网络不确定性是触发P的客观现实:网络延迟、分区和消息丢失是分区容错性(P)的具体表现。由于P在广域网环境中不可避免,系统时刻面临在C和A之间做出抉择的局面。这是所有分布式事务挑战的总根源。
3.2 具体挑战的理论映射
性能瓶颈与可用性降低:强一致性(CP)的代价
当业务要求强一致性(选择C)时,系统必须采用同步协调机制(如两阶段提交2PC)。这直接导致:
全局锁和同步阻塞:为了确保数据强一致,事务执行过程中必须锁定资源并等待所有节点确认,系统吞吐量随之下降,性能瓶颈由此产生。
链式依赖放大故障:在同步阻塞模型中,任一参与节点的故障或延迟(其本身可用性不足)都会导致整个事务链的阻塞,从而指数级地降低整体系统的可用性(如三个99.9%可用性服务组合后降至约99.7%)。
复杂性的必然性:弥补理论缺口的技术负债
无论选择CP还是AP路径,为了弥补所牺牲的特性,系统都会变得异常复杂:
选择 CP:为了强一致性,必须引入事务协调器、日志恢复、死锁处理、数据核对等一系列复杂机制来保证原子性和一致性,并需处理协调者单点故障问题。
选择 AP:为了保证高可用性并接受最终一致性,则必须引入补偿操作(TCC/Saga)、幂等控制、异步消息、对账修复等复杂逻辑来保证业务的最终正确。
3.3 BASE理论与一致性模型:应对挑战的工程实践
为了在现实中生存,BASE理论对CAP的强一致性要求进行了务实的修正,并催生了不同的一致性模型,这正是工程师们直面上述挑战的解决方案。
从ACID到BASE的妥协:BASE理论(基本可用、软状态、最终一致)是对CAP中AP路径的扩展和落地。它承认在面临网络不确定性时,优先保证核心链路基本可用,允许数据存在中间软状态,通过重试、异步和补偿等手段,达到最终一致性。这直接规避了因强一致性带来的性能瓶颈和可用性降低问题。
一致性模型是权衡后的选择:强一致性、最终一致性、因果一致性等模型,就是在C与A之间不同平衡点的具体体现。最终一致性模型是应对高并发和可用性要求场景的普遍选择,它通过接受短暂的延迟,换取了系统处理网络不确定性的能力和更高的吞吐量。
四、分布式事务的解决方案
4.1 传统方案
1. 2PC(两阶段提交)
实现原理:
阶段一:协调者向所有参与者发送准备请求,参与者完成事务操作但不提交。
阶段二:协调者根据参与者反馈决定提交或回滚。
优缺点:
✅ 强一致性保证。
❌ 同步阻塞(参与者等待协调者)。
❌ 单点故障风险。
❌ 数据锁定时间长。
问题:
阶段2,事务协调者挂了,则所有参与者接受不到commit/rollback指令,将处于"悬而不决"状态
阶段2,其中一个参与者超时或者出错,其他参与者无法确定是commit还是rollback
2PC主要在数据库层面实现(XA协议),不适合微服务架构。
2. 3PC(三阶段提交)
阶段流程:CanCommit → PreCommit → DoCommit。
CanCommit阶段(询问阶段)
核心目的:确认所有参与者是否能够执行事务。
协调者向所有参与者发送CanCommit请求,询问是否可以执行事务(如尝试连接数据库、锁资源等)。
参与者响应:
如果可以执行事务,返回Yes。
如果无法执行事务,返回No。
决策:
所有参与者都返回Yes → 进入PreCommit阶段。
有任何参与者返回No → 进入Abort阶段。
这个阶段不涉及实际的数据修改,只是确认参与者是否具备执行事务的能力。
PreCommit阶段(预提交阶段)
核心目的:参与者准备执行事务,但不真正提交。
协调者向所有参与者发送PreCommit请求。
参与者收到请求后:
执行本地事务操作(如锁定资源)。
写入Undo/Redo日志(用于后续回滚或提交)。
向协调者发送确认消息(如Prepared)。
决策:
协调者在指定时间内收到所有参与者的确认 → 进入DoCommit阶段。
协调者超时或收到任何参与者的Abort信息 → 进入Abort阶段。
这个阶段是3PC的关键改进,它将2PC的单一"准备"阶段拆分为两个阶段,为后续的提交做好准备。
DoCommit阶段(提交阶段)
核心目的:执行最终的事务提交。
协调者向所有参与者发送DoCommit请求。
参与者收到请求后:
执行正式的事务提交。
释放所有事务资源。
向协调者发送确认响应。
协调者收到所有响应后,事务完成。
如果协调者在DoCommit阶段发送请求后,未收到所有参与者的响应(超时),则:协调者向所有参与者发送Abort请求,参与者执行事务回滚。
改进:
3PC的核心改进在于新增预提交阶段,减少阻塞时间;在协调者和参与者中都引入了超时机制,这是解决2PC阻塞问题的关键。
协调者超时机制:
CanCommit阶段:协调者设置超时时间,如果在超时前未收到所有参与者的响应,协调者假设某些参与者已失败,中断事务并发送Abort命令。
PreCommit阶段:协调者等待参与者确认,如果超时未收到足够确认,发送Abort请求。
DoCommit阶段:协调者等待参与者确认,如果超时未收到所有确认,发送Abort请求。
参与者超时机制
这是3PC与2PC最重要的区别,参与者在超时后可以自主决定事务状态:
CanCommit阶段:参与者如果超时未收到协调者请求,可以假设协调者已失败,自行决定Abort事务。
PreCommit阶段:参与者如果超时未收到协调者指令,自动提交(安全,因为所有参与者已通过预询问,确认了提交条件)。
DoCommit阶段:参与者不会发生超时,因为PreCommit阶段超时后已自动提交,不会进入DoCommit阶段。(原因:3PC设计确保在PreCommit阶段超时后事务已提交,DoCommit阶段仅是协调者发送正式提交指令的阶段,不会出现等待超时)
3PC的超时机制确保了即使协调者故障,参与者也能在有限时间内做出决策,避免了2PC中可能出现的"无限期等待"问题。
优缺点:
✅ 减少了因协调者失败导致的阻塞。
✅ 提高了系统的可用性。
❌ 实现复杂性增加。
❌ 仍然可能存在网络分区问题。
4.2 现代解决方案
1. TCC(Try-Confirm-Cancel)
核心思想:需要开发者手动实现Try、Confirm、Cancel三个阶段的业务逻辑。
适用场景:对性能要求较高的场景。
实现流程:Try:尝试执行业务,预留资源。Confirm:确认执行,完成业务。Cancel:取消执行,释放预留资源。
TCC 的具体流程:
Try阶段(资源预留)
核心目的:业务检查(一致性) + 预留资源(准隔离性)
执行内容:
检查业务前提条件(如账户余额是否充足)。
预留业务资源(如冻结余额、锁定库存)。
不真正执行业务操作,仅做准备。
返回结果:
成功:返回全局唯一事务ID(XID)。
失败:触发Cancel阶段。
示例:在电商下单场景中,Try阶段会检查库存是否充足,并将库存数量"冻结",但不真正扣减。
Confirm阶段(业务确认)
核心目的:执行真正的业务操作。
执行内容:
基于Try阶段预留的资源,执行实际业务操作。
不进行业务检查(因为Try阶段已验证)。
要求幂等性(多次执行结果相同)。
返回结果:
成功:事务完成。
失败:触发Cancel阶段。
示例:在电商场景中,Confirm阶段会将"冻结库存"真正扣减。
Cancel阶段(业务取消)
核心目的:释放Try阶段预留的资源。
执行内容:
撤销Try阶段预留的资源。
回滚可能的业务操作。
要求幂等性。
触发条件:
Try阶段失败。
Confirm阶段失败。
事务超时。
示例:在电商场景中,Cancel阶段会将"冻结库存"退回,恢复到初始状态。
TCC核心难点与解决方案:
幂等性问题
问题:Confirm和Cancel接口可能被多次调用。
解决方案:
为每个事务分配唯一ID(XID)
通过事务ID记录操作状态,避免重复执行
例如:订单服务通过订单ID判断是否已确认
空回滚问题
问题:Try未执行,但Cancel被调用
解决方案:
在Cancel接口中校验"是否有对应的资源预留记录"。
若无,则直接返回成功(空操作)。
悬挂问题
问题:Cancel在Try之前执行(因网络延迟)
解决方案:
通过"事务状态记录"判断。
若Try未执行,则Cancel直接返回成功。
超时处理
问题:网络波动导致事务超时。
解决方案:
设置合理的超时时间。
实现重试机制,确保最终一致性。
TCC的优缺点:
优点:
高性能:不长时间占用数据库连接,适合高并发场景通过业务层资源预留,将锁粒度控制在业务级别,例如:冻结余额字段,而不是锁住整行记录。
灵活性:允许业务方自定义资源锁定粒度。业务逻辑可以更精确地控制事务边界。
跨资源支持:不依赖底层数据库,可以支持跨数据库、跨应用的事务。
缺点:
业务侵入性强:需要在业务代码中实现Try、Confirm、Cancel三个阶段。对现有系统改造成本高,需要所有参与服务共同配合改造。
开发难度大:
需要保证Confirm和Cancel接口的幂等性。需要处理"悬挂"和"空回滚"问题。
补偿逻辑复杂:
Cancel接口的补偿逻辑需要精准覆盖Try阶段的资源预留。复杂业务场景(如多级资源依赖)下,补偿逻辑极易出错。
2. SAGA模式
核心思想:
将分布式事务拆分成一系列局部事务,每个局部事务都有一个补偿事务来撤销操作
适用场景:
业务流程较长的分布式事务,适用于长时间运行的业务流程
实现流程:
将整个事务分为多个局部事务。
每个局部事务执行后,若失败,则触发补偿事务进行回滚。
优点:
适合长时间运行的业务流程:适用于需要多个步骤、耗时较长的业务流程(如旅行预订、复杂订单处理),不需要长时间锁定资源。
高可用性:每个本地事务独立执行,失败不会导致整个系统阻塞。服务间解耦,单个服务故障不会影响整个事务。
灵活性:事务执行路径可以根据业务需要灵活调整,可以处理复杂的业务场景。
数据一致性:通过补偿机制保证最终一致性,短暂的数据不一致是可接受的。
缺点:
补偿逻辑复杂:需要为每个业务操作实现精确的补偿逻辑,补偿逻辑可能比业务逻辑更复杂。
短暂数据不一致:在事务执行过程中,数据可能处于不一致状态需要业务设计考虑这种不一致的影响。
实现复杂度高:需要设计事务状态管理、补偿执行机制,需要处理各种异常情况。
3. AT模式
介绍:
AT模式通过代理数据源自动记录事务前后的数据快照,实现事务的自动提交和回滚,无需修改业务代码,对开发者友好。
核心思想:
通过代理数据源自动记录事务前后的数据快照,实现事务的自动提交和回滚.
适用场景:
最适合初学者入门的分布式事务解决方案。
工作原理:
代理数据源:在数据源层面对SQL进行拦截。
生成undo日志:记录事务前后的数据快照。
自动回滚:当事务失败时,根据undo日志自动回滚。
优点:
对业务无侵入:无需修改业务代码,只需添加@GlobalTransactional注解,适合快速上手和集成。
自动回滚:通过undo日志自动实现事务回滚,无需实现补偿逻辑。
高性能:基于数据源代理,对性能影响较小,本地事务执行时间短。
简单易用:适合初学者和快速开发场景,与Spring Cloud微服务架构无缝集成。
缺点:
数据库依赖:依赖于支持事务的数据库(如MySQL),不支持NoSQL数据库。
资源占用:undo_log表会占用额外存储空间,大事务可能导致undo_log过大。
数据一致性:仅保证最终一致性(在回滚时),与强一致性方案相比,有短暂数据不一致。
4. 基于消息队列的最终一致性
核心思想:通过异步消息传递,各个服务通过接收消息来保持数据一致性
实现方案:
方案1:业务方自己实现
Producer端准备消息表,将update DB和insert message放在一个DB事务中。
后台程序不断扫描本地消息表,将消息发送给消息中间件。
Consumer端准备判重表,保证消息处理的幂等性。
方案2:RocketMQ事务消息
发送Prepared消息。
update DB。
根据update DB结果确认或取消Prepared消息。
RocketMQ定期扫描Prepared消息,询问发送方是否确认。
优点:
高可用性和系统性能,异步处理减少了耦合度。
缺点:
需要实现补偿机制,增加了复杂性;数据在短时间内可能处于不一致状态
五、分布式事务的选型与最佳实践
- 选型原则
避免过度依赖分布式事务:优先通过本地事务和幂等性设计解决问题,仅在必要时引入分布式事务
选择合适的一致性模型:根据业务需求权衡强一致性与最终一致性。
金融场景:选择2PC或TCC。
电商场景:选择TCC或SAGA。
消息通知:选择最终一致性。
幂等性设计:确保重试操作不影响业务结果,如接口支持唯一标识和重复请求过滤。 - 性能优化
异步化处理:将非核心操作(如日志记录)异步化,减少事务执行时间
资源预分配:在TCC模式中提前锁定资源,避免并发冲突.
读写分离:通过数据库读写分离减轻主库压力,提升事务处理性能. - 故障处理
超时机制:设置合理的超时时间,避免事务长时间阻塞.
补偿机制:设计可回滚的补偿操作,处理事务失败后的状态恢复.
监控与报警:实时监控事务状态,及时发现并处理异常.
1061



被折叠的 条评论
为什么被折叠?



