分布式事务

1. 分布式事务场景

  当项目是微服务架构时,一个功能需求可能需要多个微服务共同完成,这个过程就难免会操作不同服务下的不同数据库,那么如何保证数据库的ACID的性质呢?
  除了微服务架构,只要是分布式架构就会面临同样的问题,此时无法通过mysql提供的事务功能解决,此时就需要用到分布式事务解决方案。

2. Seata

2.1 认识Seata

  Seata是阿里提供的开源分布式事务框架(https://seata.apache.org/zh-cn/docs/overview/what-is-seata/)。
  在Seata的事务管理中,有三个角色:

  • TC(Transaction Coordinator) - 事务协调者:维护全局和分支事务的状态,协调全局事务提交或者回滚;
  • TM(Transaction Manager) - 事务管理器:定义全局事务的范围,开始全局事务、提交或回滚全局事务;
  • RM(Resource Manager) - 资源管理器:管理分支事务,于TC交互以注册分支事务和报告分支事务的状态,驱动分支事务提交或者回滚。
      TC相当于事务管理的服务器,TM和RM是事务管理的客户端,Seata事务管理框架:

  要使用分布式事务,需要以下三步部署TC服务微服务集成Seata选择事务处理模式并配置

2.2 部署TC服务

  1. 步骤一:准备数据库表,保存Seata的临时文件,也可以选择redis等数据库

  2. 步骤二:准备配置文件

  3. 步骤三:在docker中部署

    1
    2
    3
    4
    5
    6
    7
    8
    9
    docker run --name seata \
    -p 8099:8099 \
    -p 7099:7099 \
    -e SEATA_IP=192.168.223.101 \
    -v ./seata:/seata-server/resources \
    --privileged=true \
    --network hm-net \
    -d \
    seataio/seata-server:1.5.2

2.3 微服务集成Seata

  分布式事务中涉及到的所有微服务都需要集成Seata

  1. 步骤一:引入依赖,重点是seata依赖,而前两个依赖是为了共享配置
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    <!--统一配置管理-->
    <dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
    </dependency>
    <!--读取bootstrap文件-->
    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-bootstrap</artifactId>
    </dependency>
    <!--seata-->
    <dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
    </dependency>
  2. 步骤二:添加seata配置
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    seata:
    registry: # TC服务注册中心的配置,微服务根据这些信息去注册中心获取tc服务地址
    type: nacos # 注册中心类型 nacos
    nacos:
    server-addr: 192.168.150.101:8848 # nacos地址
    namespace: "" # namespace,默认为空
    group: DEFAULT_GROUP # 分组,默认是DEFAULT_GROUP
    application: seata-server # seata服务名称
    username: nacos
    password: nacos
    tx-service-group: hmall # 事务组名称
    service:
    vgroup-mapping: # 事务组与tc集群的映射关系
    hmall: "default"
  3. 步骤三:在bootstrap.yaml中添加共享配置项
  4. 步骤四:在微服务的数据库中添加一个表,用于保存事务一些中间数据
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    -- for AT mode you must to init this sql for you business database. the seata server not need it.
    CREATE TABLE IF NOT EXISTS `undo_log`
    (
    `branch_id` BIGINT NOT NULL COMMENT 'branch transaction id',
    `xid` VARCHAR(128) NOT NULL COMMENT 'global transaction 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';
  5. 步骤五:在全局事务的方法中添加注解@GlobalTransactional

3. 分布式事务解决方案

  在Seata中提供了4中分布式事务解决方案,这里我们只介绍前两种:

  • XA
  • AT
  • TCC
  • SAGA

3.1 XA模式

  XA是一种分布式事务处理方案的标准,几乎所有主流的数据库都对XA规范提供了支持。接下来解释Seata的XA模型。

XA模型分为两个阶段

  • RM 一阶段的工作:

    1. 注册分支事务到TC
    2. 执行分支业务sql但不提交
    3. 报告执行状态到TC
  • TC 二阶段的工作:

    1. TC检测各分支事务执行状态
      1. 如果都成功,通知所有RM提交事务
      2. 如果有失败,通知所有RM回滚事务
  • RM 二阶段的工作:

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

优缺点:

  • 优点
    • 保证了事务的强一致性
    • 常用的数据都支持,实现简单
  • 缺点
    • 一阶段执行完sql后,不能提交事务,需要一直占用资源,导致性能差
    • 依赖关系型数据库实现事务

实现步骤

  1. 步骤一:在共享的seata配置文件中配置模式
    1
    2
    seata:
    data-source-proxy-mode: XA
  2. 步骤二:使用@GlobalTransactional注解标记全局事务的入口

3.2 AT模式

  AT模式依旧是两阶段模型,但是弥补了资源释放慢、性能差的缺陷。

基本流程

  • 阶段一RM的工作:
    1. 注册分支事务
    2. 记录undo-log(数据快照)
    3. 执行业务sql并提交
    4. 报告事务状态
  • 阶段二提交时RM的工作:
    • 删除undo-log即可
  • 阶段二回滚时RM的工作:
    • 根据undo-log恢复数据到更新前

  在AT模式中,分支事务完成sql语句后可以直接提交,但是在更新sql前需要记录 快照(前面数据库添加的undo-log表) 并保存,若第二阶段判断全局事务失败,则根据快照进行恢复。全局事务完成后,无论是否成功,都会删除快照

优缺点

  • 优点
    • 阶段一不锁定资源,因此效率较高
    • 不依赖关系型数据库的事务,回滚机制依赖于快照
  • 缺点
    • 在第一阶段和第二阶段间可能会有数据不一致的现象,但是最终一致

实现步骤

  1. 步骤一:修改共享配置seata钟大哥事务解决方案模型
    1
    2
    seata:
    data-source-proxy-mode: AT
  2. 步骤二:使用@GlobalTransactional注解标记全局事务的入口