分布式事务一致性解决方案学习(两段提交(2pc)、三段提交(3pc)、TCC、消息队列以及Seata四种模式)

首先分布式事务是什么呢?

分布式事务是指涉及多个独立系统或服务的操作,这些操作需要保持数据的一致性和完整性,什么意思呢?下面看一个非常常见经典的问题

假设目前我们有订单服务和库存服务,在用户下单之后会生成一个订单,然后减去库存等一系列动作,为了节省时间,这里就拿生成订单和减去库存来说了
下面分别是订单服务和商品服务两个独立的微服务(不在同一个本地事务,就会存在不一致的问题)
在这里插入图片描述
没有异常的时候是在订单表生成一个订单,在商品库存表去做减库存的操作。

那假设订单创建好了,减库存失败了,如果是在本地事务(一个服务里面的同一个数据库)的情况下是可以直接回滚的,但是我们跨服务了,这就会导致数据不一致,现在我们要做的就是分布式事务一致性的解决方案。

首先需要了解分布式中的经典理论CAP和BASE理论

CAP 理论指出,在分布式系统中,无法同时满足一致性(Consistency)、可用性(Availability)和分区容错性(Partition tolerance)这三个特性。根据 CAP 理论,分布式系统在出现网络分区时必须在一致性和可用性之间做出选择。

一致性(Consistency) 指的是多个副本之间的数据保持一致,即所有用户都能看到相同的数据状态。
可用性(Availability) 指的是系统能够提供服务并响应用户请求,即系统始终处于可用状态。
分区容错性(Partition tolerance) 指的是系统在面对网络分区(节点之间无法互相通信)时仍然能够正常运行。
根据 CAP 理论,我们只能在 CP(一致性和分区容错性) 或 AP(可用性和分区容错性) 中做出权衡选择,并没有同时满足 C、A、P 的分布式系统。

BASE 理论是对传统 ACID(原子性、一致性、隔离性和持久性)事务模型的一个补充。它通过牺牲强一致性来提高系统的可用性和性能。

基本可用性(Basically Available) 指的是系统能够提供基本功能的可用性,即在面对故障或异常情况下仍然能够保证基本服务的可用性。
软状态(Soft State) 指的是系统中的数据状态不需要实时保持一致,允许存在中间状态或部分失效的状态。
最终一致性(Eventually Consistent) 指的是系统在经过一段时间后,所有副本之间最终会达到一致的状态。
BASE 理论认为,在大规模分布式系统中,强一致性往往是难以实现的,而通过弱化一致性可以增加系统的可用性和性能。

首先了解一下相关概念:
事务协调者(全局事务协调者) :这里就是指的一个统筹在整个分布式系统的一个事务协调者,用于管理全局事务创建、回滚和提交
本地事务(分支事务) :指的是本地的事务在传统的2pc中指的就是数据库层的事务。

常见的分布式事务一致性解决方案有以下几种(都是到达最终一致性):

1、两段提交(2pc):
  • 含义: 两段提交分为:准备阶段和提交阶段,准备阶段就做业务执行和执行事务操作但是不提交,完成之后告诉全局事务协调者准备完成,在事务协调者收到了所有事务的参与者的ack确认消息时会通知所有参与者进行提交事务,如果不是全部ack确认消息则通知回滚操作。
  • 下单流程:用户下单 → 订单服务创建全局事务 → 事务协调者 → 通知所有参与者进行准备 → 参与者准备完成通知事务协调者 → 事务协调者根据第一阶段的响应结果通知提交或者回滚。
  • 优点: 通俗易懂实现简单
  • 缺点: 存在单点故障导致阻塞问题、导致事务不一致等问题
    • 单点故障:如果协调者发生故障,整个协议可能无法继续执行。这会导致阻塞和数据不一致等问题。
    • 阻塞问题:由于2pc下只有协调者有超时机制,如果再等协调者的时候协调者挂了,那所有的参与者都会阻塞并且是拿了锁的情况下。
    • 网络分区问题导致事务不一致:如果值发送了一部分的提交通知就挂了那么就会导致一些事务提交了,一些没有提交导致阻塞等问题。
2、三段提交(3pc):
  • 含义: 三段提交是两段提交的一种优化,他分为准备阶段、预提交阶段和提交阶段,相比二段提交多了一个预提交的阶段,这样能在提交的时候确定好各个参与者的一个状态(其实我感觉和两段差不多)并且在参与者方也引入了超时机制,这样就避免了协调者挂掉之后的长时间阻塞问题(超时时间内会阻塞)和部分情况的单点问题。
  • 优点: 在2pc的基础上在参与者方引入了超时机制,这样可以大概率避免在协调者挂了的时候的一些阻塞和单点问题。
  • 缺点: 1、增加了复杂性:相比于两段提交,三段提交的实现更加复杂。需要考虑各个阶段的状态转移和超时机制,增加了代码的复杂性和维护难度。2、无法解决网络分区问题:三段提交仍然无法解决网络分区问题,即当协调者和参与者之间的网络连接中断时,可能会导致无法达成一致。
3、TCC(try confirm cancel)
  • 含义: 整体分为了try 包含我们的提交事务前的所有操作,无异常则进入confirm块,否则进入cancel(我感觉整体思路和2pc差不多但具有更细粒度的控制并且但是实现方式是在代码层面人为实现。2PC 和 3PC 都是数据库层面的,而 TCC 是业务层面的分布式事务。)
  • 优点: 更加灵活,比较是在业务层实现分布式事务还是人工去写的。
  • 缺点: 1、实现复杂性:相比于2PC和其它一些分布式事务处理机制,TCC的实现相对复杂。需要开发人员对业务逻辑进行分解,并设计try、confirm和cancel阶段的操作。2、需要手动实现补偿机制: 在confirm中需要我们去手动的设计超时、重试的机制。
4、RocketMQ:
  • 含义: 通过消息队列的特性来做,例如事务消息,首先会发送一个半事务状态的消息到broker中,然后执行本地的事务,最后投递消息给消费者,消费者消费消息去执行对应的业务操作即可。
  • 优点: 在熟悉rocketmq的前提下实现比较简单。
  • 缺点: 需要去保证消息不会丢失、能正常消费并且补偿。 配置复杂性:RocketMQ的配置和参数较多,包括Broker、NameServer等的配置,需要针对具体的业务场景进行合理的调优和设置。这对于一些非专业的开发人员来说可能会增加学习和使用的难度。
5、Seata

整体机制是两阶段的,一阶段做执行和提交事务(会释放本地的锁),二阶段做补偿机制
官方说的:整体是 两阶段提交 的模型。全局事务是由若干分支事务组成的,分支事务要满足 两阶段提交 的模型要求,即需要每个分支事务都具备自己的:
一阶段 prepare 行为
二阶段 commit 或 rollback 行为

Seata中文官网

  • Seata 是一款开源的分布式事务解决方案(阿里家族出来的),致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案。目前使用比较多且成熟的一种方案。
  • AT模式(自动):
    • 前提:1、基于支持本地 ACID 事务的关系型数据库。2、Java 应用 ,通过 JDBC 访问数据库。
    • 机制:有点类似2pc,但是又有点不一样,首先也是两个阶段:
      • 第一阶段:执行本地事务,然后提交本地事务并且会记录undolog。
      • 第二阶段:根据第一阶段的反馈(成功还是失败)进行回滚(补偿)或者结束。如果是全部成功的话会删除一阶段的undolog记录。如果存在失败的分支事务则会执行根据undolog记录进行数据恢复,然后删除undolog记录。
      • 整个流程:在一阶段提交事务之前需要先拿到全局锁再去提交事务,否则是不能提交事务的。会去重试获取锁,到达一定的超时时间后会放弃进行本地事务回滚。例如:两个全局事务 tx1 和 tx2,分别对 a 表的 m 字段进行更新操作,m 的初始值 1000。tx1 先开始,开启本地事务,拿到本地锁,更新操作 m = 1000 - 100 = 900。本地事务提交前,先拿到该记录的全局锁 ,本地提交释放本地锁。 tx2 后开始,开启本地事务,拿到本地锁,更新操作 m = 900 - 100 = 800。本地事务提交前,尝试拿该记录的全局锁 ,tx1 全局提交前,该记录的全局锁被 tx1 持有,tx2 需要重试等待 全局锁
      • 总结:相比2pc、3pc来说这个锁资源的时间更短,不会造成长时间的本地事务阻塞导致其他线程无法访问,相比tcc来说,我们配置好了之后就由SeaTa自己去帮我们实现不需要我们手动去写补偿机制,他会帮我们去做。相对RocketMQ来说上手更简单,只需要引入依赖和简单配置即可,没有MQ那样繁琐的配置也不需要去保证消息的丢失等问题。
    • 官方给出的回滚日志表(undolog):
    -- 注意此处0.7.0+ 增加字段 context
    CREATE TABLE `undo_log` (
    `id` bigint(20) NOT NULL AUTO_INCREMENT,
    `branch_id` bigint(20) NOT NULL,
    `xid` varchar(100) NOT NULL,
    `context` varchar(128) NOT NULL,
    `rollback_info` longblob NOT NULL,
    `log_status` int(11) NOT NULL,
    `log_created` datetime NOT NULL,
    `log_modified` datetime NOT NULL,
    PRIMARY KEY (`id`),
    UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
    
  • TCC 模式(手动):
    • 不依赖于底层数据资源的事务支持
      一阶段 prepare 行为:调用 自定义 的 prepare 逻辑。
      二阶段 commit 行为:调用 自定义 的 commit 逻辑。
      二阶段 rollback 行为:调用 自定义 的 rollback 逻辑。
    • 所谓 TCC 模式,是指支持把 自定义 的分支事务纳入到全局事务的管理中。和前面的tcc几乎一致,也是自己去实现的。
  • Saga 模式(手动):
    • SEATA提供的长事务解决方案,在Saga模式中,业务流程中每个参与者都提交本地事务,当出现某一个参与者失败则补偿前面已经成功的参与者,一阶段正向服务和二阶段补偿服务都由业务开发实现。
    • 通过状态图来定义服务调用的流程并生成 json 状态语言定义文件
      状态图中一个节点可以是调用一个服务,节点可以配置它的补偿节点
      状态图 json 由状态机引擎驱动执行,当出现异常时状态引擎反向执行已成功节点对应的补偿节点将事务回滚
      注意: 异常发生时是否进行补偿也可由用户自定义决定

可以实现服务编排需求,支持单项选择、并发、子流程、参数转换、参数映射、服务执行状态判断、异常捕获等功能

  • XA模式(自动):我个人觉得就是基于2pc实现的一毛一样的一个seata模式前提
    • 前提:支持 XA 事务的数据库。Java 应用,通过 JDBC 访问数据库。
    • 执行阶段:可回滚:业务 SQL 操作放在 XA 分支中进行,由资源对 XA 协议的支持来保证 可回滚
      持久化:XA 分支完成后,执行 XA prepare,同样,由资源对 XA 协议的支持来保证 持久化(即,之后任何意外都不会造成无法回滚的情况)
    • 完成阶段:
      分支提交:执行 XA 分支的 commit
      分支回滚:执行 XA 分支的 rollback

我的总结就是:
在常规的基于关系型、支持 ACID 特性的数据库的 Spring Boot 或者 Cloud 微服务中,AT(Application Transaction)模式是一个较为适合的选择。它通过轻量级的本地事务来提高性能,并且不需要编写补偿机制。

TCC(Try-Confirm-Cancel)模式适用于多种不同数据协同执行的场景,特别是当存在不支持 ACID 特性的数据库时。在 TCC 模式中,需要自行实现 try、confirm 和 cancel 三个阶段的业务逻辑,并编写相应的补偿机制来保证数据的一致性。

SAGA(Saga Pattern)模式适用于需要处理长时间任务的事务。它通过将事务拆分为多个局部事务并记录补偿操作,实现了可靠的分布式事务处理。在 SAGA 模式中,同样需要编写补偿机制来保证数据的一致性。

XA(eXtended Architecture)模式在实际应用中用得较少,因为它需要使用两阶段提交(2PC)协议,以保证全局事务的一致性。XA 模式适用于需要严格的 ACID 事务特性的场景,但由于其性能和复杂性等方面的限制,通常不是首选的解决方案。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值