Seata分布式事物(一)基础信息介绍、docker部署SeataServer注册到nacos

1、什么是本地事物

一般情况一个进程中操作一个数据库,属于本地事务

@Transactional 是本地事务,在分布式系统,只能控制住自己的回滚,控制不了其它服务的回滚,所以,是有局限性的,如果方法里边需要调用其它服务的操作,则要使用分布式事务。

1.1、数据库事务特性 ACID

​ - A(Atomic):原子性,构成事务的所有操作,要么都执行完成,要么全部不执行,不可能出现部分成功部分失败的情况。

​- C(Consistency):一致性,在事务执行前后,数据库的一致性约束没有被破坏。比如:张三向李四转100元,转账前和转账后的数据是正确状态这叫一致性,如果出现张三转出100元,李四账户没有增加100元这就出现了数据错误,就没有达到一致性。

​ - I(Isolation):隔离性,数据库中的事务一般都是并发的,隔离性是指并发的两个事务的执行互不干扰,一个事务不能看到其他事务运行过程的中间状态。通过配置事务隔离级别可以避脏读、重复读等问题。

​ - D(Durability):持久性,事务完成之后,该事务对数据的更改会被持久化到数据库,且不会被回滚。

2、分布式事物是什么

一个进程中操作多个数据库,或者在多个进程中操作一个或多个数据库,就产生了分布式事务;

2.1、分布式事物是什么

分布式事务是指事务的参与者,支持事务的服务器,资源服务器分别位于分布式系统的不同节点之上,通常一个分布式事物中会涉及到对多个数据源或业务系统的操作。

2.2、分布式事务产生的场景

  1. 微服务之间通过远程调用完成事务操作。 比如:订单微服务和库存微服务,下单的同时订单微服务请求库存微服务减库存。 简言之:跨JVM进程产生分布式事务。

  2. 单体系统访问多个数据库实例,当单体系统需要访问多个数据库(实例)时就会产生分布式事务。

  3. 多服务访问同一个数据库实例 比如:订单微服务和库存微服务即使访问同一个数据库也会产生分布式事务,原因就是跨JVM进程,两个微服务持有了不同的数据库链接进行数据库操作,此时产生分布式事务。

3、Seate

Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案。

目前AT和TCC模式更为流行

  • 前提条件
    • 基于支持本地 ACID 事务的关系型数据库。
    • Java 应用,通过 JDBC 访问数据库。

很多大公司在用,可以查看使用的公司及应用的模式

3.1、seate的三种角色

在这里插入图片描述

  • TC (Transaction Coordinator) - 事务协调者:维护全局和分支事务的状态,驱动全局事务提交或回滚;

  • TM (Transaction Manager) - 事务管理器:定义全局事务的范围:开始全局事务、提交或回滚全局事务;TM事物发起方,业务调用处

  • RM (Resource Manager) - 资源管理器:管理分支事务处理的资源,与TC交互以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚;

    TC为单独部署的 Server 服务端,TM和RM为嵌入到应用中的 Client 客户端;

3.2、Seata管理分布式事务的典型生命周期

在这里插入图片描述

  1. TM请求TC开启一个全局事务,TC会生成一个XID作为该全局事务的编号。

  2. XID会在微服务的调用链路中传播,保证将多个微服务的子事务关联在一起。

  3. RM请求TC将本地事务注册为全局事务的分支事务,通过全局事务的XID进行关联。

  4. TM请求TC告诉XID对应的全局事务是进行提交还是回滚。

  5. TC驱动RM将XID对应的自己的本地事务进行提交还是回滚。

    常见问题

    问题汇总github

4、docker部署Seata server

4.1、数据库表结构

-- -------------------------------- The script used when storeMode is 'db' --------------------------------
-- the table to store GlobalSession data
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;

4.2、部署镜像操作

下载镜像
docker pull seataio/seata-server:1.4.1
启动镜像
docker run -d --name seata-server -p 8091:8091 seataio/seata-server:1.4.1
将file.conf和registry.conf复制到宿主机中
docker cp seata-server:/seata-server/resources/file.conf /root/seata-config/
docker cp seata-server:/seata-server/resources/registry.conf /root/seata-config/
文件地址可以进入镜像查看
docker exec -it seata-server /bin/sh
cd resources
在这里插入图片描述

修改file.conf
vim /root/seata-config/file.conf
将store中mode值修改为db
修改db中数据库信息即可

修改registry.conf文件
vim /root/seata-config/registry.conf
将registry中type值改为nacos,或者其他注册中心
修改nacos中信息

执行启动命令

docker run -d --name seata-server -p 8091:8091 -e SEATA_IP=192.168.0.124 -v /root/seata-config/registry.conf:/seata-server/resources/registry.conf -v /root/seata-config/file.conf:/seata-server/resources/file.conf seataio/seata-server:1.4.1

在这里插入图片描述

5、项目测试分布式事物

业务库中需要增加如下表

CREATE TABLE `undo_log` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `branch_id` bigint(20) NOT NULL,
  `xid` varchar(100) NOT NULL,
  `context` varchar(128) NOT NULL,
  `rollback_info` longblob NOT NULL,
  `log_status` int(11) NOT NULL,
  `log_created` datetime NOT NULL,
  `log_modified` datetime NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;

5.1、Springboot项目

  • 配置文件

    seata:
      # 应用id 一般使用项目名称
      application-id: demo-seata
      # 事物服务分组 自定义
      tx-service-group: demo-group
      service:
        vgroup-mapping:
       	  # demo-group:事物服务分组的名称,其实是一个key,可以查看源码 
          demo-group: default
      registry:
        type: nacos
        # nacos配置需要与 seata-server中配置相同
        nacos:
          cluster: default
          application: seata-server
          namespace: local
          group: DEFAULT_GROUP
          server-addr: localhost:8848
    
  • jar包

    <!-- seata-spring-boot-starter -->
    <dependency>
        <groupId>io.seata</groupId>
        <artifactId>seata-spring-boot-starter</artifactId>
        <version>1.3.0</version>
    </dependency>
    <dependency>
        <groupId>com.alibaba.nacos</groupId>
        <artifactId>nacos-client</artifactId>
        <version>1.3.1</version>
    </dependency>
    
  • 项目中业务网处理逻辑增加@GlobalTransactional(与@Transactional使用方式一样)

Springboot项目测试如下
在这里插入图片描述
seata库中存储全局信息
在这里插入图片描述
业务系统undo_log表中数据记录信息如下
在这里插入图片描述
存储回滚json信息如下,供查看

{
  "@class": "io.seata.rm.datasource.undo.BranchUndoLog",
  "xid": "192.168.0.124:8091:123568631891111936",
  "branchId": 123568665743339520,
  "sqlUndoLogs": [
    "java.util.ArrayList",
    [
      {
        "@class": "io.seata.rm.datasource.undo.SQLUndoLog",
        "sqlType": "INSERT",
        "tableName": "******",
        "beforeImage": {
          "@class": "io.seata.rm.datasource.sql.struct.TableRecords$EmptyTableRecords",
          "tableName": "******",
          "rows": [
            "java.util.ArrayList",
            [
              
            ]
          ]
        },
        "afterImage": {
          "@class": "io.seata.rm.datasource.sql.struct.TableRecords",
          "tableName": "******",
          "rows": [
            "java.util.ArrayList",
            [
              {
                "@class": "io.seata.rm.datasource.sql.struct.Row",
                "fields": [
                  "java.util.ArrayList",
                  [
                    {
                      "@class": "io.seata.rm.datasource.sql.struct.Field",
                      "name": "id",
                      "keyType": "PRIMARY_KEY",
                      "type": 4,
                      "value": 3
                    },
                    {
                      "@class": "io.seata.rm.datasource.sql.struct.Field",
                      "name": "name",
                      "keyType": "NULL",
                      "type": 12,
                      "value": "name"
                    },
                    {
                      "@class": "io.seata.rm.datasource.sql.struct.Field",
                      "name": "remark",
                      "keyType": "NULL",
                      "type": 12,
                      "value": "remark"
                    },
                    {
                      "@class": "io.seata.rm.datasource.sql.struct.Field",
                      "name": "create_user",
                      "keyType": "NULL",
                      "type": 4,
                      "value": null
                    },
                    {
                      "@class": "io.seata.rm.datasource.sql.struct.Field",
                      "name": "create_date",
                      "keyType": "NULL",
                      "type": 93,
                      "value": null
                    },
                    {
                      "@class": "io.seata.rm.datasource.sql.struct.Field",
                      "name": "update_user",
                      "keyType": "NULL",
                      "type": 4,
                      "value": 0
                    },
                    {
                      "@class": "io.seata.rm.datasource.sql.struct.Field",
                      "name": "update_date",
                      "keyType": "NULL",
                      "type": 93,
                      "value": null
                    }
                  ]
                ]
              }
            ]
          ]
        }
      }
    ]
  ]
}

下一篇讲解什么是at模式与微服务feign调用

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值