返回文章列表
高并发项目
分布式事务SeataXAAT模式

06分布式事务

什么是分布式事务

业务场景

下单业务中,前端请求首先进入订单服务,创建订单并写入数据库。然后订单服务调用购物车服务和库存服务:

  • 购物车服务负责清理购物车信息
  • 库存服务负责扣减商品库存

下单业务流程

整个流程涉及订单服务、购物车服务、库存服务三个独立数据库。一旦其中某一步(如扣减库存时库存不足)失败,前面已成功执行的步骤(创建订单、清理购物车)无法回滚,导致订单已生成但库存未扣减的数据不一致问题,这就是典型的超卖场景。

分布式事务的定义

在分布式系统中,如果一个业务需要多个服务合作完成,而且每一个服务都有事务,多个事务必须同时成功或失败,这样的事务就是分布式事务。其中的每个服务的事务就是一个分支事务,整个业务称为全局事务。

分布式事务结构

初识 Seata

Seata 简介

Seata 是 2019 年 1 月份蚂蚁金服和阿里巴巴共同开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务,为用户打造一站式的分布式解决方案。官网地址:http://seata.io/,其中的文档、播客中提供了大量的使用说明、源码分析。

Seata 介绍

Seata 的学习路径包含:初识 Seata、部署 TC 服务、微服务集成 Seata、XA 模式、AT 模式。

Seata 学习路径

分布式事务解决思路

解决分布式事务,各个子事务之间必须能感知到彼此的事务状态,才能保证状态一致。例如订单服务下单成功、购物车清理成功,但库存服务提示库存不足,需要让所有分支事务感知到这个失败状态并统一回滚。

分布式事务解决思路

Seata 架构

Seata 事务管理中有三个重要的角色:

  • TC (Transaction Coordinator) - 事务协调者:维护全局和分支事务的状态,协调全局事务提交或回滚
  • TM (Transaction Manager) - 事务管理器:定义全局事务的范围、开始全局事务、提交或回滚全局事务
  • RM (Resource Manager) - 资源管理器:管理分支事务,与 TC 交谈以注册分支事务和报告分支事务的状态

TM 开启并结束全局事务,各个微服务中的 RM 负责分支事务的注册与状态报告,TC 协调全局事务的提交或回滚。

部署 TC 服务

部署 TC 服务可参考讲义《2.2.部署TC服务》。

部署 TC 服务

微服务集成 Seata

引入依赖

首先在项目中引入 Seata 依赖:

<!-- seata -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
</dependency>

配置 TC 服务地址

在 application.yml 中添加配置,让微服务找到 TC 服务地址:

seata:
  registry: # 注册中心的配置,微服务根据这些信息去注册中心获取TC服务地址
    type: nacos # 注册中心类型 nacos
    nacos:
      server-addr: 192.168.150.101:8848
      namespace: ""
      group: DEFAULT_GROUP
      application: seata-server # seata服务名称
      username: nacos
      password: nacos
  tx-service-group: hmall # 事务组名称
  service:
    vgroup-mapping: # 事务组与tc集群的映射关系
      hmall: "default"

为了避免重复配置,可以将 Seata 配置共享到 Nacos 中,由各个微服务统一拉取。

XA 模式

XA 规范

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

XA 模式工作流程

Seata 的 XA 模式分为两个阶段:

一阶段的工作:

  1. RM 注册分支事务到 TC
  2. RM 执行分支业务 sql 但不提交
  3. RM 报告执行状态到 TC

二阶段的工作:

  1. TC 检测各分支事务执行状态
  2. 如果都成功,通知所有 RM 提交事务
  3. 如果有失败,通知所有 RM 回滚事务
  4. RM 接收 TC 指令,提交或回滚事务

XA 模式架构

XA 模式优缺点

优点:

  • 事务的强一致性,满足 ACID 原则
  • 常用数据库都支持,实现简单,并且没有代码侵入

缺点:

  • 因为一阶段需要锁定数据库资源,等待二阶段结束才释放,性能较差
  • 依赖关系型数据库实现事务

实现 XA 模式

Seata 的 starter 已经完成了 XA 模式的自动装配,实现非常简单:

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

seata:
  data-source-proxy-mode: XA # 开启数据源代理的XA模式

步骤二:给发起全局事务的入口方法添加 @GlobalTransactional 注解,本例中是 OrderServiceImpl 中的 create 方法:

@Override
@GlobalTransactional
public Long createOrder(OrderFormDTO order) {
    // 创建订单 ... 略
    // 清理购物车 ... 略 
    // 扣减库存 ...略
    return order.getId();
}

步骤三:重启服务并测试

AT 模式

AT 模式原理

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

阶段一 RM 的工作:

  1. 注册分支事务
  2. 记录 undo-log(数据快照)
  3. 执行业务 sql 并提交
  4. 报告事务状态

阶段二提交时 RM 的工作:

  • 删除 undo-log 即可

阶段二回滚时 RM 的工作:

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

AT 模式与 XA 模式的区别

  • XA 模式一阶段不提交事务,锁定资源;AT 模式一阶段直接提交,不锁定资源
  • XA 模式依赖数据库机制实现回滚;AT 模式利用数据快照实现数据回滚
  • XA 模式强一致;AT 模式最终一致

实现 AT 模式

步骤一:添加资料中的 seata-at.sql 到微服务对应的数据库中:

CREATE TABLE IF NOT EXISTS `undo_log`
(
    `branch_id`     BIGINT       NOT NULL COMMENT '分支事务id',
    `xid`           VARCHAR(128) NOT NULL COMMENT '全局事务id',
    `context`       VARCHAR(128) 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 KEY `ux_undo_log` (`xid`, `branch_id`)
) ENGINE = InnoDB
  AUTO_INCREMENT = 1
  DEFAULT CHARSET = utf8mb4 COMMENT ='AT transaction mode undo table';

步骤二:修改 application.yml 文件,将事务模式修改为 AT 模式:

seata:
  data-source-proxy-mode: AT # 开启数据源代理的AT模式