Seata 分布式事务使用

发布于:2025-02-10 ⋅ 阅读:(61) ⋅ 点赞:(0)


在这里插入图片描述

分布式事务

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

安装 seata

docker 安装 seata

配置参考 https://blog.csdn.net/weixin_42633509/article/details/145204282
添加数据库

-- 1. 执行语句创建名为 seata 的数据库
CREATE DATABASE seata DEFAULT CHARACTER SET utf8mb4 DEFAULT COLLATE utf8mb4_general_ci;

-- 2.执行脚本完成 Seata 表结构的创建
use seata;

-- https://github.com/seata/seata/blob/1.7.1/script/server/db/mysql.sql
-- -------------------------------- 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_status_gmt_modified` (`status` , `gmt_modified`),
    KEY `idx_transaction_id` (`transaction_id`)
) ENGINE = InnoDB
  DEFAULT CHARSET = utf8mb4;

-- 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 = utf8mb4;

-- 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),
    `status`         TINYINT      NOT NULL DEFAULT '0' COMMENT '0:locked ,1:rollbacking',
    `gmt_create`     DATETIME,
    `gmt_modified`   DATETIME,
    PRIMARY KEY (`row_key`),
    KEY `idx_status` (`status`),
    KEY `idx_branch_id` (`branch_id`),
    KEY `idx_xid` (`xid`)
) ENGINE = InnoDB
  DEFAULT CHARSET = utf8mb4;

CREATE TABLE IF NOT EXISTS `distributed_lock`
(
    `lock_key`       CHAR(20) NOT NULL,
    `lock_value`     VARCHAR(20) NOT NULL,
    `expire`         BIGINT,
    primary key (`lock_key`)
) ENGINE = InnoDB
  DEFAULT CHARSET = utf8mb4;

INSERT INTO `distributed_lock` (lock_key, lock_value, expire) VALUES ('AsyncCommitting', ' ', 0);
INSERT INTO `distributed_lock` (lock_key, lock_value, expire) VALUES ('RetryCommitting', ' ', 0);
INSERT INTO `distributed_lock` (lock_key, lock_value, expire) VALUES ('RetryRollbacking', ' ', 0);
INSERT INTO `distributed_lock` (lock_key, lock_value, expire) VALUES ('TxTimeoutCheck', ' ', 0);

拉取 docker 镜像

docker pull seataio/seata-server:2.0.0

启动临时容器

docker run -d -p 8091:8091 -p 7091:7091  --name seata-server seataio/seata-server:2.0.0

拷贝临时容器的配置至宿主机

docker cp seata-server:/seata-server/resources/. F:/docker/data/seata/config/

配置 application.yml 文件信息使用

#  Copyright 1999-2019 Seata.io Group.
#
#  Licensed under the Apache License, Version 2.0 (the "License");
#  you may not use this file except in compliance with the License.
#  You may obtain a copy of the License at
#
#  http://www.apache.org/licenses/LICENSE-2.0
#
#  Unless required by applicable law or agreed to in writing, software
#  distributed under the License is distributed on an "AS IS" BASIS,
#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#  See the License for the specific language governing permissions and
#  limitations under the License.

server:
  port: 7091

spring:
  application:
    name: seata-server

logging:
  config: classpath:logback-spring.xml
  file:
    path: ${user.home}/logs/seata
console: 
  user:
    username: seata #名字和password随意起
    password: seata

seata:
  config:
    # support: nacos 、 consul 、 apollo 、 zk  、 etcd3
    type: file
    nacos:
      server-addr: 127.0.0.1:8848
      namespace:
      group: DEFAULT_GROUP
      username:
      password:
      context-path:
      ##if use MSE Nacos with auth, mutex with username/password attribute
      #access-key:
      #secret-key:
      data-id: seataServer.properties
  registry:
    # support: nacos 、 eureka 、 redis 、 zk  、 consul 、 etcd3 、 sofa
    type: nacos
    nacos:
      application: seata-server
      server-addr: 172.17.0.5:8848
      group: DEFAULT_GROUP
      namespace:
      cluster: default
      username: nacos
      password: nacos
      context-path:
  security:
    secretKey: "seata" #key随便起名
    tokenValidityInMilliseconds: 1000000000

  server:
    raft:
      group: default
      cluster:
      snapshot-interval: 600
      apply-batch: 32
      max-append-bufferSize: 262144
      max-replicator-inflight-msgs: 256
      disruptor-buffer-size: 16384
      election-timeout-ms: 1000
      reporter-enabled: false
      reporter-initial-delay: 60
      serialization: jackson
      compressor: none
      sync: true # sync log&snapshot to disk
    service-port: 8091 #If not configured, the default is '${server.port} + 1000'
    max-commit-retry-timeout: -1
    max-rollback-retry-timeout: -1
    rollback-retry-timeout-unlock-enable: false
    enable-check-auth: true
    enable-parallel-request-handle: true
    enable-parallel-handle-branch: false
    retry-dead-threshold: 130000
    xaer-nota-retry-timeout: 60000
    enableParallelRequestHandle: true
    recovery:
      committing-retry-period: 1000
      async-committing-retry-period: 1000
      rollbacking-retry-period: 1000
      timeout-retry-period: 1000
    undo:
      log-save-days: 7
      log-delete-period: 86400000
    session:
      branch-async-queue-size: 5000 #branch async remove queue size
      enable-branch-async-remove: false #enable to asynchronous remove branchSession
  store:
    # support: file 、 db 、 redis 、 raft
    mode: db
    session:
      mode: db
    lock:
      mode: db
    db:
      datasource: druid
      db-type: mysql
      driver-class-name: com.mysql.cj.jdbc.Driver
      url: jdbc:mysql://172.17.0.3:3306/seata?rewriteBatchedStatements=true
      user: mysql
      password: password
      min-conn: 10
      max-conn: 100
      global-table: global_table
      branch-table: branch_table
      lock-table: lock_table
      distributed-lock-table: distributed_lock
      query-limit: 1000
      max-wait: 5000
  metrics:
    enabled: false
    registry-type: compact
    exporter-list: prometheus
    exporter-prometheus-port: 9898
  transport:
    rpc-tc-request-timeout: 15000
    enable-tc-server-batch-send-response: false
    shutdown:
      wait: 3
    thread-factory:
      boss-thread-prefix: NettyBoss
      worker-thread-prefix: NettyServerNIOWorker
      boss-thread-size: 1

启动docker容器, 启动前对容器内部互联docker 网络 查看配置

docker run --name seata-server \
--privileged=true \
--restart=always \
-p 8091:8091 \
-p 7091:7091 \
-e STORE_MODE=db \
-v /mydata/seata/config/application.yml:/seata-server/resources/application.yml \
-d seataio/seata-server:2.0.0

docker run --name seata-server \ 
--privileged=true \ 
--restart=always \ 
-p 8091:8091 \ 
-p 7091:7091 \ 
-e STORE_MODE=db \ 
-e SEATA_IP=192.168.52.131 \ 
-e SEATA_PORT=8091 \ 
-v /mydata/seata/config:/seata-server/resources \ 
-d seataio/seata-server:2.0.0

# windows
docker run --name seata-server --privileged=true --restart=always -p 8091:8091 -p 7091:7091 -e SEATA_IP=192.168.5.18 -e SEATA_PORT=8091 -v F:/docker/data/seata/config/:/seata-server/resources -d seataio/seata-server:2.0.0

至此 seata 启动起来

使用Seata

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

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

实现XA模式

在这里插入图片描述

XA模式的优点是什么?

  • 事务的强一致性,满足ACID原则。
  • 常用数据库都支持,实现简单,并且没有代码侵入
    XA模式的缺点是什么?
  • 因为一阶段需要锁定数据库资源,等待二阶段结束才释放,性能较差
  • 依赖关系型数据库实现事务

Seata的starter已经完成了XA模式的自动装配,实现非常简单,步骤如下:
修改application.yml文件(每个参与事务的微服务),开启XA模式:

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

在需要使用分布式事务的方法上面使用 @GlobalTransactional

@GlobalTransactional
public Long createOrder(OrderFormDTO orderFormDTO) {}

实现AT模式

在这里插入图片描述

简述AT模式与XA模式最大的区别是什么?

  • XA模式一阶段不提交事务,锁定资源;AT模式一阶段直接提交,不锁定资源。
  • XA模式依赖数据库机制实现回滚;AT模式利用数据快照实现数据回滚。
  • XA模式强一致;AT模式最终一致
    添加 数据表到每个 微服务中去
-- 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';
sql

修改 对应 application.yaml 或者nacos 统一配置

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

Seata 表介绍

在 Seata 中,分布式事务被分成全局事务和分支事务。全局事务是由业务发起方启动的事务,而分支事务则是指全局事务在一个服务内的局部事务。为了协调这些事务,Seata 使用了 AT(Automatic Transaction)、TCC(Try-Confirm-Cancel)、Saga 和 XA 模式等几种不同的事务模式。

branch_table
branch_table 是 Seata 框架中的一个重要组成部分,用于分布式事务管理。Seata 是一种开源的分布式事务解决方案,旨在提供高性能和易用性的同时保证数据的一致性。

  • 分支ID(branch_id):唯一标识一个分支事务。
  • 全局事务ID(xid):关联到全局事务。
  • 分支类型(branch_type):指示该分支事务的类型,比如AT、TCC等。
  • 资源描述(resource_desc):描述了资源的位置或相关信息,例如数据库连接信息。
  • 状态(status):表示分支事务的状态,如正在进行、已提交、已回滚等。
  • 这些信息对于协调分布式事务的提交和回滚至关重要。当一个全局事务需要提交或回滚时,Seata 服务器会根据 branch_table 中的信息来决定如何操作每个分支事务,从而确保整个分布式事务的一致性。

global_table
global_table 主要记录了全局事务相关的元数据,这些信息对于协调和管理分布式事务的生命周期至关重要。

  • Transaction ID (xid): 全局事务的唯一标识符。每个分布式事务都有一个唯一的 xid,它被用来关联该事务下的所有分支事务。
  • Transaction Name: 事务的名称或描述,有时可以用来标识事务的目的或类型。
  • Status: 全局事务的状态,例如初始化、正在进行、提交、回滚等状态。这个字段帮助跟踪事务的进度和结果。
  • Application ID: 发起事务的应用程序ID,有助于识别哪个应用启动了该事务。
  • Transaction Service Group: 服务组名,与配置相关,有助于定位具体的事务协调器。
  • Timeout: 事务的超时时间设置,如果事务在这个时间内没有完成(提交或回滚),则会被认为是超时。
  • Begin Time: 事务开始的时间戳,记录了事务创建的具体时间。
  • Application Data: 可选字段,有时会用来保存一些业务相关的额外信息或者上下文。

lock_table:
lock_table 在 Seata 分布式事务框架中扮演着关键角色,主要用于解决分布式事务中的数据锁定问题。在分布式事务处理过程中,为了保证数据的一致性和隔离性,Seata 需要对涉及的资源进行锁定,以防止并发事务造成的数据不一致问题。

  • RowKey: 这是一个唯一标识符,用来标识被锁定的记录。它通常由表名和主键值组成,确保在整个数据库范围内是唯一的。
  • XID: 全局事务ID,用于关联到具体的全局事务。这帮助 Seata 确定哪个全局事务持有了特定的锁。
  • Transaction ID (transaction_id): 与 XID 相关,有时用于内部引用或当需要直接关联到一个事务管理器时使用。
  • Branch ID (branch_id): 分支事务ID,表示持有该锁的分支事务。这对于分布式事务中的细粒度控制非常重要。
  • Lock Name: 锁的名称或描述,虽然不是所有实现都必需,但它可能用于提供更详细的锁信息或便于管理和调试。

lock_table 的主要功能是记录哪些数据项被哪些事务所锁定,以便其他事务尝试访问这些数据项时可以识别出存在冲突,并采取相应的措施(例如等待当前事务释放锁或者抛出异常)。通过这种方式,Seata 能够有效地避免脏读、不可重复读等并发问题,同时保证了数据的一致性和完整性。

异常问题

  1. 以下 可能是 jdk17 的问题 通过添加
    --add-opens=java.base/java.lang=ALL-UNNAMED
    
    解决如下问题
    org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'globalTransactionScanner' defined in class path resource [io/seata/spring/boot/autoconfigure/SeataAutoConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [io.seata.spring.annotation.GlobalTransactionScanner]: Factory method 'globalTransactionScanner' threw exception; nested exception is java.lang.ExceptionInInitializerError
    
  2. 集成seata报错:can not register RM,err:can not connect to services-server.
    通过启动时docker 添加 对应的 -e SEATA_IP=192.168.5.18 -e SEATA_PORT=8091

网站公告

今日签到

点亮在社区的每一天
去签到