2.3 动手实践

一、XA模式(强一致模式)——补充用法

  1. XA规范是X/Open组织定义的分布式事务处理(DTP,Distributed Transaction Processing)标准,XA规范描述了全局的TM与局部的RM之间的接口,几乎所有主流的数据库都对XA规范提供了支持

  2. Seata的XA模式做了一些调整,工作阶段

    1. RM一阶段工作:

      1. 注册分支事务到TC

      2. 执行分支业务SQL,但不提交

      3. 报告执行状态到TC

    2. TC二阶段工作:

      1. Tc检测各分支执行状态

        如果都成功,通知所有RM提交事务

        如果有失败,通知所有RM回滚事务

    3. RM二阶段工作:

      1. 接收TC指令,提交或回滚事务

  3. 优缺点

    1. 优点:

      1. 事物的强一致性,满足ACID原则

      2. 常用数据库都支持,实现简单,并没有代码侵入

    2. 缺点:

      1. 因为一阶段需要锁定数据库资源,等待二阶段结束才释放,性能较差

      2. 依赖关系型数据库实现事务

  4. 代码实现:

    1. 修改application.yml文件(每一个参与事务的微服务),开启XA模式

      seata:
        data-source-proxy-mode: XA # 开启数据源代理的XA模式
    2. 给发起全局事务的入口方法添加@GlobalTransactional注解

      // Service实现类的某个方法,必须是入口
      @Override
      @GlobalTransactional
      public Long create(Order order) {
          // 检查库存    调用库存服务的检查方法
          // 创建订单    本服务创建订单
          // 扣除账户余额    扣除用户服务的余额
          // 扣除库存    库存服务扣除库存
      }

      备注:举个简单的例子

    3. 重启服务

二、AT模式(最终一致模式)——比较多

  1. AT模式同样是分阶段提交的事务模型,不过弥补了XA模型中资源锁定周期过长的缺陷

  2. 工作阶段

    1. 阶段一RM的工作

      1. 注册分支事务

      2. 记录undo-log(快照数据)

      3. 执行业务SQL并提交

      4. 报告事务状态

    2. 阶段二提交时RM的工作

      1. 删除undo-log即可

    3. 阶段二回滚时RM的工作

      1. 根据undo-log恢复数据到更新前

  3. 多进程模式时候,会出现脏写模式

    1. 解决办法:全局锁,由TC记录当前正在操作某行数据的事务,改事务持有全局锁,具备执行权

  4. 优缺点

    1. 优点

      1. 一阶段完成直接提交事务,释放数据库资源,性能较好

      2. 利用全局锁实现读写隔离

      3. 没有代码入侵,框架自动完成回滚和提交

    2. 缺点

      1. 两阶段之间属于软状态,属于最终一致

      2. 框架的快照功能会影响性能,但比XA模式要好很多

  5. 代码实现

    1. 创建AT模式对应的数据库

      // TC服务对应的库表需要添加新表
      DROP TABLE IF EXISTS `lock_table`;
      CREATE TABLE `lock_table`  (
        `row_key` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
        `xid` varchar(96) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
        `transaction_id` bigint(20) NULL DEFAULT NULL,
        `branch_id` bigint(20) NOT NULL,
        `resource_id` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
        `table_name` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
        `pk` varchar(36) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
        `gmt_create` datetime NULL DEFAULT NULL,
        `gmt_modified` datetime NULL DEFAULT NULL,
        PRIMARY KEY (`row_key`) USING BTREE,
        INDEX `idx_branch_id`(`branch_id`) USING BTREE
      ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
      
      // 微服务有关的数据库中需要添加快照表
      DROP TABLE IF EXISTS `undo_log`;
      CREATE TABLE `undo_log`  (
        `branch_id` bigint(20) NOT NULL COMMENT 'branch transaction id',
        `xid` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT 'global transaction id',
        `context` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT 'undo_log context,such as serialization',
        `rollback_info` longblob NOT NULL COMMENT 'rollback info',
        `log_status` int(11) NOT NULL COMMENT '0:normal status,1:defense status',
        `log_created` datetime(6) NOT NULL COMMENT 'create datetime',
        `log_modified` datetime(6) NOT NULL COMMENT 'modify datetime',
        UNIQUE INDEX `ux_undo_log`(`xid`, `branch_id`) USING BTREE
      ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = 'AT全局事务快照表' ROW_FORMAT = Compact;
    2. 修改application.yml文件,将事务模式修改为AT模式即可:

      seata:
        data-source-proxy-mode: AT # 开启数据源代理的AT模式
    3. 给发起全局事务的入口方法添加@GlobalTransactional注解

      // Service实现类的某个方法,必须是入口
      @Override
      @GlobalTransactional
      public Long create(Order order) {
          // 检查库存    调用库存服务的检查方法
          // 创建订单    本服务创建订单
          // 扣除账户余额    扣除用户服务的余额
          // 扣除库存    库存服务扣除库存
      }

      备注:举个简单的例子

    4. 重启服务即可

三、TCC模式——补充用法

  1. TCC模式与AT模式非常相似,每个阶段都是独立事务,不同的是TCC通过人工编码来实现数据恢复。需要实现三个方法:

    1. Try:资源的检测和预留

    2. Confirm:完成资源操作业务了要求Try成功Confirm一定要能成功

    3. Cancel:预留资源释放,可以理解为Try的反向操作

  2. 优缺点

    1. 优点

      1. 一阶段完成直接提交事务,释放数据库资源,性能好

      2. 相比AT模式,无需生成快照,无需使用全局锁,性能最强

      3. 不依赖数据库事务,而是依赖补偿操作,可用于非事务型数据库

    2. 缺点

      1. 有代码入侵,需要人为编写Try、Confirm和Cancel,太麻烦

      2. 软状态,事务是最终一致

      3. 需要考虑Confirm和Cancel的失败情况,做好幂等处理

  3. 代码实现

    1. 需要数据库支持,比如金额扣除操作

      1. try:添加冻结金额,扣减可用金额(字段修改)

      2. confirm:删除冻结金额

      3. cancel:删除冻结金额,恢复可用金额

      4. 保证confirm、cancel接口的幂等性

      5. 允许空回滚

        当某分支事务的try阶段阻塞时,可能导致全局事务超时而触发二阶段的cancel操作。在未try操作时先执行了cancel操作,这时cancel不能做回滚,就是空回滚

      6. 拒绝业务悬挂

        对于已经空回滚的业务,如果以后继续执行try,就永远不可能confirm或cancel,这就是业务悬挂。应当阻止执行空回滚后的try操作,避免悬挂

    2. 声明TCC接口

      1. TCC的Try、Confirm和Cancel方法都需要在接口中基于注解声明,语法:(这个接口文件是自己编写的接口文件)

        @LocalTCC
        pubblic interfact TCCService {
            
            /**
             * Try逻辑,需要有@TwoPhaseBusinessAction注解指定,name值与当前方法名一致,指定Try逻辑
             */
            @TwoPhaseBusinessAction(name = "prepare", commitMethod = "confirm", rollbackMethod = "cancel")
            void prepare(@BusinessActionContextParameter(paramName = "param") String param);
            
            /**
             * 二阶段confirm确认方法,可以重命名,但要与上面的注解commitMethod值一致
             */
            boolean confirm(BusinessActionContext context);
            
            /**
             * 二阶段回滚方法,要保证方法名与rollbackMethod值一致
             */
            boolean cancel(BusinessActionContext context);
            
        }

        备注:@BusinessActionContextParameter注解可以指定参数,可以被上下文接管,TCC全局可用,需要几个参数,就要加几个注解

      2. 真实业务逻辑实现上面的TCCService接口文件,并在对应的方法中编写对应的业务逻辑即可

        1. BusinessActionContext中获取Try中的上下文数据:context.getActionContext("param名").toString();

          这里的param名就是上面Try逻辑中存的名字

四、Saga模式——业务跨度比较大

  1. Saga模式是Seata提供的长事务解决方案。也分为两个阶段

    1. 一阶段:直接提交本地事务

    2. 二阶段:成功则什么都不做;失败则通过编写补偿业务来回滚

  2. 优缺点

    1. 优点:

      1. 事务参与者可以基于事件驱动实现异步调用,吞吐高

      2. 一阶段直接提交事务,无锁,性能好

      3. 不用编写TCC中的三个阶段,实现简单

    2. 缺点:

      1. 软状态持续时间不确定,时效性差

      2. 没有锁,没有事务隔离,会有脏写

五、自种模式对比

XA
AT
TCC
SAGA

一致性

强一致

弱一致

弱一致

弱一致

隔离性

完全隔离

基于全局锁隔离

基于资源预留隔离

无隔离

代码侵入

有,要编写三个接口

有,要编写状态机和补偿业务

性能

非常好

非常好

场景

对一致性、隔离性有高要求的业务

基于关系型数据库的大多数分布式事务场景都可以

1. 对性能要求较高的事务 2. 有关系型数据库要参与的事务

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

最后更新于

这有帮助吗?