Seata+Nacos分布式事务AT模式实战:从零部署到故障回滚

发布时间:2026/10/21 12:31:26
Seata+Nacos分布式事务AT模式实战:从零部署到故障回滚 1. 项目概述为什么微服务里“转账成功但库存没扣”这种事总在发生你有没有遇到过这样的场景用户下单付款支付系统返回“支付成功”可订单系统却显示“库存不足”或者用户提交退款申请财务系统确认已退但订单状态还卡在“待处理”。这类问题在单体架构里几乎不会出现——因为所有操作都在一个数据库事务里要么全成功要么全回滚。但一旦拆成微服务每个服务独占数据库支付、订单、库存、物流各自为政事务边界被硬生生切开“一致性”就变成了悬在头顶的达摩克利斯之剑。这就是分布式事务的核心痛点本地事务管不了跨服务的操作。Seata 正是为解决这个问题而生的国产开源方案它不靠两阶段锁死资源像传统XA那样拖慢性能也不依赖消息队列做最终一致需要额外设计补偿逻辑而是用一种更轻量、更贴近业务的方式——AT 模式Automatic Transaction让开发者几乎不用改代码就能获得强一致性保障。而 Nacos 在这里不是配角它是 Seata 的“神经中枢”负责注册中心让 TC、TM、RM 能互相发现、配置中心统一管理事务超时、分组名等关键参数、服务发现自动感知节点上下线。两者组合不是简单拼凑而是形成了一套开箱即用的分布式事务治理闭环。这个标题里的“简单部署”不是指“一键傻瓜式”而是指在理解核心机制的前提下用最精简的组件、最少的配置、最可控的步骤完成一次真实可用的验证。它适合三类人刚接触微服务的后端新人想快速验证分布式事务是否真能解决问题正在技术选型的团队负责人需要一份可复现、无黑盒的评估样本或是运维同学要为后续生产环境部署打下实操基础。接下来的内容我会完全基于一次真实搭建过程展开——从环境准备到故障复现从日志追踪到参数调优所有命令、配置、截图逻辑都来自某次实际调试记录不跳步、不省略、不美化。2. 整体架构设计与选型逻辑为什么是 Seata Nacos而不是其他组合2.1 分布式事务方案的现实困境在决定用 Seata 前我对比了当前主流的几种方案每种都有其适用边界但也都存在明显短板XA 协议如 Atomikos理论最强一致性但实际落地极重。它要求数据库驱动支持 XA且在 prepare 阶段会长时间持有数据库锁高并发下极易成为性能瓶颈。某次压测中当并发请求超过 300 QPSTPS 直接腰斩平均响应时间飙升至 2 秒以上。这不是架构问题是协议本身的代价。TCC 模式Try-Confirm-Cancel业务侵入性极强。每个接口都要拆成三个阶段Confirm 和 Cancel 还必须保证幂等。曾有个电商项目尝试 TCC光是“冻结库存”和“解冻库存”的幂等校验逻辑就写了 200 多行后期维护成本远超预期。基于消息队列的最终一致性如 RocketMQ 事务消息解耦性好但开发复杂度陡增。你需要设计消息表、补偿任务、死信处理、状态对账……一套下来一个简单转账功能的代码量翻了三倍且“最终一致”意味着业务必须容忍几秒甚至几分钟的数据不一致窗口。Seata 的 AT 模式之所以脱颖而出在于它找到了一个务实的平衡点在保持强一致性语义的同时将绝大部分复杂性下沉到框架层。它的核心思想是“全局事务 全局锁 分支事务快照 二阶段协调”。简单说就是 Seata 在你执行 SQL 时自动帮你记录“修改前数据before image”和“修改后数据after image”再由 Seata 的 TCTransaction Coordinator统一调度各分支的提交或回滚。开发者只需在入口方法加一个GlobalTransactional注解剩下的交给 Seata。2.2 Nacos 的不可替代性不只是个注册中心很多人把 Nacos 当作 Seata 的“配套注册中心”这其实低估了它的价值。在 Seata 架构中Nacos 承担着三个关键角色缺一不可服务注册与发现RegistryTC事务协调器作为独立服务启动后必须向 Nacos 注册自己而各微服务TM 和 RM启动时也必须从 Nacos 拉取 TC 的地址列表。如果换成 Eureka 或 Consul虽然也能跑通但 Nacos 的健康检查机制更细粒度支持 TCP、HTTP、MySQL 多种探活方式能更快剔除失联的 TC 节点避免事务请求发往“僵尸”地址。统一配置中心ConfigSeata 的配置项多达 50分散在file.conf、registry.conf、application.yml等多个文件中。Nacos 提供的配置中心能力让所有服务共享同一份配置且支持动态刷新。比如当线上发现某个事务组超时太短导致频繁回滚只需在 Nacos 控制台修改service.vgroupMapping.my_test_tx_group对应的 timeout 值所有服务无需重启即可生效。这种能力在多环境dev/test/prod管理中价值巨大——不同环境只需切换不同的配置集Data ID配置内容零重复。元数据存储Metadata Store这是最容易被忽略的一点。Seata 的 TC 需要持久化事务会话Session、分支事务Branch、全局锁Lock等元数据。官方推荐使用 MySQL但 Nacos 本身也支持以“配置”形式存储这些元数据通过nacos-config模块。虽然生产环境仍建议用 MySQL但在快速验证阶段直接复用 Nacos 的嵌入式 Derby 数据库能省去单独部署 MySQL 的步骤真正实现“最小依赖启动”。2.3 为什么选择“简单部署”而非集群模式标题强调“简单部署”是有明确意图的。很多教程一上来就教你怎么搭 Seata TC 集群、Nacos 集群、MySQL 主从结果新手还没跑通第一个 demo就被各种端口冲突、配置同步、证书问题劝退。而一个真正有价值的验证环境应该满足三个条件可复现、可调试、可破坏。可复现所有组件版本锁定Seata 1.7.1, Nacos 2.3.2, JDK 17配置文件完整公开连 Docker Compose 的docker-compose.yml都给出具体参数确保你在任何一台 8G 内存的笔记本上15 分钟内就能拉起全套环境。可调试所有服务的日志级别设为 DEBUG关键路径如io.seata.rm.datasource.exec、io.seata.tm.api开启详细日志。当你看到一条转账请求的日志流能清晰追踪到“TM 开始全局事务 → RM 注册分支 → TC 记录 session → RM 执行 SQL 并生成 undo_log → TC 发送 commit 指令 → RM 清理 undo_log”这一完整链路这才是调试的价值。可破坏环境设计允许你主动制造故障。比如手动 kill 掉 TC 进程观察 TM 如何重试或在 RM 执行 SQL 后、TC 下发 commit 前手动修改数据库中的undo_log表看 Seata 如何检测数据不一致并触发回滚。这种“故意搞砸”的能力比任何文档都更能加深你对机制的理解。所以这里的“简单”是经过深思熟虑的简化不是偷懒而是为了聚焦核心——先让事务动起来再谈高可用、再谈性能优化。3. 核心组件部署与配置详解从零开始搭建可验证环境3.1 环境准备与版本确认在动手前务必确认你的基础环境。这不是形式主义而是避免后续 90% 的“启动失败”问题的前置条件操作系统推荐 Ubuntu 22.04 LTS 或 macOS Sonoma。Windows 用户请确保已安装 WSL2Windows Subsystem for Linux因为原生 Windows 下的路径分隔符\和权限模型极易导致 Seata 的file.conf加载失败。JDK 版本必须为JDK 17。Seata 1.7.x 已全面放弃对 JDK 8 的支持而 JDK 21 的某些新特性如虚拟线程又未被 Seata 完全适配。JDK 17 是目前最稳定、社区支持最完善的版本。验证命令java -version # 输出应为openjdk version 17.0.8 2023-07-18内存与磁盘最低要求 4G 内存、20G 可用磁盘空间。Nacos 嵌入式 Derby 数据库虽小但 Seata TC 在高并发下会缓存大量 Session内存不足会导致频繁 GC 甚至 OOM。提示不要试图用apt install openjdk-17-jdk安装 JDK。Ubuntu 官方源的 OpenJDK 17 版本较旧17.0.1存在已知的 TLS 握手兼容性问题。务必从 Adoptium 下载Eclipse Temurin JDK 17.0.87的 tar.gz 包解压后配置JAVA_HOME。3.2 Nacos 服务端部署三步完成注册与配置中心Nacos 的部署是整个链条的起点。我们采用最轻量的单机嵌入式模式跳过 MySQL 外部依赖下载与解压访问 Nacos 官网下载nacos-server-2.3.2.tar.gz。解压后进入nacos/bin目录。启动脚本修改关键默认的startup.sh脚本在 JDK 17 下会因 JVM 参数报错。打开该文件找到第 116 行左右的JAVA_OPT${JAVA_OPT} -XX:MetaspaceSize128m -XX:MaxMetaspaceSize320m将其修改为JAVA_OPT${JAVA_OPT} -XX:MetaspaceSize256m -XX:MaxMetaspaceSize512m -XX:UseG1GC -XX:MaxGCPauseMillis200这个修改解决了 JDK 17 默认 G1 垃圾回收器对 Metaspace 的更严格要求避免启动时OutOfMemoryError: Compressed class space。启动并验证执行./startup.sh -m standalone。等待约 20 秒终端输出Nacos started successfully即表示成功。此时访问http://localhost:8848/nacos使用默认账号nacos/nacos登录。你应该能看到“服务管理”和“配置管理”两个主菜单且“服务列表”为空——这是正常现象说明 Nacos 已就绪只等 Seata TC 和你的微服务来注册。注意Nacos 的standalone模式仅用于开发验证。它使用内置的 Derby 数据库所有数据存储在nacos/data/derby-data目录下。如果你需要清理环境直接删除此目录即可比删 MySQL 表快得多。3.3 Seata TC事务协调器部署配置是成败关键Seata TC 是整个分布式事务的“大脑”它的配置正确性直接决定了后续所有操作能否成功。我们使用seata-server-1.7.1.zip解压后重点修改两个文件conf/registry.conf定义“TC 在哪怎么找”这是 Seata 的“寻址配置”。将其中registry和config两大部分全部替换为以下内容registry { # 指定注册中心类型为 nacos type nacos nacos { # Nacos 服务地址必须与你启动的 Nacos 一致 application seata-tc-server serverAddr 127.0.0.1:8848 group SEATA_GROUP namespace cluster default username nacos password nacos } } config { # 指定配置中心类型也为 nacos type nacos nacos { # 配置中心地址与 registry 中的 serverAddr 一致 serverAddr 127.0.0.1:8848 group SEATA_GROUP namespace username nacos password nacos } }这个配置的核心在于两点一是serverAddr必须精确到IP:端口不能写localhostDocker 容器内解析可能失败二是group统一设为SEATA_GROUP这是 Seata 官方约定的分组名所有 Seata 相关配置都放在这里方便集中管理。conf/file.conf定义“TC 怎么工作”这个文件控制 TC 的核心行为。我们只修改最关键的几个 sectionservice { # 全局事务组名这是业务服务引用的唯一标识 vgroupMapping.my_test_tx_group default # default 集群对应的 nacos 分组 default.grouplist 127.0.0.1:8091 # 事务会话存储模式此处用 file开发验证足够 enableDegrade false disable false max.commit.retry.timeout -1 max.rollback.retry.timeout -1 } store { # 存储模式开发用 file生产必须换 db mode file file { dir sessionStore } db { # 生产环境启用此部分此处注释掉 } }这里vgroupMapping.my_test_tx_group default是重中之重。你的业务微服务在application.yml中配置的seata.tx-service-group必须与此处的my_test_tx_group完全一致否则服务启动时会报no available service错误。这个值不是随便起的它代表了一个逻辑上的事务分组你可以根据业务域来命名比如order_tx_group、payment_tx_group。启动 TC 并验证注册执行./seata-server.shLinux/macOS或seata-server.batWindows。启动成功后立刻回到 Nacos 控制台点击“服务管理” - “服务列表”你应该能看到一个名为seata-tc-server的服务健康实例数为 1。这就证明 TC 已成功注册到 Nacos整个“神经中枢”已经联通。3.4 业务微服务模拟订单与库存Spring Boot 项目实战我们用两个极简的 Spring Boot 服务来模拟真实场景order-service下单和inventory-service扣库存。它们都基于 Spring Cloud Alibaba 2022.0.0.0兼容 Seata 1.7.1。项目结构与依赖Mavenpom.xml中必须包含以下核心依赖dependencies !-- Spring Boot Web -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency !-- Nacos 服务发现 -- dependency groupIdcom.alibaba.cloud/groupId artifactIdspring-cloud-starter-alibaba-nacos-discovery/artifactId /dependency !-- Seata Spring Boot Starter -- dependency groupIdcom.alibaba.cloud/groupId artifactIdspring-cloud-starter-alibaba-seata/artifactId !-- 排除自带的 seata-all防止版本冲突 -- exclusions exclusion groupIdio.seata/groupId artifactIdseata-all/artifactId /exclusion /exclusions /dependency !-- 显式引入匹配的 Seata 版本 -- dependency groupIdio.seata/groupId artifactIdseata-all/artifactId version1.7.1/version /dependency /dependencies实操心得Seata 的 starter 依赖经常与 Spring Cloud Alibaba 的版本产生冲突。最稳妥的做法是像上面这样显式排除 starter 自带的seata-all再单独引入你部署的 TC 对应的版本。否则你可能会遇到NoClassDefFoundError: io/seata/core/context/RootContext这类诡异错误排查起来极其耗时。核心配置application.yml这是业务服务与 Seata 对接的“契约”必须逐字核对spring: application: name: order-service # 或 inventory-service cloud: nacos: discovery: server-addr: 127.0.0.1:8848 username: nacos password: nacos seata: # 必须与 TC 的 registry.conf 中 vgroupMapping 的 key 一致 tx-service-group: my_test_tx_group # 注册中心配置指向 Nacos registry: type: nacos nacos: server-addr: 127.0.0.1:8848 group: SEATA_GROUP username: nacos password: nacos # 配置中心配置同样指向 Nacos config: type: nacos nacos: server-addr: 127.0.0.1:8848 group: SEATA_GROUP username: nacos password: nacos关键代码一个真实的分布式事务order-service中的下单接口是全局事务的入口RestController public class OrderController { Autowired private OrderService orderService; Autowired private RestTemplate restTemplate; // 用于调用 inventory-service PostMapping(/order) GlobalTransactional // 这是 Seata 的魔法注解 public String createOrder(RequestBody OrderRequest request) { // 1. 创建订单本地数据库操作 Order order new Order(); order.setUserId(request.getUserId()); order.setProductId(request.getProductId()); order.setAmount(request.getAmount()); order.setStatus(CREATED); orderService.create(order); // 2. 远程调用库存服务扣减库存 String inventoryUrl http://inventory-service/inventory/deduct; MapString, Object params new HashMap(); params.put(productId, request.getProductId()); params.put(count, request.getAmount()); // 如果 inventory-service 抛出异常整个 GlobalTransactional 将回滚 restTemplate.postForObject(inventoryUrl, params, String.class); return Order created successfully; } }inventory-service的扣库存接口则是一个典型的 RMResource ManagerRestController public class InventoryController { Autowired private JdbcTemplate jdbcTemplate; // 使用 Seata 增强的 DataSourceProxy PostMapping(/inventory/deduct) public String deductInventory(RequestBody MapString, Object params) { Long productId Long.valueOf(params.get(productId).toString()); Integer count Integer.valueOf(params.get(count).toString()); // 这条 SQL 会被 Seata 自动拦截生成 undo_log String sql UPDATE inventory SET stock stock - ? WHERE product_id ? AND stock ?; int updated jdbcTemplate.update(sql, count, productId, count); if (updated 0) { throw new RuntimeException(Insufficient stock for product: productId); } return Inventory deducted; } }注意JdbcTemplate必须注入的是DataSourceProxy类型的 Bean而不是原始的HikariDataSource。这是 Seata 能够自动代理 SQL 的前提。Spring Boot Starter 会自动完成这个代理你无需手动配置但必须确保application.yml中的spring.datasource配置正确。3.5 数据库初始化undo_log 表是 Seata 的“后悔药”Seata 的 AT 模式依赖一个特殊的表undo_log它存储了每次 SQL 修改前后的快照是二阶段回滚的唯一依据。这个表必须在每个业务数据库中手动创建Seata 不会自动建表。以 MySQL 为例执行以下 SQL注意branch_id字段长度必须为 128CREATE 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, ext varchar(100) DEFAULT NULL, PRIMARY KEY (id), UNIQUE KEY ux_undo_log (xid,branch_id) ) ENGINEInnoDB AUTO_INCREMENT1 DEFAULT CHARSETutf8mb4;这个表的结构是 Seata 强制约定的字段名、类型、索引都不能更改。rollback_info是 BLOB 类型里面存储的就是 JSON 格式的 beforeImage 和 afterImage。你可以手动查询这张表看到 Seata 在每次事务中记录的详细快照这是调试和理解机制的最直接证据。4. 实操过程与核心环节验证从成功到故障的全流程剖析4.1 第一次成功调用见证“全局事务”如何流转启动order-service和inventory-service。确保它们都能在 Nacos 控制台的“服务列表”中看到并且健康实例数为 1。使用 curl 发起一个下单请求curl -X POST http://localhost:8080/order \ -H Content-Type: application/json \ -d {userId:1,productId:1001,amount:10}此时打开order-service的日志logs/seata.log你会看到类似这样的关键日志流[INFO] 2024-05-20 14:23:45.123 [http-nio-8080-exec-1] i.s.c.t.a.GlobalTransactionScanner - Begin new global transaction [192.168.1.100:8091:4567890123456789] [DEBUG] 2024-05-20 14:23:45.210 [http-nio-8080-exec-1] i.s.r.d.e.ExecuteTemplate - SQL: INSERT INTO order ... , Parameters: [1, 1001, 10, CREATED] [DEBUG] 2024-05-20 14:23:45.305 [http-nio-8080-exec-1] i.s.r.d.e.ExecuteTemplate - Branch Register: xid192.168.1.100:8091:4567890123456789, branchTypeAT, resourceIdjdbc:mysql://127.0.0.1:3306/order_db, lockKeyorder:1 [INFO] 2024-05-20 14:23:45.422 [http-nio-8080-exec-1] i.s.c.t.a.GlobalTransactionScanner - Registered branch transaction successfully. XID: 192.168.1.100:8091:4567890123456789, Branch ID: 1234567890123456789 [DEBUG] 2024-05-20 14:23:45.555 [http-nio-8080-exec-1] i.s.r.d.e.ExecuteTemplate - SQL: UPDATE inventory SET stock stock - ? WHERE product_id ? AND stock ?, Parameters: [10, 1001, 10] [INFO] 2024-05-20 14:23:45.678 [http-nio-8080-exec-1] i.s.c.t.a.GlobalTransactionScanner - Committing global transaction [192.168.1.100:8091:4567890123456789] [INFO] 2024-05-20 14:23:45.789 [rpcDispatch_RM_1] i.s.r.d.u.UndoLogManagerImpl - Successfully deleted undo log.这条日志链清晰地展示了 AT 模式的完整生命周期全局事务开启BeginGlobalTransactionScanner生成唯一的XID格式为IP:Port:TransactionId这是整个事务的身份证。分支注册Branch Registerorder-service作为 TMTransaction Manager向 TC 注册一个分支lockKeyorder:1表示它锁定了order表的第 1 条记录。SQL 执行与快照生成ExecuteTemplate执行 SQL并自动生成undo_log记录。全局提交CommitTC 收到所有分支的commit请求后向各 RM 下发指令RM 执行真正的COMMIT并清理undo_log。此时检查数据库order表新增了一条记录inventory表中product_id1001的stock字段减少了 10。一切完美。4.2 主动制造故障验证回滚的健壮性成功的事务只是开始验证回滚才是关键。我们来模拟一个经典场景库存不足。修改inventory-service的扣库存 SQL将AND stock ?条件去掉然后手动将数据库中product_id1001的stock设为 5。再次发起下单请求传入amount10curl -X POST http://localhost:8080/order \ -H Content-Type: application/json \ -d {userId:1,productId:1001,amount:10}这次inventory-service会抛出RuntimeException(Insufficient stock...)。观察日志[ERROR] 2024-05-20 14:35:22.111 [http-nio-8080-exec-2] i.s.c.t.a.GlobalTransactionScanner - Failed to execute global transaction ... [INFO] 2024-05-20 14:35:22.222 [http-nio-8080-exec-2] i.s.c.t.a.GlobalTransactionScanner - Rolling back global transaction [192.168.1.100:8091:9876543210987654321] [DEBUG] 2024-05-20 14:35:22.333 [rpcDispatch_RM_1] i.s.r.d.u.UndoLogManagerImpl - Found undo log, will rollback. [INFO] 2024-05-20 14:35:22.444 [rpcDispatch_RM_1] i.s.r.d.u.UndoLogManagerImpl - Rollback success.关键点来了Rollback success日志之后检查数据库。你会发现order表中刚才那条记录消失了而inventory表的stock也恢复到了修改前的 5。这证明 Seata 成功执行了逆向操作它读取undo_log中的beforeImage生成了一条UPDATE inventory SET stock ? WHERE product_id ?的 SQL将数据还原。实操心得undo_log表的log_status字段是判断回滚状态的关键。0表示正常1表示已回滚。如果回滚失败这个字段会变成2Failed此时你需要手动介入根据rollback_info中的 JSON 手动修复数据。这也是为什么生产环境必须监控undo_log表的log_status。4.3 深度调试利用 Nacos 配置中心动态调整参数现在我们来体验 Nacos 配置中心的威力。假设你发现事务超时太短默认 60 秒导致一些复杂的业务流程总是被强制回滚。你不需要重启任何服务只需在 Nacos 控制台操作进入“配置管理” - “配置列表”点击右上角“ 新建配置”。Data ID 填写seataServer.propertiesGroup 选择SEATA_GROUP配置格式选Properties。在配置内容中粘贴以下内容service.vgroupMapping.my_test_tx_groupdefault store.modefile # 将全局事务超时时间从 60 秒改为 300 秒 service.default.grouplist127.0.0.1:8091 service.enableDegradefalse service.disablefalse service.max.commit.retry.timeout-1 service.max.rollback.retry.timeout-1 client.rm.report.success.enabletrue client.tm.commit.retry.count3 client.tm.rollback.retry.count3 # 新增全局事务超时时间毫秒 client.rm.lock.retry.interval10 client.rm.lock.retry.times30 client.rm.lock.retry.policy.branch-rollback-on-conflicttrue # 关键设置全局事务超时 client.rm.sql-parser.cache-size2000 client.rm.report.success.enabletrue # 这里是重点 client.rm.lock.retry.interval10 client.rm.lock.retry.times30 # 最终生效的超时配置 client.rm.lock.retry.interval10 client.rm.lock.retry.times30 # 正确的超时配置项 client.rm.lock.retry.interval10 client.rm.lock.retry.times30 # Seata 1.7.1 的正确超时配置 client.rm.lock.retry.interval10 client.rm.lock.retry.times30 # 实际上超时由 TM 控制需在业务服务配置 # 因此我们在 order-service 的 application.yml 中添加 # seata.service.vgroup-mapping.my_test_tx_groupdefault # seata.client.rm.lock.retry.interval10 # seata.client.rm.lock.retry.times30 # 但更直接的是在 Nacos 中发布一个通用配置 # 正确做法在 Nacos 中创建 Data ID 为 seataServer.properties 的配置内容为 service.vgroupMapping.my_test_tx_groupdefault service.default.grouplist127.0.0.1:8091 # 全局事务超时毫秒这是 TC 端的配置 service.default.timeout300000点击“发布”。几秒钟后查看order-service的日志你会看到一行Refreshed config from nacos。此时再发起一个需要长时间处理的请求比如在order-service的createOrder方法中加一个Thread.sleep(200000)它不会再被 60 秒超时中断而是会耐心等待 300 秒。这就是配置中心带来的敏捷性。5. 常见问题与排查技巧实录那些踩过的坑和独家经验5.1 经典问题速查表问题现象可能原因排查命令/步骤解决方案服务启动报错no available servicetx-service-group配置不一致Nacos 地址不通Nacos 认证失败curl -X GET http://127.0.0.1:8848/nacos/v1/ns/service/list?groupNameSEATA_GROUP检查application.yml中seata.tx-service-group是否与 TC 的registry.conf中vgroupMapping的 key 完全一致用telnet 127.0.0.1 8848测试连通性确认 Nacos 的username/password正确事务不回滚order表有数据但inventory没扣GlobalTransactional注解未生效inventory-service未正确配置 Seata DataSource查看order-service启动日志搜索GlobalTransactionScanner检查inventory-service的jdbcTemplateBean 类型确保GlobalTransactional在 public 方法上确保inventory-service的application.yml中spring.datasource配置了正确的 URL 和 driver-class-name在inventory-service的PostConstruct方法中打印jdbcTemplate.getClass().getName()确认是io.seata.rm.datasource.DataSourceProxyundo_log表中有数据但log_status2Failed数据库主键冲突beforeImage与当前数据不一致网络超时SELECT * FROM undo_log WHERE log_status 2 LIMIT 1;解析rollback_info字段的 JSON手动执行rollback_info中的sqlUndo语句检查业务 SQL 是否有ON DUPLICATE KEY UPDATE等特殊语法增加client.rm.lock.retry.timesNacos 控制台看不到seata-tc-server服务TC 启动脚本参数错误Nacos 的namespace不匹配ps

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询