分布式事务-seata

一、实际业务场景。

场景:

        一个在线购物系统,该系统包括【订单服务】和【库存服务】两个独立的微服务。当用户下单时,需要确保订单创建成功后相应的库存会被锁定,以避免库存不足的情况。

方案: 

        在这个场景下,可以使用分布式事务来确保订单创建和库存锁定操作要么同时成功,要么同时失败。

二、什么是事务?

              事务是指作为单个逻辑工作单元执行的一系列操作,这些操作要么全部成功完成,要么全部失败回滚。

三、事务最基本的四个特征是什么?

3.1 原子性(Atomicity)

        事务是一个不可分割的工作单位,要么全部执行成功,要么全部执行失败回滚。如果事务中的任何一个操作失败,整个事务都会被回滚到最初状态,保持数据的一致性。

3.2 一致性(Consistency)

        事务在执行前后,数据库的完整性约束没有被破坏。即使事务执行失败,数据库也会保持一致性状态

3.3 隔离性(Isolation)

        多个事务并发执行时,每个事务的操作应互不干扰,即一个事务的中间结果对其他事务不可见,直到事务提交。隔离级别可以根据需求设置,以保证数据的一致性和并发性能.

3.4 持久性(Durability)

一旦事务提交,其对数据库的修改就会永久保存,即使系统发生故障或重启,修改的数据也不会丢失。

四、什么是分布式事务?

        分布式事务是指涉及多个独立服务或系统的事务操作,这些服务或系统可能位于不同的物理位置,通过网络进行通信。在分布式系统中,需要确保不同服务之间的事务操作要么全部成功,要么全部失败,以保持数据的一致性。

五、实现分布式事务主要的思路有哪些?

5.1 两阶段提交

              两阶段提交是一种经典的分布式事务协议,包括准备阶段和提交阶段。在准备阶段,协调者向所有参与者发送准备请求,询问它们是否可以提交事务;在提交阶段,如果所有参与者都同意提交,则协调者发出提交请求,所有参与者执行提交操作。如果有任何一个参与者拒绝提交,或者在提交阶段发生故障,那么事务会被中止。

5.2 补偿事务

      补偿事务是一种基于事务的回滚和补偿机制来处理分布式环境下的异常情况。即在事务执行过程中,如果某个步骤失败,系统会执行相应的补偿操作来撤销之前的操作,以保持数据的一致性。这种方式相对灵活,但需要谨慎设计补偿逻辑,以确保系统状态能够正确回滚和修复。

5.3 基于消息队列的事务消息

       使用消息队列作为事务消息的中间件,实现分布式事务的一致性。当一个服务需要跨多个系统执行事务时,将事务操作封装成消息发布到消息队列中,各个参与者服务接收消息并执行相应操作。通过消息队列的确认机制,可以保证事务的原子性和一致性。

六、一款不错的产品:Stata。

 官网:Apache Seata

       Seata(原名Fescar)是阿里巴巴开源的一套分布式事务解决方案,旨在解决微服务架构下的分布式事务问题。Seata提供了分布式事务的最终一致性解决方案,支持对分布式事务的开启、提交和回滚等操作,保证数据的一致性和完整性。

6.1 各事务模式

        (1)AT 模式。

        Seata AT模式是Seata分布式事务管理器的一种模式,它致力于提供一种自动化的分布式事务解决方案,使得原本在本地事务中使用的编程模型能够无缝地应用于分布式场景。

        (2)TCC 模式。

         Seata TCC(Try-Confirm-Cancel)模式是Seata分布式事务管理器的另一种模式,它是一种补偿性事务模式,通过在业务代码中实现Try、Confirm和Cancel三个操作的接口,来管理分布式事务。

        (3)Saga 模式。

        Seata Saga模式是一种长事务的分布式事务解决方案,它通过将长事务拆分为多个短事务(子事务),并确保每个子事务都能够成功提交或者能够在失败时被补偿,以此来保证整个长事务的最终一致性。

        (4)XA 模式。

        XA是一个分布式事务的规范协议,由X/Open组织定义,它定义了全局事务协调者和局部资源管理器之间的接口协议。
        在Seata XA模式下,分布式事务由一个全局事务协调者(Transaction Coordinator,TC)和多个局部资源管理器(Resource Manager,RM)组成。每个RM通常对应一个数据库实例,它负责管理和协调本地事务。TC负责协调各个RM上的事务,确保它们要么全部提交,要么全部回滚,从而实现分布式系统的全局一致性。

6.2 AT模式(常用)

6.2.1 原理机制:两阶段提交。

(1)第一阶段(准备阶段)

        a.业务请求:客户端发起一个分布式事务的业务请求。
        b.资源注册:参与该分布式事务的各个服务(称为分支事务)将自己的本地事务资源(通常是数据库)注册到全局事务管理器(Transaction Coordinator,TC)。
        c.执行业务SQL:每个分支事务在自己的数据库中执行业务SQL操作,并将undo log(回滚日志)和业务数据在同一个本地事务中提交。这样,即使后续全局事务需要回滚,也可以使用undo log来撤销之前的操作。
        d.报告状态:分支事务将执行结果报告给TC。

(2)第二阶段(提交阶段)

        a.决策:TC根据所有分支事务的报告结果,决定全局事务是提交还是回滚。
        b,分支提交:如果TC决定提交,各分支事务会提交各自的事务,并删除相应的undo log。
        c.分支回滚:如果TC决定回滚,各分支事务根据undo log执行回滚操作,撤销之前的业务数据变更。

6.2.2 写隔离(官网案例,写的很好)

  • 一阶段本地事务提交前,需要确保先拿到 全局锁 。
  • 拿不到 全局锁 ,不能提交本地事务。
  • 拿 全局锁 的尝试被限制在一定范围内,超出范围将放弃,并回滚本地事务,释放本地锁。

举例:

两个全局事务 tx1 和 tx2,分别对 a 表的 m 字段进行更新操作,m 的初始值 1000。

tx1 先开始,开启本地事务,拿到本地锁,更新操作 m = 1000 - 100 = 900。本地事务提交前,先拿到该记录的 全局锁 ,本地提交释放本地锁。 tx2 后开始,开启本地事务,拿到本地锁,更新操作 m = 900 - 100 = 800。本地事务提交前,尝试拿该记录的 全局锁 ,tx1 全局提交前,该记录的全局锁被 tx1 持有,tx2 需要重试等待 全局锁 。

如果 tx1 的二阶段全局回滚,则 tx1 需要重新获取该数据的本地锁,进行反向补偿的更新操作,实现分支的回滚。

此时,如果 tx2 仍在等待该数据的 全局锁,同时持有本地锁,则 tx1 的分支回滚会失败。分支的回滚会一直重试,直到 tx2 的 全局锁 等锁超时,放弃 全局锁 并回滚本地事务释放本地锁,tx1 的分支回滚最终成功。

因为整个过程 全局锁 在 tx1 结束前一直是被 tx1 持有的,所以不会发生 脏写 的问题。

6.2.3 日志表 UNDO_LOG

以 MySQL 为例:

FieldType
branch_idbigint PK
xidvarchar(100)
contextvarchar(128)
rollback_infolongblob
log_statustinyint
log_createddatetime
log_modifieddatetime
-- 注意此处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;

6.3 TCC 模式

一个分布式的全局事务,整体是 两阶段提交 的模型。全局事务是由若干分支事务组成的,分支事务要满足 两阶段提交 的模型要求,即需要每个分支事务都具备自己的:

  • 一阶段 prepare 行为
  • 二阶段 commit 或 rollback 行为

根据两阶段行为模式的不同,我们将分支事务划分为 Automatic (Branch) Transaction Mode 和 Manual (Branch) Transaction Mode.

AT 模式基于 支持本地 ACID 事务 的 关系型数据库

  • 一阶段 prepare 行为:在本地事务中,一并提交业务数据更新和相应回滚日志记录。
  • 二阶段 commit 行为:马上成功结束,自动 异步批量清理回滚日志。
  • 二阶段 rollback 行为:通过回滚日志,自动 生成补偿操作,完成数据回滚。

相应的,TCC 模式,不依赖于底层数据资源的事务支持:

  • 一阶段 prepare 行为:调用 自定义 的 prepare 逻辑。
  • 二阶段 commit 行为:调用 自定义 的 commit 逻辑。
  • 二阶段 rollback 行为:调用 自定义 的 rollback 逻辑。

所谓 TCC 模式,是指支持把 自定义 的分支事务纳入到全局事务的管理中。

6.4 Saga 模式

        Saga模式是SEATA提供的长事务解决方案,在Saga模式中,业务流程中每个参与者都提交本地事务,当出现某一个参与者失败则补偿前面已经成功的参与者,一阶段正向服务和二阶段补偿服务都由业务开发实现。

  • 业务流程长、业务流程多
  • 参与者包含其它公司或遗留系统服务,无法提供 TCC 模式要求的三个接口

优势:

  • 一阶段提交本地事务,无锁,高性能
  • 事件驱动架构,参与者可异步执行,高吞吐
  • 补偿服务易于实现。

七、 SEATA 的分布式交易解决方案

7.1 AT模式

我们只需要使用一个 @GlobalTransactional 注解在业务方法上:


    @GlobalTransactional
    public void purchase(String userId, String commodityCode, int orderCount) {
        // 订单服务落库
        // 库存服务落库
        // 帐户服务落库
    }

如果您曾使用过 Spring 框架 @Transactional 注解的话,也可以根据命名类比理解 @GlobalTransactional 的功能。是的,这里只是引入了一个注解就轻松实现了分布式事务能力,使用 AT 模式可以最小程度减少业务改造成本。

详细步骤参考:快速启动 | Apache Seata

7.2 TCC模式

区别于在 AT 模式直接使用数据源代理来屏蔽分布式事务细节,业务方需要自行定义 TCC 资源的“准备”、“提交”和“回滚” 。

public interface TccActionOne {
    @TwoPhaseBusinessAction(name = "DubboTccActionOne", commitMethod = "commit", rollbackMethod = "rollback")
    public boolean prepare(BusinessActionContext actionContext, @BusinessActionContextParameter(paramName = "a") String a);
    public boolean commit(BusinessActionContext actionContext);
    public boolean rollback(BusinessActionContext actionContext);
}

Seata 会把一个 TCC 接口当成一个 Resource,也叫 TCC Resource。在业务接口中核心的注解是 @TwoPhaseBusinessAction,表示当前方法使用 TCC 模式管理事务提交,并标明了 Try,Confirm,Cancel 三个阶段。name属性,给当前事务注册了一个全局唯一的的 TCC bean name。同时 TCC 模式的三个执行阶段分别是:

  • Try 阶段,预定操作资源(Prepare) 这一阶段所以执行的方法便是被 @TwoPhaseBusinessAction 所修饰的方法。如示例代码中的 prepare 方法。
  • Confirm 阶段,执行主要业务逻辑(Commit) 这一阶段使用 commitMethod 属性所指向的方法,来执行Confirm 的工作。
  • Cancel 阶段,事务回滚(Rollback) 这一阶段使用 rollbackMethod 属性所指向的方法,来执行 Cancel 的工作。

详细步骤参考:Seata TCC 模式 | Apache Seata

7.3 Saga 模式

Seata Saga 模式 | Apache Seata详细步骤参考:Seata Saga 模式 | Apache Seata

7.4 XA 模式

XA 模式使用起来与 AT 模式基本一致,用法上的唯一区别在于数据源代理的替换:使用 DataSourceProxyXA 来替代 DataSourceProxy

public class DataSourceProxy {
    @Bean("dataSourceProxy")
    public DataSource dataSource(DruidDataSource druidDataSource) {
        // DataSourceProxyXA for XA mode
        return new DataSourceProxyXA(druidDataSource);
        // DataSourceProxy for AT mode
        // return new DataSourceProxy(druidDataSource);
    }
}
欢迎订阅关注公众号(JAVA和人工智能)

  • 29
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JAVA和人工智能

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值