分布式事务-Seata框架

Seata介绍

Seata:Simple Extensible Autonomous Transaction Architecture,简易可扩展的自治式分布式事务管理框架,其前身是fescar。是一种简单分布式事务的解决方案。Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案。

官方文档:https://seata.io/zh-cn/docs/overview/what-is-seata.html

三大组件

  1. 事务协调器(TC):维护全局事务和分支事务的状态,驱动全局提交或回滚,相当于是协调者。
  2. 事务管理器(TM):定义全局事务的范围:开始全局事务,提交或回滚全局事务,相当于LCN中发起方。
  3. 资源管理器(RM):管理分支事务正在处理的资源,与TC进行对话以注册分支事务并报告分支事务的状态,并驱动分支事务的提交或回滚,相当于是LCN中的参与方

实现原理

  1. 发起方™和我们的参与方(RM)项目启动之后和协调者TC保持长连接;
  2. 发起方™调用接口之前向TC获取一个全局的事务的id 为xid,注册到 seata 中.Aop实现
  3. 使用feign客户端调用接口的时候,seata重写了feign客户端,在请求中传递该xid。
  4. 参与方(RM)从请求头中获取到该xid,方法执行完后不会立马提交而是等待协调者告诉提交状态。

四种事务模式

第一种、AT
使用这种模式有个前提:

  1. 基于支持本地 ACID 事务的关系型数据库。
  2. Java 应用,通过 JDBC 访问数据库。

实现过程分为两个阶段:
一阶段:
在一阶段中,Seata会拦截“业务SQL“,首先解析SQL语义,找到要更新的业务数据,在数据被更新前,保存下来"undo",然后执行”业务SQL“更新数据,更新之后再次保存数据”redo“,最后生成行锁,这些操作都在本地数据库事务内完成,这样保证了一阶段的原子性。

  1. 解析 SQL:得到 SQL 的类型(UPDATE),表(product),条件(where name = ‘TXC’)等相关的信息。
  2. 查询前镜像:根据解析得到的条件信息,生成查询语句,定位数据。
  3. 执行业务 SQL:更新这条记录的 name 为 ‘GTS’。
  4. 查询后镜像:根据前镜像的结果,通过 主键 定位数据。
  5. 插入回滚日志:把前后镜像数据以及业务 SQL 相关的信息组成一条回滚日志记录,插入到 UNDO_LOG 表中。
  6. 提交前,向 TC 注册分支:申请 product 表中,主键值等于 1 的记录的 全局锁 。
  7. 本地事务提交:业务数据的更新和前面步骤中生成的 UNDO LOG 一并提交。
  8. 将本地事务提交的结果上报给 TC。

二阶段:
相对一阶段,二阶段比较简单,负责整体的回滚和提交,如果之前的一阶段中有本地事务没有通过,那么就执行全局回滚,否在执行全局提交,回滚用到的就是一阶段记录的"undo Log",通过回滚记录生成反向更新SQL并执行,以完成分支的回滚。当然事务完成后会释放所有资源和删除所有日志。

事务回滚的情况

  1. 收到 TC 的分支回滚请求,开启一个本地事务,执行如下操作。
  2. 通过 XID 和 Branch ID 查找到相应的 UNDO LOG 记录。.
  3. 数据校验:拿 UNDO LOG 中的后镜与当前数据进行比较,如果有不同,说明数据被当前全局事务之外的动作做了修改。这种情况,需要根据配置策略来做处理
  4. 根据 UNDO LOG 中的前镜像和业务 SQL 的相关信息生成并执行回滚的语句:
  5. 提交本地事务。并把本地事务的执行结果(即分支事务回滚的结果)上报给 TC。

事务提交的情况

  1. 收到 TC 的分支提交请求,把请求放入一个异步任务的队列中,马上返回提交成功的结果给 TC。
  2. 异步任务阶段的分支提交请求将异步和批量地删除相应 UNDO LOG 记录。

**总结:**AT模式是一种无侵入的分布式事务解决方案,在 AT 模式下,用户只需关注自己的“业务 SQL”,用户的 “业务 SQL” 作为一阶段,Seata 框架会自动生成事务的二阶段提交和回滚操作。该模式会根据用户执行的SQL生成对应的回滚数据的SQL语句,然后根据事务的提交还是回滚来执行回滚的SQL语句还是删除对应的UNDO LOG 记录(SQL语句)。

第二种、TCC
不依赖于底层数据资源的事务支持:是指支持把 自定义 的分支事务纳入到全局事务的管理中。

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

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

第四种、XA
在 Seata 定义的分布式事务框架内,利用事务资源(数据库、消息服务等)对 XA 协议的支持,以 XA 协议的机制来管理分支事务的一种事务模式。
使用前提:支持XA 事务的数据库。Java 应用,通过 JDBC 访问数据库。

这里主要介绍使用AT模式,后续提供每种模式的实现方式、代码案例。

搭建seata服务端

单机版安装

下载地址:https://github.com/seata/seata/releases
这里使用的是1.4.2版本

学习和测试建议使用单机版,简单搭建,生成环境不建议。
直接在github上下载对应的软件包,一键启动即可,默认是file模式,也就是数据以文件的形式保存本地。
解压之后,进入bin目录执行对应的启动命令即可:windows环境执行 bat文件,Linux环境执行 sh文件。
分布式事务-Seata框架_第1张图片

分布式事务-Seata框架_第2张图片

启动成功:
分布式事务-Seata框架_第3张图片
数据保存在本地
分布式事务-Seata框架_第4张图片

集群安装

首先准备mysql、nacos环境,在准备至少两台服务器,这里数据存到MySQL中去。

多个 Seata TC Server 通过 db 数据库,实现全局事务会话信息的共享。同时,每个 Seata TC Server 可以注册自己到注册中心上,方便应用从注册中心获得到他们。

初始化SQL语句(可以去github中找到,源码中也有),seata框架需要用的数据库表
分布式事务-Seata框架_第5张图片

CREATE TABLE IF NOT EXISTS `global_table`
(
    `xid`                       VARCHAR(128) NOT NULL,
    `transaction_id`            BIGINT,
    `status`                    TINYINT      NOT NULL,
    `application_id`            VARCHAR(32),
    `transaction_service_group` VARCHAR(32),
    `transaction_name`          VARCHAR(128),
    `timeout`                   INT,
    `begin_time`                BIGINT,
    `application_data`          VARCHAR(2000),
    `gmt_create`                DATETIME,
    `gmt_modified`              DATETIME,
    PRIMARY KEY (`xid`),
    KEY `idx_gmt_modified_status` (`gmt_modified`, `status`),
    KEY `idx_transaction_id` (`transaction_id`)
) ENGINE = InnoDB
  DEFAULT CHARSET = utf8;

-- the table to store BranchSession data
CREATE TABLE IF NOT EXISTS `branch_table`
(
    `branch_id`         BIGINT       NOT NULL,
    `xid`               VARCHAR(128) NOT NULL,
    `transaction_id`    BIGINT,
    `resource_group_id` VARCHAR(32),
    `resource_id`       VARCHAR(256),
    `branch_type`       VARCHAR(8),
    `status`            TINYINT,
    `client_id`         VARCHAR(64),
    `application_data`  VARCHAR(2000),
    `gmt_create`        DATETIME(6),
    `gmt_modified`      DATETIME(6),
    PRIMARY KEY (`branch_id`),
    KEY `idx_xid` (`xid`)
) ENGINE = InnoDB
  DEFAULT CHARSET = utf8;

-- the table to store lock data
CREATE TABLE IF NOT EXISTS `lock_table`
(
    `row_key`        VARCHAR(128) NOT NULL,
    `xid`            VARCHAR(128),
    `transaction_id` BIGINT,
    `branch_id`      BIGINT       NOT NULL,
    `resource_id`    VARCHAR(256),
    `table_name`     VARCHAR(32),
    `pk`             VARCHAR(36),
    `gmt_create`     DATETIME,
    `gmt_modified`   DATETIME,
    PRIMARY KEY (`row_key`),
    KEY `idx_branch_id` (`branch_id`)
) ENGINE = InnoDB
  DEFAULT CHARSET = utf8;

每个seata服务节点的配置信息修改:主要修改下面两个文件
分布式事务-Seata框架_第6张图片
file.conf文件的修改:注意mysql 的版本,我这里使用的是MySQL8

store {
  ## store mode: file、db、redis
  mode = "db"
  ## rsa decryption public key
  publicKey = ""

  ## database store property
  db {
    ## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp)/HikariDataSource(hikari) etc.
    datasource = "druid"
    ## mysql/oracle/postgresql/h2/oceanbase etc.
    dbType = "mysql"
    driverClassName = "com.mysql.cj.jdbc.Driver"
    ## if using mysql to store the data, recommend add rewriteBatchedStatements=true in jdbc connection param
    url = "jdbc:mysql://www.kaicostudy.com:3306/transaction_seata?rewriteBatchedStatements=true"
    user = "root"
    password = "123456"
    minConn = 5
    maxConn = 100
    globalTable = "global_table"
    branchTable = "branch_table"
    lockTable = "lock_table"
    queryLimit = 100
    maxWait = 5000
  }
}

registry.conf 文件的修改:

registry {
  # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
  type = "nacos"

  nacos {
    application = "seata-server"
    serverAddr = "www.kaicostudy.com:8848"
    group = "SEATA_GROUP"
    namespace = ""
    cluster = "default"
    username = ""
    password = ""
  }
 
}

config {
  # file、nacos 、apollo、zk、consul、etcd3
  type = "nacos"

  nacos {
    serverAddr = "www.kaicostudy.com:8848"
    namespace = ""
    group = "SEATA_GROUP"
    username = ""
    password = ""
    dataId = "seataServer.properties"
  }
}

修改好每个seata服务节点的配置信息后,正常一次启动seata服务就可以了。之后可以在nacos看到seata服务的信息。

你可能感兴趣的:(分布式学习,分布式,java,数据库)