一、分布式事务概述
分布式事务是指涉及多个不同资源或数据库的事务处理,这些资源或数据库分布在不同的网络节点上,通过协调器将多个事务组合成一个分布式事务。分布式事务的目的是确保多个事务操作要么全部成功,要么全部失败,保持事务的一致性和完整性。在分布式事务中,涉及到的节点可能位于不同的计算机、不同的进程或者不同的数据库系统中。
二、分布式事务的实现的方案
主要有以下 5 种:
- XA 方案:两阶段提交(X/Open XA),是分布式事务的标准。事务管理器(Transaction Manager)通过协调者(Coordinator)向所有参与者(Participant)发送请求,参与者接收到请求后进行准备操作,如果全部准备成功则向协调者发送响应,协调者收到所有参与者的响应后向事务管理器发送事务完成通知。
- TCC 方案:把业务流程拆分成多个原子操作,通过事务管理器来协调每个参与者的事务。每个参与者创建一个本地事务,事务管理器监控每个本地事务的状态,如果发现某个本地事务失败,则回滚该本地事务,如果所有本地事务都成功,则提交所有本地事务。
- 本地消息表:将需要分布式处理的操作以消息的形式存储在消息队列中,消息的接收方是各个业务节点,业务节点处理完消息后将状态更新到本地数据库。如果消息发送失败或业务节点执行失败,则需要重试。
- 可靠消息最终一致性方案:通过使用消息队列和确认机制实现分布式事务。当一个节点发送消息给其他节点时,其他节点收到消息后需要回复确认消息,如果发送失败则需要重试。
- 最大努力通知方案:当一个节点需要通知其他节点时,它需要尽最大努力将通知发送给所有节点,即使有节点无法接收到通知,也需要继续执行本地事务。
三、XA 方案
1. XA协议概述
XA协议是一个基于数据库的分布式事务协议,其分为两部分:
- 事务管理器。
- 本地资源管理器。
事务管理器:作为一个全局的调度者,负责对各个本地资源管理器统一号令提交或者回滚。二阶提交协议(2PC)和三阶提交协议(3PC)就是根据此协议衍生出来而来。如今Oracle、Mysql等数据库均已实现了XA接口。
2. XA协议目前状况
- XA 协议比较简单,而且一旦商业数据库实现了 XA 协议,使用分布式事务的成本也比较低。
- XA 性能不理想,特别是在交易下单链路,往往并发量很高,XA 无法满足高并发场景。
- XA目前在商业- 数据库支持的比较理想,在mysql数据库中支持的不太理想,mysql的XA 实现,没有记录 prepare 阶段日志,主备切换回导致主库与备库数据不一致。
- 许多 nosql也没有支持 XA,这让 XA的应用场景变得非常狭隘。
- 也有3PC,引入了超时机制(无论协调者还是参与者,在向对方发送请求后,若长时间未收到回应则做出相应处理)
3. 2PC协议
两个阶段:
第一阶段提交:
协调者收到客户端请求,协调者给每一个参与者发送prepare指令,参与者接收指令执行本地数据但不提交事务,并返回ready
第二阶段提交:
1)当第一阶段所有参与者返回接收prepare指令并返回ready时,协调者发送commit指令给参与者,所有参与者提交事务
2)当第一阶段返回超时或者参与者执行失败,协调者在第二阶段告诉所有参与者回滚
如图:
4. 案例1:订单管理和支付管理
假设有一个电商系统,包含了订单管理和支付管理两个模块。订单管理模块负责生成订单,并将订单状态更新到数据库中;支付管理模块负责处理用户的支付操作,并将支付结果更新到数据库中。这两个模块需要在一个事务中执行,确保订单和支付状态的一致性。
在这种情况下,可以使用 XA 方案来实现分布式事务。具体实现步骤如下:
- 订单管理模块生成订单,并将订单状态更新到数据库中。
- 支付管理模块处理用户的支付操作,并将支付结果更新到数据库中。
- 订单管理模块向 XA 协调器发送准备提交请求。
- 支付管理模块向 XA 协调器发送准备提交请求。
- XA 协调器接收到所有参与者的准备提交请求后,开始执行两阶段提交协议。
- 第一阶段提交协议:XA 协调器向所有参与者发送投票请求,并等待各个参与者的响应。如果某个参与者投票成功,则向该参与者发送准备提交请求,否则向该参与者发送准备回滚请求。
- 第二阶段提交协议:XA 协调器根据各个参与者的响应结果,决定事务的提交或回滚。如果所有参与者都投票成功,则向所有参与者发送提交请求,否则向所有参与者发送回滚请求。
- 订单管理模块和支付管理模块接收到提交或回滚请求后,进行相应的操作,确保订单和支付状态的一致性。
通过以上步骤,就可以使用 XA 方案实现分布式事务,保证订单和支付状态的一致性。需要注意的是,在实际应用中,还需要考虑性能问题、并发能力受限等问题。
5. 案例2:用户下单与扣减库存
一个下单流程会用到多个服务,各个服务都无法保证调用的其他服务的成功与否,这个时候就需要一个全局的角色(协调者)对各个服务(参与者)进行协调。
一个下单请求过来通过协调者,给每一个参与者发送Prepare消息,执行本地数据脚本但不提交事务。
如果协调者收到了参与者的失败消息或者超时,直接给每个参与者发送回滚(Rollback)消息;否则,发送提交(Commit)消息;参与者根据协调者的指令执行提交或者回滚操作,释放所有事务处理过程中被占用的资源,显然2PC做到了所有操作要么全部成功、要么全部失败。
如图:
6. XA协议优点:
- 简单易用:XA 方案是一种标准的事务管理协议,各个厂商都提供了相应的实现,因此使用起来非常简单易用。
- 事务的一致性:XA 方案可以实现分布式事务的一致性,即确保多个事务操作要么全部成功,要么全部失败,从而保持事务的一致性和完整性。
- 可扩展性:XA 方案是基于资源管理器的一种事务管理机制,因此可以很方便地扩展到多个数据库或资源管理器上,从而实现分布式事务的处理。
- 可靠性:XA 方案通过两阶段提交协议来保证事务的可靠提交或回滚,即在进行提交操作之前,会对各个资源管理器进行预提交操作,如果预提交操作成功,则进行提交操作,否则进行回滚操作,从而确保事务的可靠性。
7. XA协议缺点:
二阶段提交看似能够提供原子性的操作,但它存在着严重的缺陷
- 网络抖动导致的数据不一致: 第二阶段中协调者向参与者发送commit命令之后,一旦此时发生网络抖动,导致一部分参与者接收到了commit请求并执行,可其他未接到commit请求的参与者无法执行事务提交。进而导致整个分布式系统出现了数据不一致。
- 超时导致的同步阻塞问题: 2PC中的所有的参与者节点都为事务阻塞型,当某一个参与者节点出现通信超时,其余参与者都会被动阻塞占用资源不能释放。
- 单点故障的风险: 由于严重的依赖协调者,一旦协调者发生故障,而此时参与者还都处于锁定资源的状态,无法完成事务commit操作。虽然协调者出现故障后,会重新选举一个协调者,可无法解决因前一个协调者宕机导致的参与者处于阻塞状态的问题。
四、3PC 方案
1. 概述:
三个阶段:
1. CanCommit:
协调者向所有参与者发送CanCommit命令,询问是否可以执行事务提交操作。如果全部响应YES则进入下一个阶段。
- PreCommit:
协调者向所有参与者发送PreCommit命令,询问是否可以进行事务的预提交操作,参与者接收到PreCommit请求后,如参与者成功的执行了事务操作,则返回Yes响应,进入最终commit阶段。一旦参与者中有向协调者发送了No响应,或因网络造成超时,协调者没有接到参与者的响应,协调者向所有参与者发送abort请求,参与者接受abort命令执行事务的中断。
- DoCommit:
在前两个阶段中所有参与者的响应反馈均是YES后,协调者向参与者发送DoCommit命令正式提交事务,如协调者没有接收到参与者发送的ACK响应,会向所有参与者发送abort请求命令,执行事务的中断。
2、3PC目前状况:
- 三段提交(3PC)是对两段提交(2PC)的一种升级优化,
- 3PC在2PC的第一阶段和第二阶段中插入一个准备阶段。保证了在最后提交阶段之前,各参与者节点的状态都一致。
- 同时在协调者和参与者中都引入超时机制,当参与者各种原因未收到协调者的commit请求后,会对本地事务进行commit,不会一直阻塞等待,解决了2PC的单点故障问题。
- 但3PC还是没能从根本上解决数据一致性的问题,主要原因:网络中断,自动提交
3. 优点和缺点:
优点
- 降低延迟:3PC将两阶段提交协议中的第二阶段提交操作分为两个阶段,即准备阶段和预提交阶段,避免了第二阶段提交时的长时间阻塞,降低了事务提交的延迟。
- 提高并发性能:3PC在准备阶段和预提交阶段,通过只加锁部分资源,减少了资源占用,提高了系统的并发性能。
- 容错性强:3PC在事务提交过程中,如果发生错误,可以回滚到事务的初始状态,保证系统的数据一致性。
缺点:
- 实现复杂:3PC协议的实现相对于两阶段提交协议来说更加复杂,需要额外的逻辑和计算开销。
- 事务并发量限制:3PC协议对事务的并发量有一定限制,如果系统中有大量并发的事务,可能会导致资源占用过量,降低系统的性能。
- 单点问题:3PC协议中的协调者是重要的组件,如果协调者出现故障,整个系统的事务将无法进行,存在单点问题
4. 应用案例
- 订单管理模块生成订单,并将订单状态更新到数据库中。
- 支付管理模块处理用户的支付操作,并将支付结果更新到数据库中。
- 订单管理模块向协调者发送准备提交请求。
- 支付管理模块向协调者发送准备提交请求。
- 协调者根据所有参与者的准备情况,判断是否可以进行预提交操作。
- 如果所有参与者都准备成功,则进行预提交操作,向所有参与者发送预提交请求。
- 如果某个参与者准备失败,则进行回滚操作,向所有参与者发送回滚请求。
- 所有参与者接收到预提交请求后,进行提交操作,更新本地数据库。
- 如果某个参与者提交失败,则进行回滚操作,向协调者发送回滚请求。
- 协调者接收到所有参与者的提交或回滚请求后,进行最终提交或回滚操作,更新全局数据库。
五、柔性事务(BASE)— TCC模式
TCC与XA协议不同,不是数据库原生实现
1. 概述
TCC(Try-Confirm-Cancel)分布式事务模型通过对业务逻辑进行分解来实现分布式事务。顾名思义,TCC事务模型需要业务系统提供以下三种业务逻辑。
- Try:完成业务检查,预留业务所需的资源。Try操作是整个TCC的精髓,可以灵活选择业务资源锁的粒度。
- Confirm:执行业务逻辑,直接使用Try阶段预留的业务资源,无须再次进行业务检查。
- Cancel:释放Try阶段预留的业务资源。
TCC模型仅提供两阶段原子提交协议,保证分布式事务的原子性。事务的隔离交给业务逻辑来实现。TCC 模型的隔离性思想是,通过对业务的改造将对数据库资源层面加锁上移至对业务层面加锁,从而释放底层数据库锁资源,拓宽分布式事务锁协议,提高系统的并发性。
2. 案例
以A账户向B账户汇款100元为例。汇款服务和收款服务需要分别实现Try、Confirm、Cancel这三个接口,并在业务初始化阶段将这三个接口的实现注入TCC事务管理器。
- 汇款服务
— Try:检查A账户的有效性;检查A账户的余额是否充足;从A账户中扣减100元,并将状态置为“转账中”;预留扣减资源,将“从A账户向B账户转账100元”这个事件存入消息或日志。
— Confirm:不做任何操作。
— Cancel:A账户增加100元;从日志或消息中释放扣减资源。 - 收款服务
—Try:检查B账户的有效性。
— Confirm:读取日志或者消息,B账户增加100元;从日志或消息中释放扣减资源。
— Cancel:不做任何操作。
由此可以看出,TCC模型对业务的侵入性较强,改造的难度较大。
如图:
3. 优缺点:
优点:
- 解决跨服务的业务操作原子性问题,例如组合支付,订单减库存等场景非常实用。
- TCC的本质原理是把数据库的二阶段提交上升到微服务来实现,从而避免了数据库2阶段中锁冲突的长事务低性能风险。
- TCC异步高性能,它采用了try先检查,然后异步实现confirm,真正提交的是在confirm方法中。
缺点:
- 对微服务的侵入性强,微服务的每个事务都必须实现try,confirm,cancel等3个方法,开发成本高,今后维护改造的成本也高。
- 为了达到事务的一致性要求,try,confirm、cancel接口必须实现等幂性操作(定时器+重试)。
- 由于事务管理器要记录事务日志,必定会损耗一定的性能,并使得整个TCC事务时间拉长,建议采用redis的方式来记录事务日志。
六、最大努力通知型
1. 概述
最大努力通知型(Maximally Effort Notification,MEN)是一种分布式事务的实现方式,它通过在事务涉及的各个业务系统之间传递消息来实现事务的协调和提交。MEN的主要思想是在事务提交之前,尽力将所有消息发送给接收方,但是不保证接收方是否能够成功处理这些消息。
2. MEN的实现方式通常包括以下步骤:
- 在事务开始时,业务系统A向消息队列发送一条消息,通知其他业务系统将要发生的事务。
- 业务系统B收到消息后,尝试完成事务操作,并将操作结果发送回消息队列。
- 业务系统A收到B的操作结果后,根据结果决定是否重试或提交事务。
3. 应用案例
场景:一个电商网站在进行订单支付时,需要完成以下操作:
- 在订单系统中创建订单。
- 在支付系统中进行支付操作。
- 在物流系统中更新库存。
实现方案:
- 订单系统创建一个订单并向消息队列发送一条消息,通知其他业务系统将要发生的事务。
- 支付系统收到消息后,尝试完成支付操作,并将操作结果发送回消息队列。
- 物流系统收到消息后,尝试更新库存,并将操作结果发送回消息队列。
- 订单系统收到所有业务系统的操作结果后,根据结果决定是否重试或提交事务。
4. 优缺点
MEN的优点包括:
- 低侵入性:MEN的实现方式对业务系统的侵入性较低,不需要修改业务系统的代码。
- 高可用性:MEN的消息传输机制通常采用至少送达一次的方式,确保消息能够被接收方收到。
- 弹性扩展:MEN的消息传输机制通常支持多个消息消费者,能够轻松扩展到更多的业务系统。
缺点包括:
- 数据不一致:由于MEN不能保证消息被接收方成功处理,因此可能会导致数据不一致的问题。
- 重复消息:由于MEN不能保证消息的唯一性,可能会发送重复的消息,导致接收方处理错误。
- 系统复杂性:MEN的实现方式需要依赖消息队列和其他中间件,增加了系统的复杂性。
七、可靠消息+最终一致性
1. 概述
消息一致性方案是通过消息中间件保证上下游应用数据操作一致性的。
基本思路是,将本地操作和发送消息放在同一个本地事务中,下游应用从消息系统订阅该消息,收到消息后执行相应的操作,本质上是依靠消息的重试机制达到最终一致性的
主要包括以下两个步骤:
- 本地事务执行成功后,向消息中间件发送一条消息,通知其他业务系统将要发生的事务。
- 业务系统接收到消息后,异步执行事务操作,并在操作完成后向消息中间件发送一条结果消息,以确保最终一致性。
2. 应用案例
场景:一个电商网站在进行订单支付时,需要完成以下操作:
- 在订单系统中创建订单。
- 在支付系统中进行支付操作。
- 在物流系统中更新库存。
实现方案:
- 订单系统创建一个订单,并向消息队列发送一条消息,通知其他业务系统将要发生的事务。
- 支付系统和物流系统各自订阅订单创建消息,异步执行事务操作。
- 支付系统执行支付操作后,向消息队列发送一条支付成功消息。
- 物流系统执行库存更新操作后,向消息队列发送一条更新成功消息。
- 订单系统收到所有业务系统的操作结果后,根据结果决定是否重试或提交事务。
3. 优缺点:
优点包括:
- 低侵入性:可靠消息+最终一致性的实现方式对业务系统的侵入性较低,不需要修改业务系统的代码。
- 高可用性:消息中间件通常采用至少送达一次的方式,确保消息能够被接收方收到。
- 弹性扩展:消息中间件通常支持多个消息消费者,能够轻松扩展到更多的业务系统。
缺点包括:
- 数据不一致:由于可靠消息+最终一致性不能保证消息被接收方成功处理,因此可能会导致数据不一致的问题。
- 重复消息:由于可靠消息+最终一致性不能保证消息的唯一性,可能会发送重复的消息,导致接收方处理错误。
- 系统复杂性:可靠消息+最终一致性的实现方式需要依赖消息中间件和其他中间件,增加了系统的复杂性。