最终一致性分布式事务概述
强一致性分布式事务解决方案要求参与事务的哥哥节点的数据时刻保持一致,查询任意节点的数据都能得到最新的数据结果。这就导致在分布式场景,尤其是高并发场景下,系统的性能受到影响。而最终一致性分布式事务解决方案并不要求参与事务的各节点数据时刻保持一致,允许其存在中间状态,只要一段时间后,能够达到数据的最终一致状态即可。
为了解决分布式、高并发场景下系统的性能问题,业界基于 Base 理论提出了最终一致性分布式事务解决方案,典型的最终一致性解决方案如下:
- TCC 解决方案
- 可靠消息最终一致性解决方案
- 最大努力通知型解决方案
TCC 解决方案
TCC 解决方案适用于具有强隔离性、严格一致性要求的业务场景,也适用于执行时间比较短的业务。
以电商场景为例,如果使用 TCC 分布式事务,则会经过 Try
、Confirm
、Cancel
三个阶段。
1、Try 阶段
提交订单并将订单的状态设置为待提交,调用库存服务预扣减库存,具体操作为在库存数据表中将商品库存字段的数据减去提交订单时传递的商品数量,同时在预扣减库存字段中增加提交订单时传递的商品数量。
2、Confirm 阶段
如果 Try 阶段的操作全部执行成功,则执行 Confirm 阶段。在 Confirm 阶段,订单服务将订单数据的状态更新为已提交。库存服务则将库存数据表中预扣减库存字段的数量减去提交订单时传递的商品数量,实现真正扣减库存。
3、Cancel 阶段
如果 Try 阶段执行失败或者抛出异常,则执行 Cancel 阶段。在 Cancel 阶段,订单服务将订单数据的状态更新为已取消。库存服务将库存数据表中商品库存字段的数据增加提交订单时传递的商品数量,同时对预扣减库存字段的数据减去提交订单时传递的商品数量,实现事务回滚。
TCC 方案的优缺点
TCC 分布式事务的优点如下:
- 在应用层实现具体逻辑,锁定资源的粒度变小,不会锁定所有资源,提升了系统的性能
- Confirm 阶段和 Cancel 阶段的方法具备幂等性,能够保证分布式事务执行完毕后数据的一致性
- TCC 分布式事务解决方案由主业务发起整个事务,无论是主业务还是分支事务所在的业务,都能部署为集群模式,从而解决了 XA 规范的单点故障问题。
TCC 方案的缺点是代码需要耦合到具体业务中,每个参与分布式事务的业务方都要拆分成 Try
、Confirm
和 Cancel
三个阶段的方法,提高了开发成本。
TCC 方案需要主要的问题
使用 TCC 方案解决分布式事务问题时,需要注意空回滚
、幂等
和悬挂
的问题。
空回滚问题
(1)空回滚问题出现的原因
出现空回滚的原因是一个分支事务所在的服务器宕机或者网络发生异常,此分支事务调用失败,此时并未执行此分支事务 Try 阶段的方法。当服务器或者网络恢复后,TCC 分布式事务执行回滚操作,会调用分支事务 Cancel 阶段的方法,如果 Cancel 阶段的方法不能处理这种情况,就会出现空回滚问题。
(2)空回滚问题的解决方案
识别是否出现了空回滚操作的方法是判断是否执行了 Try 阶段的方法。如果执行了 Try 阶段的方法,就没有空回滚,否则就出现了空回滚。
具体解决方案是在主业务发起全局事务时,生成全局事务记录,并为全局事务记录生成一个全局唯一的ID,叫作全局事务ID。这个全局事务ID会贯穿整个分布式事务的执行流程。再创建一张分支事务记录表,用于记录分支事务,将全局事务ID和分支事务ID保存到分支事务表中。执行 Try 阶段的方法时,会向分支事务记录表中插入一条记录,其中包含全局事务ID和分支事务ID,表示执行了 Try 阶段。当事务回滚执行 Cancel 阶段的方法时,首先读取分支事务表中的数据,如果存在 Try 阶段插入的数据,则执行正常操作回滚事务,否则为空回滚,不做任何操作。
幂等问题
(1)幂等问题出现的原因
由于服务器宕机、应用崩溃或者网络异常等原因,可能会出现方法调用超时的情况,为了保证方法的正常执行,往往会在 TCC 方案中加入超时重试机制。因为超时重试有可能导致数据不一致的问题,所以需要保证分支事务的执行以及 TCC 方案的 Confirm 阶段和 Cancel 阶段具备幂等性。
(2)幂等问题的解决方案
解决方案是在分支事务记录表中增加事务的执行状态,每次执行分支事务以及 Confirm 阶段和 Cancel 阶段的方法是,都查询词事务的执行状态,以此判断事务的幂等性。
悬挂问题
(1)悬挂问题出现的原因
悬挂是指因为网络问题,RM 开始没有收到 try 指令,但是执行了 Rollback 后 RM 又收到了 try 指令并且预留资源成功,这时全局事务已经结束,最终导致预留的资源不能释放。
(2)解决悬挂问题的方案
解决方案的思路是如果执行了 Confirm 阶段或者 Cancel 阶段的方法,则 Try 阶段的方法就不能再执行。具体方案是在执行 Try 阶段的方法时,判断分支记录表中是否已经存在同一全局事务下 Confirm 阶段或者 Cancel 阶段的事务记录,如果存在,则不再执行 Try 阶段的方法。
可靠消息最终一致性解决方案
可靠消息最终一致性分布式事务解决方案指的是事务的发起方执行本地事务之后,发出一条消息,事务的参与方,也就是消息的消费者一定能够接收到这条消息并处理成功。这个方案强调的是只要事务发起方将消息发送给事务参与方,事务参与方就一定能够执行成功,事务最终达到一致的状态。
适用场景
可靠消息最终一致性方案主要适用于消息数据能够独立存储,能够降低系统之间耦合度,并且业务对数据一致性的时间敏感度高的场景。例如,基于 RocketMQ 实现的可靠消息最终一致性分布式事务解决方案。
以电商支付场景,向用户发放优惠券为例,具体流程为订单服务向 RocketMQ 发送 Half 消息,发送成功后,RocketMQ 会向订单服务响应 Half 消息发送成功的状态。接下来,订单服务执行本地事务,修改订单数据的状态,并向 RocketMQ 发送提交事务或者回滚事务的消息。如果是提交事务的消息,则 RocketMQ 会向优惠券服务投递消息,优惠券服务收到消息后,会执行为用户发放优惠券的逻辑。如果是回滚消息,则 RocketMQ 会删除相应的消息,不再向优惠券服务投递对应的事务消息。
方案的执行流程
首先,事务发起方将消息发送给可靠消息服务,这里的可靠消息服务可以基于本地数据表实现,也可以基于消息队列中间件实现。然后,事务参与方从可靠消息服务中接收消息。事务发起方和可靠消息服务之间、可靠消息服务和事务参与方之间都是通过网络进行通信的。由于网络本身的不稳定性,可能会造成分布式事务问题,因此实现上,需要引入消息确认服务和消息恢复服务。
消息确认服务会定期检测事务发起方业务的执行状态和消息库中的数据,如果发现事务发起方业务的执行状态与消息库中的数据不一致,消息确认服务就会同步事务发起方的业务数据和消息库中的数据,保证数据一致性,确保事务发起方业务完成本地事务后消息一定会发送成功。
消息恢复服务会定期检测事务参与方业务的执行状态和消息库中的数据,如果发现事务参与方业务的执行状态与消息库中的数据不一致(这里的不一致,通常指的是事务参与方消费消息后,执行本地事务操作失败,导致事务参与方本地事务的执行状态与消息库中的数据不一致),消息恢复服务就会恢复消息库中的消息,使消息的状态回滚为事务发起方发送消息成功,但未被事务参与方消费的状态。
方案的优缺点
消息最终一致性方案的可靠消息服务可以基于本地消息表和消息队列中间件两种方式实现。
1、基于本地消息表实现的最终消息一致性方案
优点:在业务应用中实现了消息的可靠性,减少了对消息中间件的依赖
缺点:
- 绑定了具体的业务场景,耦合度太高,不可公用和扩展
- 消息数据与业务数据在同一个数据库,占用了业务系统的资源
- 消息数据可能会收到数据库并发性的影响
2、基于消息队列中间件实现的最终消息一致性方案
优点:
- 消息数据能够独立存储,与具体业务数据库解耦
- 消息的并发性和吞吐量优于本地消息表方案
缺点:
- 发送一次消息需要完成两次网络交互,一次是消息的发送,另一次是消息的提交或者回滚
- 需要实现消息的回查接口,增加了开发成本
需要主要的问题
事务发送方本地事务与消息发送的原子性问题
(1)原子性问题产生的原因
可靠消息最终一致性要求事务发起方的本地事务与消息发送的操作具有原子性,也就是事务发起方执行本地事务成功后,一定要将消息发送出去,执行本地事务失败后,一定要丢弃消息。执行本地事务和发送消息,要么都成功,要么都失败。
(2)原子性问题的解决方案
可以通过消息确认服务解决本地事务与消息发送的原子性问题。
事务参与方接收消息的可靠性问题
(1)可靠性问题产生的原因
优于服务器宕机、服务崩溃或网络异常等原因,导致事务参与方不能正常接收消息,或者接收消息后处理事务的过程中发生异常,无法将结果正确回传到消息库中。此时就会产生可靠性问题。
(2)可靠性问题的解决方案
可以通过消息恢复服务保证事务参与方的可靠性。
事务参与方接收消息的幂等性问题
(1)幂等性问题产生的原因
在实际场景中,由于某种原因,可靠消息服务可能会多次向事务参与方发送消息,如果事务参与方的方法不具有幂等性,就会造成消息重复消费的问题。
(2)幂等性问题的解决方案
事务参与方实现幂等方法。
最大努力通知型解决方案
当分布式事务跨越多个不同的系统,尤其是不同企业之间的系统时,解决分布式事务问题就需要用到最大努力通知型方案。
适用场景
最大努力通知型解决方案适用于最终一致性时间敏感度低的场景,并且事务被动方的处理结果不会影响主动方的处理结果。最典型的使用场景就是支付成功后,支付平台异步通知商户支付结果。
方案的执行流程
最大努力通知型分布式事务解决方案在执行的过程中,允许丢失消息,但需要业务主动方提供事务状态查询接口,以便业务被动方主动调用并恢复丢失的业务。执行流程如下图所示。
实现最大努力通知型方案时,需要实现如下功能:
- 业务主动方在完成业务处理后,会向业务被动方发送消息通知。发送消息通知时,允许消息丢失
- 在实现上,业务主动方可以设置时间阶梯型通知规则,在消息通知失败后,可以按照规则再次通知,直至达到最大通知次数为止
- 业务主动方需要提供查询接口供业务被动方按照需要查询,用于恢复丢失的消息
方案的优缺点
最大努力通知型方案存在如下优点:
- 能够实现跨企业的数据一致性;
- 业务被动方的处理结果不会影响业务主动方的处理结果;
- 能够快速接入其他业务系统,达到业务数据一致性。
最大努力通知型方案存在如下缺点:
- 只适用于时间敏感度低的场景;
- 业务主动方发送的消息可能丢失,造成业务被动方收不到消息;
- 需要业务主动方提供查询消息的接口,业务被动方需要按照主动方的接口要求查询数据,增加了开发成本。
最大努力通知与可靠消息最终一致性的区别
1、设计不同
- 可靠消息最终一致性方案需要事务发起方一定要将消息发送成功;
- 最大努力通知型方案中,业务主动方尽最大努力将消息通知给业务被动方,但消息可能会丢失,业务被动方不一定能接收到消息。
2、业务场景不同
- 可靠消息最终一致性方案适用于时间敏感度高的场景,以异步的方式达到事务的最终一致;
- 最大努力通知型方案适用于时间敏感度低的场景,业务主动方只需要将处理结果通知出去。
3、解决的问题不同
- 可靠消息最终一致性方案解决的是消息从事务发起方发出,到事务参与方接收的一致性,并且事务参与方接收到消息后,能够正确地执行事务操作,达到事务最终一致性;
- 最大努力通知型方案虽然无法保证消息从业务主动方发出到业务被动方接收的一致性,但是能够提供消息接收的可靠性。这里的可靠性包括业务被动方能够接收到业务主动方通知的消息和业务被动方能够主动查询业务主动方提供的消息回查接口,来恢复丢失的业务。
版权属于:带翅膀的猫
本文链接:https://chengpengper.cn/archives/232/
转载时须注明出处及本声明
这篇文章不错!