Version: Next

分布式事务问题

引例

  • 单体应用被拆分为微服务应用,原来的三个模块被拆分为三个独立应用,分别使用三个独立的数据源
  • 业务操作需要调用三个服务来完成
  • 每个服务内部的数据一致性由本地事务保证
  • 全局数据一致性无法保证,三个服务物理上分离,逻辑上应当是一个整体
  • 仓储服务:针对给定的商品扣除仓储数目
  • 订单服务:根据采购需求创建订单
  • 账户服务:从用户账户中扣除余额

常见分布式事务解决方案

  • 2PC
  • TCC
  • 可靠消息最终一致性
  • 最大努力通知

分布式事务两阶段提交方案

2PC 协议

  • 两阶段提交协议,将整个事务流程分为两个阶段
    • 准备阶段 Prepare Phase
    • 提交阶段 Commit Phase
  • 2 指两个阶段
  • P 指准备阶段
  • C 指提交阶段
  • 主要由 事务管理器 TM、事务参与者 RM 完成
  • 常见关系型数据库 Oracle、MySQL 都支持 2PC 协议

准备阶段

事务管理器给每个参与者发送 Prepare 消息,每个数据库参与者都在本地执行事务,并写本地的 Undo / Redo 日志,此时事务没有提交

  • Undo 日志是记录修改前的数据,用于数据库回滚
  • Redo 日志是记录修改后的数据,用于提交事务后写入数据文件

提交阶段

如果事务管理器收到了参与者的执行失败或者超时消息,直接给每个参与者发送 回滚 Rollback 消息;否则发送 提交 Commit 消息

  • 事务参与者根据事务管理器的指令执行提交或者回滚操作,并释放事务处理过程中使用的锁资源
  • 必须在最后阶段释放锁

成功情况

失败情况

2PC 解决方案

XA 方案

关系型数据库如 OracleMySQL 都支持 2PC 协议,XA 就是基于数据库层面实现的 2PC 解决方案

  • 国际开放标准组织 Open Group 定义了分布式事务处理模型 DTP (Distributed Transaction Prcessing Reference Model)

场景:注册用户送积分

执行过程

  1. 应用程序 AP 持有 用户库积分库 两个数据源
  2. 应用程序 AP 通过 事务管理器 TM 通知用户库 资源管理器 RM 新增用户,同时通知积分库 RM 为该新用户新增积分,RM 此时并为提交事务,此时用户和积分资源被锁定
  3. 事务管理器 TM 收到执行回复,只要一方失败则分别向其他 RM 发起事务回滚,回滚完毕,资源锁释放
  4. 事务管理器 TM 收到执行回复,全部成功,此时向所有 RM 发起提交事务,提交完毕,资源锁释放

DTP 模型定义如下角色

  • AP (Application Program):即 应用程序,可以理解为使用 DTP 分布式事务的程序
  • RM(Resource Manager):即 资源管理器,可以理解为事务参与者,一般情况下是指一个数据库实例,通过资源管理器对该数据库进行控制,资源管理器控制着分支事务
  • TM(Transaction Manager):即 事务管理器,负责协调和管理事务,事务管理器控制着全局事务,管理事务生命周期,并协调各个 RM全局事务 是指分布式事务处理环境中,需要操作多个数据库共同完成的一个工作,这个工作即是一个全局事务
  • DTP 模型定义 TMRM 之间通讯的接口规范叫做 XA,简单理解成数据库提供的 2PC 接口协议,基于数据库的 XA 协议来实现 2PC 又称为 XA 方案

三角色交互方式

  1. TMAP 提供 应用程序编程接口AP 通过 TM 提交及回滚事务
  2. TM 通过 XA 接口来通知 RM 数据库事务开始、结束、提交、回滚等

XA 方案的缺陷

  • 需要数据库层面支持 XA 协议,一般需要是关系型数据库
  • 在正式提交前,会锁定相关资源,资源锁需要等到两个阶段结束才释放,性能较差

Seata 方案

传统 2PC 存在的问题在 Seata 中得到了解决,它通过对本地关系型数据库分支事务的协调来驱动完成全局事务,是工作在应用层的中间件

  • 性能较好,不会长时间占用连接资源
  • 以高效并且对业务零入侵的方式解决微服务场景下面临的分布式事务问题
  • 目前提供 AT(即 2PC) 模式及 TCC 模式的分布式事务解决方案

Seata 设计思想

  • 将一个分布式事务理解成一个 包含了若干 分支事务全局事务
  • 全局事务的职责是协调其下管辖的分支事务达成一致,要么一起成功提交,要么一起失败回滚
  • 通常分支事务就是一个关系型数据库的本地事务

Seata 三组件

  • Seata 定义了 3 个组件来协调分布式事务的处理过程


  • Transaction Coordinator TC:事务协调器,它是独立的中间件,需要独立部署运行,它维护事务的运行状态,接收 TM 指令发起全局事务的提交和回滚,负责与 RM 通信协调各个分支事务的提交和回滚
  • Transaction Manager TM:事务管理器,TM 需要嵌入应用程序工作,它负责开启一个全局事务,并最终向 TC 发起全局提交或全局回滚指令
  • Resource Manager RM:控制分支事务,负责分支注册、状态汇报,并接收事务协调器 TC 的指令,驱动(本地)事务的提交和回滚

Seata 实现 2PC 与传统 2PC 实现的差别:

  • 架构层次方面:
    • 传统 2PC 方案: RM 实际上在数据库层面,RM 本质上就是数据库自身,通过 XA 协议实现
    • Seata:RM 是以 Jar 包形式作为中间件部署在应用程序一侧
  • 两阶段提交方面:
    • 传统 2PC 方案:无论第二阶段的决议是 提交 还是 回滚,事务性资源的锁都要保持到第二阶段结束才能释放
    • Seata:在第一阶段就提交本地事务,可以省去第二阶段持有锁的时间,提高整体效率

Seata 概述

http://seata.io/zh-cn/

  • Simple Extensible Autonomous Transaction Architecture 简单可扩展自治事务框架
  • Transaction ID XID —— 全局唯一事务 ID
  • 3 组件:
    • Transaction Coordinator TC 事务协调器:维护全局和分支事务的状态,驱动全局事务提交或回滚。
    • Transaction Manage TM 事务管理器:定义全局事务的范围:开始全局事务、提交或回滚全局事务。
    • Resource Manager RM 资源管理器:管理分支事务处理的资源,与 TC 交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。


  1. TMTC 申请开启一个全局事务,全局事务创建成功并生成一个全局唯一事务 ID—— XID
  2. XID 在微服务调用链路的上下文中传播
  3. RM (数据库)向 TC 注册分支事务,本地分支事务执行并提交,分支事务纳入 XID 对应全局事务管辖范围
  4. 各分支事务提交完毕
  5. RM (数据库)向 TC 泛起针对 XID 的全局提交或回滚决议
  6. TC 调度 XID 下管辖的全部分支事务完成提交或回滚请求

Seata 下载安装

  1. 本次采用 seata-server 0.9.0 版本,官网下载
  2. 下载后解压,进入 /conf 目录,修改 file.conf 配置文件
    • 修改 自定义事务组名称 + 事务日志存储模式为 db + 数据库连接信息
    • service 模块、store 模块
      • 修改一下组名
      service {
      #vgroup->rgroup
      vgroup_mapping.my_test_tx_group = "bsx_group" # 修改一下这个名字即可
      #only support single node
      default.grouplist = "127.0.0.1:8091"
      #degrade current not support
      enableDegrade = false
      #disable
      disable = false
      #unit ms,s,m,h,d represents milliseconds, seconds, minutes, hours, days, default permanent
      max.commit.retry.timeout = "-1"
      max.rollback.retry.timeout = "-1"
      }
  3. 在 MySQL 5.7 中新建 seata
  4. seata 库里建表,脚本在 /conf/db_store.sql
  5. 修改 /conf 目录下的 registry.conf 配置文件
    • 修改为 nacos
    registry {
    # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
    # type = "file"
    type = "nacos" # 修改为 nacos
    nacos {
    serverAddr = "localhost:8848"
    namespace = ""
    cluster = "default"
    }
    # 省略之后的东西
  6. 启动 Nacos8848
  7. 启动 seata-server
    • sh seata-server.sh
  8. 查看 Nacos,出现了一个服务,查看详情可以看到其运行在 8091 端口,即 seata-server