吃透 Seata 分布式事务:原理拆解 + 生产级落地 + 全场景避坑实战

简介: 本文深度解析阿里开源分布式事务框架Seata:剖析TC/TM/RM三大角色与全局事务流程,详解AT(零侵入)、TCC(强控制)、SAGA(长事务)、XA(强一致)四大模式原理、适用场景及核心对比,并通过电商下单实战演示AT模式落地,最后系统梳理生产环境高可用、SQL限制、幂等处理、XID传播等全链路避坑指南。

引言

在微服务架构成为主流的今天,业务系统被拆分为多个独立部署的服务,原本单体应用内的本地事务,演变为跨服务、跨数据库的分布式事务场景。分布式事务的核心挑战,是在网络不可靠、服务节点独立的前提下,保证多个独立数据源操作的原子性与一致性,避免出现订单创建成功但库存未扣减、余额扣减但订单未生成等数据不一致问题。

Seata作为阿里开源的轻量级分布式事务框架,凭借低侵入、高性能、易接入的特性,成为国内微服务体系下分布式事务的首选方案。本文将从底层原理出发,拆解Seata的核心架构与四大事务模式,通过实战案例完成落地,最后梳理全场景生产避坑指南,帮你彻底掌握Seata的核心能力与生产实践。


一、Seata核心架构与分布式事务本质

1.1 分布式事务的核心矛盾

本地事务依赖数据库的ACID特性保证原子性,而分布式场景下,多个数据库实例、多个服务节点的操作相互独立,单个节点的成功无法保证整体操作的成功。经典的电商下单场景就完美体现了这一矛盾:用户下单需要依次执行扣减商品库存扣减用户账户余额生成订单记录三个操作,三个操作分属不同的服务与数据库,任何一个环节失败,都需要所有已执行的操作全部回滚,否则就会出现数据不一致。

分布式事务的本质,就是通过一套协调机制,保证跨节点、跨数据源的多个操作,要么全部成功,要么全部失败,最终实现数据的最终一致性。

1.2 Seata三大核心角色

Seata的分布式事务模型,基于三个核心角色协同实现,三者分工明确,共同完成全局事务的协调与执行:

  • TC(Transaction Coordinator 事务协调器):维护全局事务的运行状态,负责接收TM的指令,协调并驱动所有分支事务的提交或回滚,是Seata的大脑。
  • TM(Transaction Manager 事务管理器):负责全局事务的生命周期管理,向TC发起全局事务的开启、提交或回滚指令,是全局事务的发起者。
  • RM(Resource Manager 资源管理器):部署在每个业务服务节点,负责分支事务的执行,向TC注册分支事务、汇报分支事务状态,接收TC的指令,驱动本地事务的提交或回滚,同时管理本地的undo_log回滚日志。

三者的协同架构如下:

1.3 Seata全局事务的核心流程

一个完整的Seata全局事务,执行流程如下:

  1. TM向TC发起请求,开启一个全局事务,TC生成唯一的全局事务ID(XID);
  2. XID通过微服务的调用链路,在所有相关的服务节点中传播;
  3. 每个服务节点的RM,向TC注册分支事务,将该分支事务纳入对应XID的全局事务管理;
  4. RM执行本地的业务操作,完成分支事务的执行,并向TC汇报分支事务的执行状态;
  5. 当所有分支事务执行完成后,TM根据所有分支的执行状态,向TC发起全局提交或全局回滚指令;
  6. TC接收到指令后,协调所有对应的RM,完成分支事务的提交或回滚。

二、Seata四大事务模式深度拆解

Seata提供了四种不同的事务模式,分别适配不同的业务场景,每种模式的底层实现、一致性保证、性能表现都有显著差异,下面逐一拆解其核心原理与适用场景。

2.1 AT模式:无侵入自动事务模式

AT模式是Seata的默认模式,也是生产环境使用最广泛的模式,基于支持本地ACID特性的关系型数据库实现,对业务代码零侵入,通过自动生成回滚日志完成分布式事务的协调。

2.1.1 核心原理:两阶段提交

AT模式的核心是改进后的两阶段提交模型,大幅优化了传统XA模式的性能问题:

  • 一阶段:业务执行与快照生成RM通过代理数据源拦截业务SQL,解析SQL的类型、表结构、查询条件,在执行业务SQL前,查询对应数据生成前镜像;执行业务SQL后,再次查询对应数据生成后镜像;将前镜像、后镜像与业务SQL信息组装为undo_log回滚日志,与业务数据的修改在同一个本地事务中提交到数据库。提交完成后,向TC申请对应数据的全局锁,申请成功后释放本地数据库锁与数据库连接,完成一阶段执行。
  • 二阶段:提交/回滚执行
  • 全局提交:当TM发起全局提交指令时,TC通知所有RM异步清理对应分支事务的undo_log日志,整个过程无需操作业务数据,执行速度极快。
  • 全局回滚:当TM发起全局回滚指令时,TC通知所有RM执行分支回滚,RM通过一阶段生成的undo_log日志,校验数据的后镜像与当前数据库数据的一致性,校验通过后,根据前镜像生成反向补偿SQL,执行回滚操作,恢复业务数据到事务执行前的状态,完成后清理undo_log并释放全局锁。

AT模式的完整执行流程如下:

2.1.2 隔离级别保证

AT模式通过两层机制实现事务隔离:

  • 写隔离:通过全局锁实现,一阶段本地事务提交前,必须成功获取对应数据的全局锁,否则无法提交本地事务;全局锁在全局事务结束(提交/回滚)后才会释放,保证同一时间只有一个全局事务能修改同一行数据,彻底避免脏写。
  • 读隔离:默认使用数据库本地的隔离级别,若需要实现全局的读已提交,可通过SELECT FOR UPDATE语句触发全局锁检查,避免读取到未提交的全局事务数据。

2.1.3 适用场景

绝大多数基于关系型数据库的微服务业务场景,尤其是希望对业务代码零侵入、快速接入分布式事务的场景,是生产环境的首选模式。

2.2 TCC模式:手动编程事务模式

TCC(Try-Confirm-Cancel)是一种侵入式的手动编程分布式事务模式,需要开发者手动实现三个阶段的业务逻辑,完全不依赖底层数据库的事务能力,适配非关系型数据库、特殊业务逻辑等AT模式无法覆盖的场景。

2.2.1 三个阶段核心职责

  • Try阶段:完成业务资源的检查与预留,是预处理阶段。例如转账场景中,Try阶段不会直接扣减用户余额,而是冻结用户的转账金额,完成资源预留。
  • Confirm阶段:确认执行业务操作,使用Try阶段预留的资源完成最终的业务提交,该阶段必须保证幂等性,因为TC会重复调用Confirm直到执行成功。例如转账场景中,Confirm阶段扣减冻结的金额,完成转账操作。
  • Cancel阶段:取消执行业务操作,释放Try阶段预留的资源,完成业务回滚,该阶段同样必须保证幂等性,同时需要处理空回滚、事务悬挂等问题。例如转账场景中,Cancel阶段解冻用户冻结的金额,恢复到初始状态。

2.2.2 适用场景

非关系型数据库(如Redis、MongoDB)操作、跨金融机构转账、需要自定义事务逻辑的特殊业务场景,对开发者的编码能力要求较高,需要手动处理幂等、空回滚、悬挂等核心问题。

2.3 SAGA模式:长事务解决方案

SAGA模式是针对长事务场景设计的分布式事务方案,核心思想是将长事务拆分为多个正向的本地分支事务,每个分支事务都对应一个反向的补偿操作;当全局事务执行失败时,通过反向补偿操作,将已执行成功的分支事务逐一回滚,保证数据的最终一致性。

Seata的SAGA模式提供两种实现方式:

  • 注解模式:通过@SagaTransactional注解快速接入,适合简单的长事务场景。
  • 状态机模式:通过JSON配置定义事务流程与分支的依赖关系,支持复杂的分支编排、异步执行、异常重试,适合复杂的长事务场景。

适用场景

业务流程长、事务参与者多、事务执行周期长的场景,例如供应链系统、跨境电商订单流程、金融信贷审批流程等。

2.4 XA模式:强一致性事务模式

XA模式基于数据库的XA协议实现,是传统的强一致性两阶段提交方案。一阶段执行业务SQL后,不提交本地事务,仅向TC汇报执行状态,持有数据库的本地锁;二阶段TC根据所有分支的执行状态,通知所有RM统一提交或回滚本地事务,释放数据库锁。

XA模式保证了全局事务的强一致性,但因为一阶段长期持有数据库锁,性能极低,并发能力差,仅适用于短事务、强一致性要求极高的核心场景。

2.5 四大模式核心对比

模式 代码侵入性 一致性保证 性能表现 数据库依赖 核心适用场景
AT 零侵入 最终一致性 关系型数据库 绝大多数常规微服务业务场景
TCC 高侵入 最终一致性 无依赖 非关系型数据库、自定义事务逻辑场景
SAGA 中侵入 最终一致性 无依赖 长事务、复杂业务流程场景
XA 零侵入 强一致性 支持XA协议的数据库 短事务、强一致性核心场景

三、生产级落地实战

本文以经典的电商下单场景为例,基于AT模式实现完整的分布式事务落地,包含订单、库存、账户三个微服务,实现下单时库存扣减、余额扣减、订单创建的原子性操作。

3.1 环境与版本说明

  • 基础环境:JDK 17、MySQL 8.0、Maven 3.8+、Seata TC 2.2.0
  • 核心组件版本:Spring Boot 3.2.4、Spring Cloud 2023.0.1、Spring Cloud Alibaba 2023.0.1.0、MyBatis Plus 3.5.6、Fastjson2 2.0.52、Guava 33.1.0

3.2 数据库脚本

3.2.1 数据库创建

CREATE DATABASE IF NOT EXISTS seata_order DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE DATABASE IF NOT EXISTS seata_storage DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE DATABASE IF NOT EXISTS seata_account DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

3.2.2 UndoLog表创建(AT模式必须,每个业务库都需执行)

USE seata_order;
CREATE TABLE IF NOT EXISTS undo_log (
 id BIGINT NOT NULL AUTO_INCREMENT COMMENT 'increment id',
 branch_id BIGINT NOT NULL COMMENT 'branch transaction id',
 xid VARCHAR(100) 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 NOT NULL COMMENT '0:normal status, 1:defense status',
 log_created DATETIME NOT NULL COMMENT 'create datetime',
 log_modified DATETIME NOT NULL COMMENT 'modify datetime',
 PRIMARY KEY (id),
 UNIQUE KEY ux_undo_log (xid, branch_id),
 KEY ix_log_created (log_created)
) ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8mb4 COMMENT = 'AT transaction mode undo table';

USE seata_storage;
CREATE TABLE IF NOT EXISTS undo_log (
 id BIGINT NOT NULL AUTO_INCREMENT COMMENT 'increment id',
 branch_id BIGINT NOT NULL COMMENT 'branch transaction id',
 xid VARCHAR(100) 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 NOT NULL COMMENT '0:normal status, 1:defense status',
 log_created DATETIME NOT NULL COMMENT 'create datetime',
 log_modified DATETIME NOT NULL COMMENT 'modify datetime',
 PRIMARY KEY (id),
 UNIQUE KEY ux_undo_log (xid, branch_id),
 KEY ix_log_created (log_created)
) ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8mb4 COMMENT = 'AT transaction mode undo table';

USE seata_account;
CREATE TABLE IF NOT EXISTS undo_log (
 id BIGINT NOT NULL AUTO_INCREMENT COMMENT 'increment id',
 branch_id BIGINT NOT NULL COMMENT 'branch transaction id',
 xid VARCHAR(100) 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 NOT NULL COMMENT '0:normal status, 1:defense status',
 log_created DATETIME NOT NULL COMMENT 'create datetime',
 log_modified DATETIME NOT NULL COMMENT 'modify datetime',
 PRIMARY KEY (id),
 UNIQUE KEY ux_undo_log (xid, branch_id),
 KEY ix_log_created (log_created)
) ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8mb4 COMMENT = 'AT transaction mode undo table';

3.2.3 业务表创建与初始化

-- 订单表
USE seata_order;
CREATE TABLE IF NOT EXISTS t_order (
 id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT '订单ID',
 order_no VARCHAR(64) NOT NULL COMMENT '订单编号',
 user_id BIGINT NOT NULL COMMENT '用户ID',
 product_id BIGINT NOT NULL COMMENT '商品ID',
 count INT NOT NULL COMMENT '购买数量',
 amount DECIMAL(18,2) NOT NULL COMMENT '订单金额',
 status TINYINT NOT NULL DEFAULT 0 COMMENT '订单状态:0-待支付,1-已支付,2-已取消',
 create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
 update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
 UNIQUE KEY uk_order_no (order_no)
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COMMENT = '订单表';

-- 库存表
USE seata_storage;
CREATE TABLE IF NOT EXISTS t_storage (
 id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT '库存ID',
 product_id BIGINT NOT NULL COMMENT '商品ID',
 total INT NOT NULL DEFAULT 0 COMMENT '总库存',
 used INT NOT NULL DEFAULT 0 COMMENT '已用库存',
 residue INT NOT NULL DEFAULT 0 COMMENT '剩余库存',
 create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
 update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
 UNIQUE KEY uk_product_id (product_id)
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COMMENT = '库存表';
INSERT INTO t_storage (product_id, total, used, residue) VALUES (1, 100, 0, 100);

-- 账户表
USE seata_account;
CREATE TABLE IF NOT EXISTS t_account (
 id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT '账户ID',
 user_id BIGINT NOT NULL COMMENT '用户ID',
 balance DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '账户余额',
 frozen DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '冻结金额',
 create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
 update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
 UNIQUE KEY uk_user_id (user_id)
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COMMENT = '账户表';
INSERT INTO t_account (user_id, balance, frozen) VALUES (1, 1000.00, 0.00);

3.3 Maven工程结构

工程采用父子结构,父工程统一管理依赖版本,子模块分为三个业务服务:order-servicestorage-serviceaccount-service

3.3.1 父工程pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">

   <modelVersion>4.0.0</modelVersion>
   <parent>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-parent</artifactId>
       <version>3.2.4</version>
       <relativePath/>
   </parent>
   <groupId>com.jam.demo</groupId>
   <artifactId>seata-demo-parent</artifactId>
   <version>1.0.0</version>
   <packaging>pom</packaging>
   <modules>
       <module>order-service</module>
       <module>storage-service</module>
       <module>account-service</module>
   </modules>
   <properties>
       <java.version>17</java.version>
       <spring-cloud.version>2023.0.1</spring-cloud.version>
       <spring-cloud-alibaba.version>2023.0.1.0</spring-cloud-alibaba.version>
       <seata.version>2.2.0</seata.version>
       <mybatis-plus.version>3.5.6</mybatis-plus.version>
       <mysql.version>8.3.0</mysql.version>
       <lombok.version>1.18.30</lombok.version>
       <fastjson2.version>2.0.52</fastjson2.version>
       <guava.version>33.1.0-jre</guava.version>
       <springdoc.version>2.5.0</springdoc.version>
   </properties>
   <dependencyManagement>
       <dependencies>
           <dependency>
               <groupId>org.springframework.cloud</groupId>
               <artifactId>spring-cloud-dependencies</artifactId>
               <version>${spring-cloud.version}</version>
               <type>pom</type>
               <scope>import</scope>
           </dependency>
           <dependency>
               <groupId>com.alibaba.cloud</groupId>
               <artifactId>spring-cloud-alibaba-dependencies</artifactId>
               <version>${spring-cloud-alibaba.version}</version>
               <type>pom</type>
               <scope>import</scope>
           </dependency>
           <dependency>
               <groupId>io.seata</groupId>
               <artifactId>seata-spring-boot-starter</artifactId>
               <version>${seata.version}</version>
           </dependency>
           <dependency>
               <groupId>com.baomidou</groupId>
               <artifactId>mybatis-plus-boot-starter</artifactId>
               <version>${mybatis-plus.version}</version>
           </dependency>
           <dependency>
               <groupId>com.mysql</groupId>
               <artifactId>mysql-connector-j</artifactId>
               <version>${mysql.version}</version>
           </dependency>
           <dependency>
               <groupId>org.projectlombok</groupId>
               <artifactId>lombok</artifactId>
               <version>${lombok.version}</version>
               <scope>provided</scope>
           </dependency>
           <dependency>
               <groupId>com.alibaba.fastjson2</groupId>
               <artifactId>fastjson2</artifactId>
               <version>${fastjson2.version}</version>
           </dependency>
           <dependency>
               <groupId>com.google.guava</groupId>
               <artifactId>guava</artifactId>
               <version>${guava.version}</version>
           </dependency>
           <dependency>
               <groupId>org.springdoc</groupId>
               <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
               <version>${springdoc.version}</version>
           </dependency>
       </dependencies>
   </dependencyManagement>
   <build>
       <plugins>
           <plugin>
               <groupId>org.apache.maven.plugins</groupId>
               <artifactId>maven-compiler-plugin</artifactId>
               <version>3.13.0</version>
               <configuration>
                   <source>${java.version}</source>
                   <target>${java.version}</target>
               </configuration>
           </plugin>
       </plugins>
   </build>
</project>

3.3.2 子模块公共pom依赖(三个服务通用)

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">

   <modelVersion>4.0.0</modelVersion>
   <parent>
       <groupId>com.jam.demo</groupId>
       <artifactId>seata-demo-parent</artifactId>
       <version>1.0.0</version>
   </parent>
   <artifactId>order-service</artifactId>
   <dependencies>
       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-web</artifactId>
       </dependency>
       <dependency>
           <groupId>org.springframework.cloud</groupId>
           <artifactId>spring-cloud-starter-openfeign</artifactId>
       </dependency>
       <dependency>
           <groupId>io.seata</groupId>
           <artifactId>seata-spring-boot-starter</artifactId>
       </dependency>
       <dependency>
           <groupId>com.baomidou</groupId>
           <artifactId>mybatis-plus-boot-starter</artifactId>
       </dependency>
       <dependency>
           <groupId>com.mysql</groupId>
           <artifactId>mysql-connector-j</artifactId>
       </dependency>
       <dependency>
           <groupId>org.projectlombok</groupId>
           <artifactId>lombok</artifactId>
       </dependency>
       <dependency>
           <groupId>com.alibaba.fastjson2</groupId>
           <artifactId>fastjson2</artifactId>
       </dependency>
       <dependency>
           <groupId>com.google.guava</groupId>
           <artifactId>guava</artifactId>
       </dependency>
       <dependency>
           <groupId>org.springdoc</groupId>
           <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
       </dependency>
   </dependencies>
</project>

注:storage-serviceaccount-service的pom.xml仅需修改artifactId即可。

3.4 核心配置文件

3.4.1 订单服务application.yml

server:
 port: 8081
spring:
 application:
   name: order-service
 datasource:
   driver-class-name: com.mysql.cj.jdbc.Driver
   url: jdbc:mysql://127.0.0.1:3306/seata_order?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&allowMultiQueries=true
   username: root
   password: your_password
seata:
 service:
   vgroup-mapping:
     order_tx_group: default
 registry:
   type: file
 config:
   type: file
mybatis-plus:
 mapper-locations: classpath:mapper/*.xml
 configuration:
   map-underscore-to-camel-case: true
   log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
springdoc:
 swagger-ui:
   path: /swagger-ui.html
 api-docs:
   path: /v3/api-docs

3.4.2 库存服务application.yml

server:
 port: 8082
spring:
 application:
   name: storage-service
 datasource:
   driver-class-name: com.mysql.cj.jdbc.Driver
   url: jdbc:mysql://127.0.0.1:3306/seata_storage?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&allowMultiQueries=true
   username: root
   password: your_password
seata:
 service:
   vgroup-mapping:
     storage_tx_group: default
 registry:
   type: file
 config:
   type: file
mybatis-plus:
 mapper-locations: classpath:mapper/*.xml
 configuration:
   map-underscore-to-camel-case: true
   log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
springdoc:
 swagger-ui:
   path: /swagger-ui.html
 api-docs:
   path: /v3/api-docs

3.4.3 账户服务application.yml

server:
 port: 8083
spring:
 application:
   name: account-service
 datasource:
   driver-class-name: com.mysql.cj.jdbc.Driver
   url: jdbc:mysql://127.0.0.1:3306/seata_account?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&allowMultiQueries=true
   username: root
   password: your_password
seata:
 service:
   vgroup-mapping:
     account_tx_group: default
 registry:
   type: file
 config:
   type: file
mybatis-plus:
 mapper-locations: classpath:mapper/*.xml
 configuration:
   map-underscore-to-camel-case: true
   log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
springdoc:
 swagger-ui:
   path: /swagger-ui.html
 api-docs:
   path: /v3/api-docs

3.5 核心代码实现

3.5.1 库存服务核心代码

实体类

package com.jam.demo.storage.entity;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;

import java.math.BigDecimal;
import java.time.LocalDateTime;

/**
* 库存实体类
* @author ken
*/

@Data
@TableName("t_storage")
@Schema(description = "库存实体")
public class Storage {
   @TableId(type = IdType.AUTO)
   @Schema(description = "库存ID")
   private Long id;

   @Schema(description = "商品ID")
   private Long productId;

   @Schema(description = "总库存")
   private Integer total;

   @Schema(description = "已用库存")
   private Integer used;

   @Schema(description = "剩余库存")
   private Integer residue;

   @Schema(description = "创建时间")
   private LocalDateTime createTime;

   @Schema(description = "更新时间")
   private LocalDateTime updateTime;
}

Mapper接口

package com.jam.demo.storage.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.jam.demo.storage.entity.Storage;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Update;

/**
* 库存Mapper接口
* @author ken
*/

@Mapper
public interface StorageMapper extends BaseMapper<Storage> {

   /**
    * 扣减库存
    * @param productId 商品ID
    * @param count 扣减数量
    * @return 影响行数
    */

   @Update("UPDATE t_storage SET used = used + #{count}, residue = residue - #{count} WHERE product_id = #{productId} AND residue >= #{count}")
   int deductStorage(@Param("productId") Long productId, @Param("count") Integer count);
}

Service层

package com.jam.demo.storage.service;

import com.jam.demo.storage.mapper.StorageMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.util.ObjectUtils;

/**
* 库存服务
* @author ken
*/

@Slf4j
@Service
@RequiredArgsConstructor
public class StorageService {

   private final StorageMapper storageMapper;
   private final TransactionTemplate transactionTemplate;

   /**
    * 扣减商品库存
    * @param productId 商品ID
    * @param count 扣减数量
    * @return 扣减结果
    */

   public Boolean deductStorage(Long productId, Integer count) {
       if (ObjectUtils.isEmpty(productId) || ObjectUtils.isEmpty(count) || count <= 0) {
           log.error("库存扣减参数异常,productId:{}, count:{}", productId, count);
           return Boolean.FALSE;
       }
       return transactionTemplate.execute(new TransactionCallback<Boolean>() {
           @Override
           public Boolean doInTransaction(TransactionStatus status) {
               try {
                   int rows = storageMapper.deductStorage(productId, count);
                   if (rows <= 0) {
                       log.error("库存扣减失败,商品库存不足,productId:{}, count:{}", productId, count);
                       status.setRollbackOnly();
                       return Boolean.FALSE;
                   }
                   log.info("库存扣减成功,productId:{}, count:{}", productId, count);
                   return Boolean.TRUE;
               } catch (Exception e) {
                   log.error("库存扣减异常,productId:{}, count:{}", productId, count, e);
                   status.setRollbackOnly();
                   return Boolean.FALSE;
               }
           }
       });
   }
}

Controller层

package com.jam.demo.storage.controller;

import com.jam.demo.storage.service.StorageService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

/**
* 库存控制器
* @author ken
*/

@RestController
@RequestMapping("/storage")
@RequiredArgsConstructor
@Tag(name = "库存管理", description = "库存相关操作接口")
public class StorageController {

   private final StorageService storageService;

   @PostMapping("/deduct")
   @Operation(summary = "扣减库存", description = "根据商品ID扣减对应数量的库存")
   public Boolean deductStorage(
           @Parameter(description = "商品ID", required = true)
@RequestParam Long productId,
           @Parameter(description = "扣减数量", required = true) @RequestParam Integer count) {
       return storageService.deductStorage(productId, count);
   }
}

启动类

package com.jam.demo.storage;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
* 库存服务启动类
* @author ken
*/

@SpringBootApplication
@MapperScan("com.jam.demo.storage.mapper")
public class StorageApplication {
   public static void main(String[] args) {
       SpringApplication.run(StorageApplication.class, args);
   }
}

3.5.2 账户服务核心代码

实体类

package com.jam.demo.account.entity;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;

import java.math.BigDecimal;
import java.time.LocalDateTime;

/**
* 账户实体类
* @author ken
*/

@Data
@TableName("t_account")
@Schema(description = "账户实体")
public class Account {
   @TableId(type = IdType.AUTO)
   @Schema(description = "账户ID")
   private Long id;

   @Schema(description = "用户ID")
   private Long userId;

   @Schema(description = "账户余额")
   private BigDecimal balance;

   @Schema(description = "冻结金额")
   private BigDecimal frozen;

   @Schema(description = "创建时间")
   private LocalDateTime createTime;

   @Schema(description = "更新时间")
   private LocalDateTime updateTime;
}

Mapper接口

package com.jam.demo.account.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.jam.demo.account.entity.Account;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Update;

import java.math.BigDecimal;

/**
* 账户Mapper接口
* @author ken
*/

@Mapper
public interface AccountMapper extends BaseMapper<Account> {

   /**
    * 扣减账户余额
    * @param userId 用户ID
    * @param amount 扣减金额
    * @return 影响行数
    */

   @Update("UPDATE t_account SET balance = balance - #{amount} WHERE user_id = #{userId} AND balance >= #{amount}")
   int deductBalance(@Param("userId") Long userId, @Param("amount") BigDecimal amount);
}

Service层

package com.jam.demo.account.service;

import com.jam.demo.account.mapper.AccountMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.util.ObjectUtils;

import java.math.BigDecimal;

/**
* 账户服务
* @author ken
*/

@Slf4j
@Service
@RequiredArgsConstructor
public class AccountService {

   private final AccountMapper accountMapper;
   private final TransactionTemplate transactionTemplate;

   /**
    * 扣减账户余额
    * @param userId 用户ID
    * @param amount 扣减金额
    * @return 扣减结果
    */

   public Boolean deductBalance(Long userId, BigDecimal amount) {
       if (ObjectUtils.isEmpty(userId) || ObjectUtils.isEmpty(amount) || amount.compareTo(BigDecimal.ZERO) <= 0) {
           log.error("余额扣减参数异常,userId:{}, amount:{}", userId, amount);
           return Boolean.FALSE;
       }
       return transactionTemplate.execute(new TransactionCallback<Boolean>() {
           @Override
           public Boolean doInTransaction(TransactionStatus status) {
               try {
                   int rows = accountMapper.deductBalance(userId, amount);
                   if (rows <= 0) {
                       log.error("余额扣减失败,账户余额不足,userId:{}, amount:{}", userId, amount);
                       status.setRollbackOnly();
                       return Boolean.FALSE;
                   }
                   log.info("余额扣减成功,userId:{}, amount:{}", userId, amount);
                   return Boolean.TRUE;
               } catch (Exception e) {
                   log.error("余额扣减异常,userId:{}, amount:{}", userId, amount, e);
                   status.setRollbackOnly();
                   return Boolean.FALSE;
               }
           }
       });
   }
}

Controller层

package com.jam.demo.account.controller;

import com.jam.demo.account.service.AccountService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.math.BigDecimal;

/**
* 账户控制器
* @author ken
*/

@RestController
@RequestMapping("/account")
@RequiredArgsConstructor
@Tag(name = "账户管理", description = "账户相关操作接口")
public class AccountController {

   private final AccountService accountService;

   @PostMapping("/deduct")
   @Operation(summary = "扣减账户余额", description = "根据用户ID扣减对应金额的账户余额")
   public Boolean deductBalance(
           @Parameter(description = "用户ID", required = true)
@RequestParam Long userId,
           @Parameter(description = "扣减金额", required = true) @RequestParam BigDecimal amount) {
       return accountService.deductBalance(userId, amount);
   }
}

启动类

package com.jam.demo.account;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
* 账户服务启动类
* @author ken
*/

@SpringBootApplication
@MapperScan("com.jam.demo.account.mapper")
public class AccountApplication {
   public static void main(String[] args) {
       SpringApplication.run(AccountApplication.class, args);
   }
}

3.5.3 订单服务核心代码

实体类

package com.jam.demo.order.entity;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;

import java.math.BigDecimal;
import java.time.LocalDateTime;

/**
* 订单实体类
* @author ken
*/

@Data
@TableName("t_order")
@Schema(description = "订单实体")
public class Order {
   @TableId(type = IdType.AUTO)
   @Schema(description = "订单ID")
   private Long id;

   @Schema(description = "订单编号")
   private String orderNo;

   @Schema(description = "用户ID")
   private Long userId;

   @Schema(description = "商品ID")
   private Long productId;

   @Schema(description = "购买数量")
   private Integer count;

   @Schema(description = "订单金额")
   private BigDecimal amount;

   @Schema(description = "订单状态:0-待支付,1-已支付,2-已取消")
   private Integer status;

   @Schema(description = "创建时间")
   private LocalDateTime createTime;

   @Schema(description = "更新时间")
   private LocalDateTime updateTime;
}

Mapper接口

package com.jam.demo.order.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.jam.demo.order.entity.Order;
import org.apache.ibatis.annotations.Mapper;

/**
* 订单Mapper接口
* @author ken
*/

@Mapper
public interface OrderMapper extends BaseMapper<Order> {
}

Feign客户端

package com.jam.demo.order.feign;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;

import java.math.BigDecimal;

/**
* 库存服务Feign客户端
* @author ken
*/

@FeignClient(name = "storage-service", url = "http://127.0.0.1:8082")
public interface StorageFeignClient {

   @PostMapping("/storage/deduct")
   Boolean deductStorage(@RequestParam("productId") Long productId, @RequestParam("count") Integer count);
}

package com.jam.demo.order.feign;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;

import java.math.BigDecimal;

/**
* 账户服务Feign客户端
* @author ken
*/

@FeignClient(name = "account-service", url = "http://127.0.0.1:8083")
public interface AccountFeignClient {

   @PostMapping("/account/deduct")
   Boolean deductBalance(@RequestParam("userId") Long userId, @RequestParam("amount") BigDecimal amount);
}

Service层

package com.jam.demo.order.service;

import com.jam.demo.order.entity.Order;
import com.jam.demo.order.feign.AccountFeignClient;
import com.jam.demo.order.feign.StorageFeignClient;
import com.jam.demo.order.mapper.OrderMapper;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.util.UUID;

/**
* 订单服务
* @author ken
*/

@Slf4j
@Service
@RequiredArgsConstructor
public class OrderService {

   private final OrderMapper orderMapper;
   private final StorageFeignClient storageFeignClient;
   private final AccountFeignClient accountFeignClient;
   private final TransactionTemplate transactionTemplate;
   private static final BigDecimal UNIT_PRICE = new BigDecimal("10");

   /**
    * 创建订单
    * @param userId 用户ID
    * @param productId 商品ID
    * @param count 购买数量
    * @return 订单创建结果
    */

   @GlobalTransactional(name = "order-create-tx", rollbackFor = Exception.class)
   public Boolean createOrder(Long userId, Long productId, Integer count)
{
       if (ObjectUtils.isEmpty(userId) || ObjectUtils.isEmpty(productId) || ObjectUtils.isEmpty(count) || count <= 0) {
           log.error("订单创建参数异常,userId:{}, productId:{}, count:{}", userId, productId, count);
           return Boolean.FALSE;
       }
       // 1. 计算订单金额
       BigDecimal orderAmount = UNIT_PRICE.multiply(new BigDecimal(count));
       log.info("开始创建订单,userId:{}, productId:{}, count:{}, orderAmount:{}", userId, productId, count, orderAmount);

       // 2. 远程调用库存服务扣减库存
       Boolean storageResult = storageFeignClient.deductStorage(productId, count);
       if (!storageResult) {
           log.error("库存扣减失败,终止订单创建");
           throw new RuntimeException("库存扣减失败");
       }

       // 3. 远程调用账户服务扣减余额
       Boolean accountResult = accountFeignClient.deductBalance(userId, orderAmount);
       if (!accountResult) {
           log.error("余额扣减失败,终止订单创建");
           throw new RuntimeException("余额扣减失败");
       }

       // 4. 本地事务创建订单
       return transactionTemplate.execute(new TransactionCallback<Boolean>() {
           @Override
           public Boolean doInTransaction(TransactionStatus status) {
               try {
                   Order order = new Order();
                   order.setOrderNo(UUID.randomUUID().toString().replace("-", ""));
                   order.setUserId(userId);
                   order.setProductId(productId);
                   order.setCount(count);
                   order.setAmount(orderAmount);
                   order.setStatus(0);
                   int rows = orderMapper.insert(order);
                   if (rows <= 0) {
                       log.error("订单创建失败,数据插入异常");
                       status.setRollbackOnly();
                       throw new RuntimeException("订单创建失败");
                   }
                   log.info("订单创建成功,orderNo:{}", order.getOrderNo());
                   return Boolean.TRUE;
               } catch (Exception e) {
                   log.error("订单创建异常", e);
                   status.setRollbackOnly();
                   throw e;
               }
           }
       });
   }
}

Controller层

package com.jam.demo.order.controller;

import com.jam.demo.order.service.OrderService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

/**
* 订单控制器
* @author ken
*/

@RestController
@RequestMapping("/order")
@RequiredArgsConstructor
@Tag(name = "订单管理", description = "订单相关操作接口")
public class OrderController {

   private final OrderService orderService;

   @PostMapping("/create")
   @Operation(summary = "创建订单", description = "创建订单并完成库存扣减与余额扣减的分布式事务")
   public Boolean createOrder(
           @Parameter(description = "用户ID", required = true)
@RequestParam Long userId,
           @Parameter(description = "商品ID", required = true) @RequestParam Long productId,
           @Parameter(description = "购买数量", required = true) @RequestParam Integer count) {
       return orderService.createOrder(userId, productId, count);
   }
}

启动类

package com.jam.demo.order;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;

/**
* 订单服务启动类
* @author ken
*/

@SpringBootApplication
@MapperScan("com.jam.demo.order.mapper")
@EnableFeignClients
public class OrderApplication {
   public static void main(String[] args) {
       SpringApplication.run(OrderApplication.class, args);
   }
}


四、生产环境全链路避坑指南

4.1 TC服务端高可用避坑

  1. 集群部署强制要求:生产环境严禁使用单点TC,必须采用集群部署,通过注册中心(Nacos)实现集群节点的发现与负载均衡;TC的存储模式必须使用DB模式(MySQL),严禁使用File模式,File模式仅适用于本地测试,无法实现集群数据同步与持久化。
  2. 事务分组隔离:生产环境必须按业务集群、环境划分独立的事务分组,避免测试环境与生产环境、不同业务线的TC集群混用,防止事务请求路由错误导致的事务失效。
  3. 核心参数调优
  • globalLockTimeout:全局锁超时时间,高并发场景建议调整为3000-5000ms,避免锁等待时间过长导致的吞吐量下降,同时防止锁超时导致的事务回滚。
  • maxCommitRetryTimeoutmaxRollbackRetryTimeout:提交/回滚最大重试时间,建议设置为86400000ms(24小时),保证异常场景下TC有足够的时间重试,避免事务悬挂。
  • store.db.maxConn:TC数据库连接池最大连接数,根据集群规模调整,建议设置为100-200,避免连接池耗尽导致TC无法处理事务请求。

4.2 AT模式核心避坑

  1. 数据源代理必须正确:AT模式的核心是数据源代理,Spring Boot 3.x中Seata会自动代理数据源,但若业务代码中自定义了DataSource Bean,必须手动配置Seata的DataSourceProxy,否则无法拦截SQL生成undo_log,导致分布式事务完全失效。
  2. 业务表主键强制要求:业务表必须有显式的主键,严禁使用无主键表,否则Seata无法准确定位数据行,无法生成正确的前镜像与后镜像,最终导致回滚失败、数据不一致;不建议使用联合主键,联合主键会增加镜像生成的复杂度,容易出现解析异常。
  3. undo_log表运维:undo_log表必须设置定时清理任务,生产环境建议每日清理7天以上的已完成事务的undo_log数据,避免表数据量过大导致的查询、回滚性能下降,甚至数据库磁盘空间耗尽;必须给undo_log表的log_created字段建立索引,提升清理任务的执行效率。
  4. 脏写问题规避:严禁通过非Seata代理的数据源操作AT模式管理的业务表,否则会绕过Seata的全局锁检查,直接修改数据库数据,导致脏写、数据不一致;即使是数据订正、离线任务,也必须通过Seata代理的数据源执行操作。
  5. SQL支持限制:AT模式不支持动态表名、存储过程、多表关联更新、INSERT INTO ... SELECT等复杂SQL,此类SQL无法被Seata正确解析,会导致undo_log生成失败,事务回滚异常;生产环境使用前必须对业务SQL进行充分测试。
  6. 全局锁粒度优化:严禁大事务、长事务,全局锁的持有时间越长,并发性能越差,死锁概率越高;建议将非事务性操作(如参数校验、非核心查询、文件操作)提前到全局事务之外执行,缩小全局事务的范围,减少全局锁的持有时间。

4.3 TCC模式核心避坑

  1. 三大核心问题必须处理
  • 幂等性:Confirm和Cancel阶段会被TC重复调用,必须通过事务控制表记录分支事务的执行状态,重复调用时直接返回,严禁重复执行业务逻辑,否则会导致数据重复扣减、重复补偿。
  • 空回滚:Cancel阶段在Try阶段未执行的情况下被调用,必须在事务控制表中校验,若没有Try阶段的执行记录,直接返回成功,不执行回滚逻辑,避免抛出异常导致TC无限重试。
  • 事务悬挂:Cancel阶段比Try阶段先执行,导致Try阶段预留的资源永远无法释放;解决方案是Cancel阶段执行时,若没有Try阶段的记录,插入一条状态为已回滚的记录,Try阶段执行时,若发现已存在回滚记录,直接不执行资源预留逻辑。
  1. 三个阶段事务隔离:Try、Confirm、Cancel三个阶段必须使用独立的本地事务,严禁将多个阶段的逻辑放在同一个事务中,否则会导致事务状态异常。
  2. 异常处理规范:Try阶段业务校验失败时,必须抛出明确的业务异常,严禁返回null或false,否则TC会认为分支事务执行成功,不会触发Cancel阶段,导致数据不一致。

4.4 微服务调用链路避坑

  1. XID传播必须保证:Seata的XID必须在微服务调用链路中正确传播,Feign、Dubbo框架Seata已提供自动适配,但若使用自定义RPC框架、跨线程池执行任务,必须手动传递XID,通过请求头、ThreadLocal传递,否则分支事务无法加入全局事务,导致事务失效。
  2. 全局事务超时设置:全局事务的超时时间必须大于所有分支事务的总执行时间,包括服务调用的超时时间、数据库执行时间,否则会出现分支事务还在执行,TC已经发起全局回滚,最终导致数据不一致。
  3. 异常处理规范
  • @GlobalTransactional注解必须指定rollbackFor = Exception.class,否则非RuntimeException不会触发全局回滚。
  • 业务代码中严禁捕获异常后不抛出,必须将异常向上抛出,否则Seata无法感知业务异常,不会触发全局回滚,导致数据不一致。
  • 分支事务执行失败时,必须抛出RuntimeException,严禁返回错误码,否则Seata会认为分支事务执行成功,不会触发全局回滚。

4.5 版本与运维避坑

  1. 版本兼容要求:Seata客户端与TC服务端的大版本必须保持一致,严禁跨大版本混用,否则会出现协议不兼容、事务请求解析失败、事务状态异常等问题,甚至导致数据丢失。
  2. 监控体系搭建:生产环境必须搭建Seata监控体系,核心监控指标包括:全局事务提交率、回滚率、超时率、分支事务平均执行时间、TC节点CPU/内存/连接数、undo_log表数据量;通过监控及时发现异常事务、性能瓶颈,避免故障扩大。
  3. 死事务处理:生产环境会出现悬挂的死事务(如TC宕机、网络中断导致的事务状态未知),必须定期扫描TC的全局事务表,处理超过24小时的未完成事务,根据分支事务的执行状态,手动发起提交或回滚,避免全局锁长期持有、数据长期冻结。
  4. 灰度发布规范:Seata相关的代码变更、版本升级,必须进行灰度发布,先在小范围节点验证,确认事务执行正常后再全量发布,避免全量发布后出现大规模事务失效问题。

五、总结

Seata作为轻量级分布式事务框架,通过清晰的角色划分、灵活的事务模式,解决了微服务架构下的分布式事务核心痛点。AT模式实现了业务代码零侵入,大幅降低了分布式事务的接入成本;TCC、SAGA、XA模式则覆盖了更多特殊的业务场景,满足不同的一致性与性能需求。

生产环境落地Seata的核心,不仅是完成代码接入,更重要的是理解其底层的两阶段提交、全局锁、undo_log机制,提前规避本文梳理的各类坑点,做好TC集群高可用、参数调优、监控运维、事务异常处理等工作,才能保证分布式事务的稳定运行,最终实现业务数据的一致性。

目录
相关文章
|
10天前
|
人工智能 安全 Linux
【OpenClaw保姆级图文教程】阿里云/本地部署集成模型Ollama/Qwen3.5/百炼 API 步骤流程及避坑指南
2026年,AI代理工具的部署逻辑已从“单一云端依赖”转向“云端+本地双轨模式”。OpenClaw(曾用名Clawdbot)作为开源AI代理框架,既支持对接阿里云百炼等云端免费API,也能通过Ollama部署本地大模型,完美解决两类核心需求:一是担心云端API泄露核心数据的隐私安全诉求;二是频繁调用导致token消耗过高的成本控制需求。
5495 13
|
18天前
|
人工智能 JavaScript Ubuntu
5分钟上手龙虾AI!OpenClaw部署(阿里云+本地)+ 免费多模型配置保姆级教程(MiniMax、Claude、阿里云百炼)
OpenClaw(昵称“龙虾AI”)作为2026年热门的开源个人AI助手,由PSPDFKit创始人Peter Steinberger开发,核心优势在于“真正执行任务”——不仅能聊天互动,还能自动处理邮件、管理日程、订机票、写代码等,且所有数据本地处理,隐私完全可控。它支持接入MiniMax、Claude、GPT等多类大模型,兼容微信、Telegram、飞书等主流聊天工具,搭配100+可扩展技能,成为兼顾实用性与隐私性的AI工具首选。
21829 117
|
14天前
|
人工智能 安全 前端开发
Team 版 OpenClaw:HiClaw 开源,5 分钟完成本地安装
HiClaw 基于 OpenClaw、Higress AI Gateway、Element IM 客户端+Tuwunel IM 服务器(均基于 Matrix 实时通信协议)、MinIO 共享文件系统打造。
8304 8

热门文章

最新文章