Seata分布式事务环境搭建

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: Seata分布式事务环境搭建

下载seata服务端

https://github.com/seata/seata/releases

修改registry.conf

这里使用nacos做注册中心和配置中心, 也就不需要服务端的file.conf了

但是使用nacos时, nacos的密码不能有特殊符号, 否则seata可能连接不上(1.5.0已修复)

registry {
  type = "nacos"
  nacos {
    application = "seata-server"
    serverAddr = "127.0.0.1:8848"
    group = "SEATA_GROUP"
    namespace = "e794b575-4231-4935-8271-145c5840d392"
    cluster = "default"
    username = "nacos"
    password = "nacos"
  }
}
config {
  type = "nacos"
  nacos {
    serverAddr = "127.0.0.1:8848"
    namespace = "e794b575-4231-4935-8271-145c5840d392"
    group = "SEATA_GROUP"
    username = "nacos"
    password = "nacos"
    dataId = "seataServer.properties"
  }
}


seata服务端需要的几个表: https://github.com/seata/seata/blob/develop/script/server/db/mysql.sql

其他的一些相关的脚本https://github.com/seata/seata/tree/develop/script

nacos建立命名空间

新增配置文件

Data ID: seataServer.properties

Group: SEATA_GROUP

seataServer.properties配置内容

### seata
store.mode=db
store.publicKey=
## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp)/HikariDataSource(hikari) etc.
store.db.datasource=druid
## mysql/oracle/postgresql/h2/oceanbase etc.
store.db.dbType=mysql
store.db.driverClassName=com.mysql.cj.jdbc.Driver
## if using mysql to store the data, recommend add rewriteBatchedStatements=true in jdbc connection param
store.db.url=jdbc:mysql://192.168.101.128:3309/seata?rewriteBatchedStatements=true&useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&zeroDateTimeBehavior=convertToNull&&serverTimezone=Asia/Shanghai
store.db.user=root
store.db.password=123456
store.db.minConn=5
store.db.maxConn=100
store.db.globalTable = global_table
store.db.branchTable = branch_table
store.db.lockTable =lock_table
store.db.queryLimit = 100
store.db.maxWait = 5000
## transport
# tcp udt unix-domain-socket
transport.type=TCP
#NIO NATIVE
transport.server=NIO
#enable heartbeat
transport.heartbeat=true
transport.serialization=seata
transport.compressor=none
transport.threadFactory.bossThreadPrefix = NettyBoss
transport.threadFactory.workerThreadPrefix = NettyServerNIOWorker
transport.threadFactory.serverExecutorThread-prefix = NettyServerBizHandler
transport.threadFactory.shareBossWorker = false
transport.threadFactory.clientSelectorThreadPrefix = NettyClientSelector
transport.threadFactory.clientSelectorThreadSize = 1
transport.threadFactory.clientWorkerThreadPrefix = NettyClientWorkerThread
transport.threadFactory.bossThreadSize = 1
transport.threadFactory.workerThreadSize = default
# 销毁服务器时, 等待几秒钟
transport.shutdown.wait=3
server.undo.logSaveDays=7
server.undo.logDeletePeriod=86400000


单体服务多库事务

SpringBoot项目引入依赖

<dependency>
    <groupId>io.seata</groupId>
    <artifactId>seata-spring-boot-starter</artifactId>
    <version>1.4.2</version>
</dependency>


项目配置文件

seata:
  application-id: test #这里填你应用的id
  service:
    grouplist:
      # seata-server地址
      default: 127.0.0.1:8091
    # 分组事务
    vgroup-mapping:
      global_tx_group: default
      enable-degrade: false
      disable-global-transaction: false
  # 是否开启spring-boot自动装配
  enabled: true
  # 是否启用数据源 bean 的自动代理
  enable-auto-data-source-proxy: true
  tx-service-group: global_tx_group
  client:
    tm:
      # 一阶段全局提交结果上报TC重试次数 默认1次,建议大于1
      commit-retry-count: 3
      # 一阶段全局回滚结果上报TC重试次数 默认1次,建议大于1
      rollback-retry-count: 3
    rm:
      # 是否上报一阶段成功 true、false,从1.1.0版本开始,默认false.true用于保持分支事务生命周期记录完整,false可提高不少性能
      report-success-enable: true
      # 自动刷新缓存中的表结构 默认false
      table-meta-check-enable: true
      # 一阶段结果上报TC重试次数
      report-retry-count: 5
      # 异步提交缓存队列长度 默认10000。 二阶段提交成功,RM异步清理undo队列
      async-commit-buffer-limit: 1000
      lock:
        # 校验或占用全局锁重试间隔 默认10,单位毫秒
        retry-interval: 10
        # 分支事务与其它全局回滚事务冲突时锁策略 默认true,优先释放本地锁让回滚成功
        retry-policy-branch-rollback-on-conflict: true
        # 校验或占用全局锁重试次数
        retry-times: 30
    undo:
      # 自定义undo表名 默认undo_log
      log-table: seata_undo_log
      # 二阶段回滚镜像校验
      data-validation: true
      # undo log序列化方式
      log-serialization: jackson
  transport:
    type: TCP
    server: NIO
    heartbeat: true
    # client和server通信编解码方式 seata(ByteBuf)、protobuf、kryo、hession、fst,默认seata
    serialization: seata
    # client和server通信数据压缩方式 none、gzip,默认none
    compressor: none
    thread-factory:
      boss-thread-prefix: NettyBoss
      client-worker-thread-prefix: NettyServerNIOWorker
      server-executor-thread-prefix: NettyServerBizHandler
      client-selector-thread-size: 1
      client-selector-thread-prefix: NettyClientWorkerThread


简单使用, 配合dynamic-datasource-spring-boot-starter使用

@Autowired
StaffMapper staffMapper;
@Override
@GlobalTransactional(rollbackFor = Exception.class)
public void globalTx() {
    userService.updateMaster();
    userService.updateIndependent();
  //模拟异常回滚
    int i = 1 / 0;
}
@DS("master")
@Transactional(rollbackFor = Exception.class)
public void updateMaster() {
    User user1 = baseDao.selectById(1);
    user1.setAge(999);
    baseDao.updateById(user1);
    User user2 = baseDao.selectById(2);
    user2.setAge(999);
    baseDao.updateById(user2);
}
@DS("independent")
@Transactional(rollbackFor = Exception.class)
public void updateIndependent() {
    Staff staff1 = staffMapper.selectById(1);
    staff1.setAge(999);
    staffMapper.updateById(staff1);
    Staff staff2 = staffMapper.selectById(2);
    staff2.setAge(999);
    staffMapper.updateById(staff2);
}


可以观察到seata_undo_log中的undo记录

SELECT CAST(rollback_info AS char) FROM seata_undo_log


{
    "@class": "io.seata.rm.datasource.undo.BranchUndoLog",
    "xid": "192.168.101.1:8091:6593516322371825665",
    "branchId": 6593516322371825668,
    "sqlUndoLogs": [
        "java.util.ArrayList",
        [
            {
                "@class": "io.seata.rm.datasource.undo.SQLUndoLog",
                "sqlType": "UPDATE",
                "tableName": "staff",
                "beforeImage": {
                    "@class": "io.seata.rm.datasource.sql.struct.TableRecords",
                    "tableName": "staff",
                    "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": [
                                                "java.lang.Long",
                                                1
                                            ]
                                        },
                                        {
                                            "@class": "io.seata.rm.datasource.sql.struct.Field",
                                            "name": "name",
                                            "keyType": "NULL",
                                            "type": 12,
                                            "value": "1"
                                        },
                                        {
                                            "@class": "io.seata.rm.datasource.sql.struct.Field",
                                            "name": "age",
                                            "keyType": "NULL",
                                            "type": 4,
                                            "value": 2
                                        }
                                    ]
                                ]
                            }
                        ]
                    ]
                },
                "afterImage": {
                    "@class": "io.seata.rm.datasource.sql.struct.TableRecords",
                    "tableName": "staff",
                    "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": [
                                                "java.lang.Long",
                                                1
                                            ]
                                        },
                                        {
                                            "@class": "io.seata.rm.datasource.sql.struct.Field",
                                            "name": "name",
                                            "keyType": "NULL",
                                            "type": 12,
                                            "value": "1"
                                        },
                                        {
                                            "@class": "io.seata.rm.datasource.sql.struct.Field",
                                            "name": "age",
                                            "keyType": "NULL",
                                            "type": 4,
                                            "value": 999
                                        }
                                    ]
                                ]
                            }
                        ]
                    ]
                }
            },
            {
                "@class": "io.seata.rm.datasource.undo.SQLUndoLog",
                "sqlType": "UPDATE",
                "tableName": "staff",
                "beforeImage": {
                    "@class": "io.seata.rm.datasource.sql.struct.TableRecords",
                    "tableName": "staff",
                    "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": [
                                                "java.lang.Long",
                                                2
                                            ]
                                        },
                                        {
                                            "@class": "io.seata.rm.datasource.sql.struct.Field",
                                            "name": "name",
                                            "keyType": "NULL",
                                            "type": 12,
                                            "value": "2"
                                        },
                                        {
                                            "@class": "io.seata.rm.datasource.sql.struct.Field",
                                            "name": "age",
                                            "keyType": "NULL",
                                            "type": 4,
                                            "value": 3
                                        }
                                    ]
                                ]
                            }
                        ]
                    ]
                },
                "afterImage": {
                    "@class": "io.seata.rm.datasource.sql.struct.TableRecords",
                    "tableName": "staff",
                    "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": [
                                                "java.lang.Long",
                                                2
                                            ]
                                        },
                                        {
                                            "@class": "io.seata.rm.datasource.sql.struct.Field",
                                            "name": "name",
                                            "keyType": "NULL",
                                            "type": 12,
                                            "value": "2"
                                        },
                                        {
                                            "@class": "io.seata.rm.datasource.sql.struct.Field",
                                            "name": "age",
                                            "keyType": "NULL",
                                            "type": 4,
                                            "value": 999
                                        }
                                    ]
                                ]
                            }
                        ]
                    ]
                }
            }
        ]
    ]
}


可以看到该表内存储了数据操作前和操作后的记录

微服务项目分布式事务

如果是微服务项目, 需要分布式事务支持, 配置如下

引入依赖

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
</dependency>


配置和单体服务多库事务是一样的, seata.application-id可以不填, 默认取当前应用id

每个模块都需要配置, 因为seata需要代理数据源

但实际1.4.2版本使用jackson/fastjson序列化Date字段时会失败(https://github.com/seata/seata/issues/3883), 可以替换序列化方式为kryo

需要额外引入依赖

<dependency>
    <groupId>io.seata</groupId>
    <artifactId>seata-serializer-kryo</artifactId>
    <version>1.4.2</version>
</dependency>


简易demo

@Autowired
RoleFeignClient roleFeignClient;
@Autowired
StaffFeignClient staffFeignClient;
@GlobalTransactional(rollbackFor = Exception.class)
public RestResult<Boolean> globalTxTest() {
    log.info("xid: {}", RootContext.getXID());
    roleFeignClient.updateRole();
    staffFeignClient.updateUser();
    int i = 1 / 0;
    return RestResult.ok();
}


@Override
@Transactional(rollbackFor = Exception.class)
public RestResult<Boolean> updateRole() {
    log.info("xid: {}", RootContext.getXID());
    RolePO role1 = roleDAO.selectById(1);
    role1.setName("1111111111");
    roleDAO.updateById(role1);
    RolePO role2 = roleDAO.selectById(2);
    role2.setName("2222222");
    roleDAO.updateById(role2);
    return RestResult.ok();
}


@Override
@Transactional(rollbackFor = Exception.class)
public RestResult<Boolean> updateUser() {
    log.info("xid: {}", RootContext.getXID());
    StaffPO staff1 = baseMapper.selectById(1);
    staff1.setAge(999);
    baseMapper.updateById(staff1);
    StaffPO staff2 = baseMapper.selectById(2);
    staff2.setAge(999);
    baseMapper.updateById(staff2);
    return RestResult.ok();
}


相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
2月前
|
SQL NoSQL 数据库
SpringCloud基础6——分布式事务,Seata
分布式事务、ACID原则、CAP定理、Seata、Seata的四种分布式方案:XA、AT、TCC、SAGA模式
SpringCloud基础6——分布式事务,Seata
|
6月前
|
存储 关系型数据库 MySQL
基于Seata实现分布式事务
通过以上步骤,你可以使用 Seata 实现分布式事务,确保在微服务架构中的事务一致性。Seata 支持多种语言和框架,能够满足不同业务场景的需求。欢迎关注威哥爱编程,一起学习成长。
153 1
|
3月前
|
消息中间件 Java Kafka
"Kafka快速上手:从环境搭建到Java Producer与Consumer实战,轻松掌握分布式流处理平台"
【8月更文挑战第10天】Apache Kafka作为分布式流处理平台的领头羊,凭借其高吞吐量、可扩展性和容错性,在大数据处理、实时日志收集及消息队列领域表现卓越。初学者需掌握Kafka基本概念与操作。Kafka的核心组件包括Producer(生产者)、Broker(服务器)和Consumer(消费者)。Producer发送消息到Topic,Broker负责存储与转发,Consumer则读取这些消息。首先确保已安装Java和Kafka,并启动服务。接着可通过命令行创建Topic,并使用提供的Java API实现Producer发送消息和Consumer读取消息的功能。
69 8
|
3月前
|
关系型数据库 MySQL 数据库
SpringCloud2023中使用Seata解决分布式事务
对于分布式系统而言,需要保证分布式系统中的数据一致性,保证数据在子系统中始终保持一致,避免业务出现问题。分布式系统中对数据的操作要么一起成功,要么一起失败,必须是一个整体性的事务。Seata简化了这个使用过程。
83 2
|
3月前
|
Java 关系型数据库 MySQL
(二十七)舞动手指速写一个Seata-XA框架解决棘手的分布式事务问题
相信大家对于事务问题都不陌生,在之前《MySQL事务篇》中曾详解过MySQL的事务机制,在传统的单库环境下开发,咱们可依赖于MySQL所提供的事务机制,来确保单个事务内的一组操作,要么全部执行成功,要么全部执行失败。
|
3月前
|
Java Nacos Docker
"揭秘!Docker部署Seata遇上Nacos,注册成功却报错?这些坑你不得不防!一网打尽解决秘籍,让你的分布式事务稳如老狗!"
【8月更文挑战第15天】在微服务架构中,Nacos搭配Seata确保数据一致性时,Docker部署Seata后可能出现客户端连接错误,如“can not connect to services-server”。此问题多由网络配置不当、配置文件错误或版本不兼容引起。解决策略包括:调整Docker网络设置确保可达性;检查并修正`file.conf`和`registry.conf`中的Nacos地址和端口;验证Seata与Nacos版本兼容性;修改配置后重启服务;参考官方文档和最佳实践进行配置。通过这些步骤,能有效排除故障,保障服务稳定运行。
224 0
|
5月前
|
Java 数据库 开发者
深入解析 Spring Cloud Seata:分布式事务的全面指南
深入解析 Spring Cloud Seata:分布式事务的全面指南
310 1
|
5月前
|
存储 关系型数据库 Java
技术经验解读:三种分布式事务LCN、Seata、MQ
技术经验解读:三种分布式事务LCN、Seata、MQ
168 0
|
5月前
|
消息中间件 SQL 关系型数据库
分布式事务-seata
分布式事务-seata
129 0