Seata AT事务模式详解 AT(Automatic Transaction)模式是 Seata 提供的零侵入 分布式事务解决方案,无需修改业务代码,仅需添加注解即可实现分布式事务。
一、AT 模式核心概念 1.1 全局事务与分支事务 在 Seata 的 AT 模式中,理解分布式事务生命周期需要掌握两个核心概念:
全局事务 (Global Transaction) :
是整个业务链路(跨多个微服务)
由 TM(事务管理器)发起和管理
拥有全局唯一的 XID(事务 ID)
分支事务 (Branch Transaction) :
是单个微服务内的本地数据库操作
自动注册到全局事务
拥有 Branch ID 标识
1 2 3 4 5 6 全局事务 (XID: xxx) ↓ ├─ 分支事务 1 (订单服务 → 订单 DB) ├─ 分支事务 2 (库存服务 → 库存 DB) ├─ 分支事务 3 (账户服务 → 账户 DB) └─ 分支事务 4 (积分服务 → 积分 DB)
1.2 两阶段提交模型 AT 模式采用改进的两阶段提交(2PC)模型:
阶段一(Phase 1) :
拦截业务 SQL,解析语义
查询数据前镜像(Before Image)
执行业务 SQL
查询数据后镜像(After Image)
保存 undo_log 回滚日志
提交本地事务(立即释放锁 )
向 TC 汇报执行结果
阶段二(Phase 2) :
成功提交 : 异步批量删除 undo_log(快速返回)
失败回滚 : 根据 undo_log 前镜像恢复数据
1 2 3 4 5 6 7 8 9 10 ┌─────────────────────────────────────────────┐ │ Phase 1: 执行并记录 │ │ 查询前镜像 → 执行 SQL → 查询后镜像 │ │ → 保存 undo_log → 提交本地事务 │ └─────────────────────────────────────────────┘ ↓ ┌───────┴───────┐ ↓ ↓ 成功则提交 失败则回滚 (删除 undo_log) (根据 undo_log 恢复)
二、自动注册原理 2.1 数据源代理技术 Seata 通过 DataSourceProxy (数据源代理)和 SQL 拦截 技术实现分支事务的自动注册。
核心组件 :
DataSourceProxy: 代理真实数据源
ConnectionProxy: 代理数据库连接
StatementProxy: 代理 SQL 执行
ExecuteTemplate: SQL 执行模板
2.2 自动注册流程 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 1. 业务代码执行 SQL ↓ 调用 mapper.insert() 或 jdbcTemplate.update() 2. Seata 拦截 SQL 执行 ↓ DataSourceProxy 拦截数据库连接 检查 RootContext 中是否有 XID 3. 自动注册分支事务 ↓ 有 XID → 向 TC 注册分支事务,获取 Branch ID 无 XID → 普通本地事务,直接执行 4. 执行 SQL 并记录日志 ↓ 查询前镜像 → 执行业务 SQL → 查询后镜像 保存 undo_log → 提交本地事务 5. 汇报执行结果 ↓ 向 TC 汇报 Phase 1 执行成功
2.3 XID 传递机制 XID 是什么 :
全局事务的唯一标识符
格式:{ip}:{port}:{transactionId}
例如:192.168.1.100:8091:1234567890
XID 传递路径 :
1 2 3 4 5 6 7 入口服务 (@GlobalTransactional) ↓ RootContext.bind(xid) Spring Cloud OpenFeign / Dubbo ↓ HTTP Header / RPC Context 下游服务 1 ↓ RootContext.bind(xid) 下游服务 2
注意事项 :
✅ Spring Cloud/Dubbo 默认支持 XID 透传
⚠️ 自定义线程需手动传递 XID
⚠️ 消息队列需手动绑定 XID
三、快速开始 3.1 数据库初始化 在各个微服务的数据库中创建 undo_log 表:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 CREATE TABLE IF NOT EXISTS `undo_log` ( `id` BIGINT (20 ) NOT NULL AUTO_INCREMENT COMMENT '主键' , `branch_id` BIGINT (20 ) NOT NULL COMMENT '分支事务 ID' , `xid` VARCHAR (100 ) NOT NULL COMMENT '全局事务 ID' , `context` VARCHAR (128 ) NOT NULL COMMENT '上下文信息' , `rollback_info` LONGBLOB NOT NULL COMMENT '回滚日志' , `log_status` INT (11 ) NOT NULL COMMENT '日志状态' , `log_created` DATETIME NOT NULL COMMENT '创建时间' , `log_modified` DATETIME NOT NULL COMMENT '修改时间' , `ext` VARCHAR (100 ) DEFAULT NULL COMMENT '扩展字段' , PRIMARY KEY (`id` ), UNIQUE KEY `ux_undo_log` (`xid` , `branch_id` ) USING BTREE ) ENGINE =InnoDB DEFAULT CHARSET =utf8mb4 COMMENT ='AT事务回滚日志表' ;
表结构说明 :
字段
类型
说明
branch_id
BIGINT
分支事务 ID
xid
VARCHAR
全局事务 ID
context
VARCHAR
序列化方式等上下文
rollback_info
LONGBLOB
前后镜像数据
log_status
INT
0-正常,1-防御性回滚
log_created
DATETIME
创建时间
log_modified
DATETIME
修改时间
3.2 添加依赖 各微服务项目引入 Seata 依赖:
Maven :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <dependencies > <dependency > <groupId > com.alibaba.cloud</groupId > <artifactId > spring-cloud-starter-alibaba-seata</artifactId > <version > 2021.0.1.0</version > </dependency > <dependency > <groupId > com.baomidou</groupId > <artifactId > mybatis-plus-boot-starter</artifactId > <version > 3.5.2</version > </dependency > </dependencies >
Gradle :
1 2 3 dependencies { implementation 'com.alibaba.cloud:spring-cloud-starter-alibaba-seata:2021.0.1.0' }
3.3 配置文件 application.yml :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 seata: enabled: true tx-service-group: default_tx_group service: vgroup-mapping: default_tx_group: DEFAULT grouplist: DEFAULT: 192.168 .1 .100 :8091 registry: type: nacos nacos: server-addr: laosan.xin:8848 namespace: dev-wdg group: DEFAULT_GROUP config: type: nacos nacos: server-addr: laosan.xin:8848 namespace: dev-wdg group: DEFAULT_GROUP data-id: seataService.properties mybatis-plus: configuration: map-underscore-to-camel-case: true default-executor-type: simple
3.4 开启全局事务 在业务入口类添加 @GlobalTransactional 注解:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 @Service public class OrderServiceImpl implements OrderService { @Autowired private OrderMapper orderMapper; @Autowired private InventoryService inventoryService; @Autowired private AccountService accountService; @Override @GlobalTransactional (timeoutMills = 300000 , name = "create-order-tx" ) public void createOrder (OrderDTO orderDTO) { OrderPO order = convertToPO(orderDTO); orderMapper.insert(order); inventoryService.decreaseStock( orderDTO.getProductId(), orderDTO.getCount() ); accountService.decreaseBalance( orderDTO.getUserId(), orderDTO.getAmount() ); } private OrderPO convertToPO (OrderDTO dto) { OrderPO po = new OrderPO(); BeanUtils.copyProperties(dto, po); return po; } }
注解参数说明 :
参数
类型
默认值
说明
timeoutMills
long
60000
超时时间(毫秒)
name
String
“”
事务名称(便于监控)
rollbackFor
Class[]
Exception.class
回滚异常类型
noRollbackFor
Class[]
-
不回滚异常类型
3.5 下游服务实现 下游服务无需添加注解,自动参与分布式事务:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @Service public class InventoryServiceImpl implements InventoryService { @Autowired private InventoryMapper inventoryMapper; @Override public void decreaseStock (Long productId, Integer count) { int updated = inventoryMapper.decreaseStock(productId, count); if (updated == 0 ) { throw new RuntimeException("库存不足" ); } } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @Service public class AccountServiceImpl implements AccountService { @Autowired private AccountMapper accountMapper; @Override public void decreaseBalance (Long userId, BigDecimal amount) { int updated = accountMapper.decreaseBalance(userId, amount); if (updated == 0 ) { throw new RuntimeException("余额不足" ); } } }
3.6 测试验证 单元测试 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 @SpringBootTest public class OrderServiceTest { @Autowired private OrderService orderService; @Autowired private OrderMapper orderMapper; @Autowired private InventoryMapper inventoryMapper; @Test public void testCreateOrderSuccess () { OrderDTO orderDTO = new OrderDTO(); orderDTO.setOrderId(10001L ); orderDTO.setUserId(888L ); orderDTO.setProductId(2001L ); orderDTO.setCount(2 ); orderDTO.setAmount(new BigDecimal("199.00" )); assertDoesNotThrow(() -> { orderService.createOrder(orderDTO); }); assertNotNull(orderMapper.selectById(10001L )); Integer stock = inventoryMapper.selectById(2001L ).getStock(); assertEquals(98 , stock); } @Test public void testCreateOrderRollback () { OrderDTO orderDTO = new OrderDTO(); orderDTO.setOrderId(10002L ); orderDTO.setUserId(888L ); orderDTO.setProductId(2001L ); orderDTO.setCount(1000 ); assertThrows(RuntimeException.class , () -> { orderService.createOrder(orderDTO); }); assertNull(orderMapper.selectById(10002L )); Integer stock = inventoryMapper.selectById(2001L ).getStock(); assertEquals(100 , stock); } }
查看 Seata Server 日志 启动测试后,查看 Seata Server 日志:
1 2 tail -f /opt/seata/logs/seata/seata-server.log
关键日志 :
1 2 3 4 5 INFO - Global transaction begin, xid: 192.168.1.100:8091:1234567890 INFO - Branch register to tc, xid: 192.168.1.100:8091:1234567890, branchId: 1 INFO - Branch register to tc, xid: 192.168.1.100:8091:1234567890, branchId: 2 INFO - Branch register to tc, xid: 192.168.1.100:8091:1234567890, branchId: 3 INFO - Global transaction commit, xid: 192.168.1.100:8091:1234567890
四、常见问题排查 4.1 分支事务未自动注册 现象 : TC 上没有分支事务记录
可能原因 :
1. 手动创建了 DataSource 问题代码 :
1 2 3 4 5 6 7 8 9 @Configuration public class DataSourceConfig { @Bean public DataSource dataSource () { return new HikariDataSource(); } }
解决方案 :
1 2 3 4 5 6 7 8 9 10 @Configuration public class DataSourceConfig { @Bean public DataSource dataSource () { DataSource dataSource = new HikariDataSource(); return new DataSourceProxy(dataSource); } }
最佳实践 :
使用 Spring Boot 自动配置,无需手动创建 DataSource
如必须手动创建,确保包装为 DataSourceProxy
2. 多数据源场景 问题描述 : 使用 DynamicDataSource 切换数据源时,部分数据源未注册
解决方案 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 @Configuration public class MultiDataSourceConfig { @Bean public DataSource dynamicDataSource () { DataSource ds1 = createDataSource("ds1" ); DataSource ds2 = createDataSource("ds2" ); DataSourceProxy proxy1 = new DataSourceProxy(ds1); DataSourceProxy proxy2 = new DataSourceProxy(ds2); DynamicDataSource dynamicDataSource = new DynamicDataSource(); Map<Object, Object> targetDataSources = new HashMap<>(); targetDataSources.put("ds1" , proxy1); targetDataSources.put("ds2" , proxy2); dynamicDataSource.setTargetDataSources(targetDataSources); return dynamicDataSource; } private DataSource createDataSource (String name) { HikariDataSource ds = new HikariDataSource(); ds.setJdbcUrl("jdbc:mysql://localhost:3306/" + name); ds.setUsername("root" ); ds.setPassword("password" ); return ds; } }
3. XID 丢失(最常见) 场景 1: 子线程中执行 ❌ 错误示例 :
1 2 3 4 5 6 7 8 9 @GlobalTransactional public void createOrder (OrderDTO orderDTO) { new Thread(() -> { inventoryService.decreaseStock(); }).start(); }
✅ 正确示例 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @GlobalTransactional public void createOrder (OrderDTO orderDTO) { String xid = RootContext.getXID(); new Thread(() -> { try { RootContext.bind(xid); inventoryService.decreaseStock(); } finally { RootContext.unbind(); } }).start(); }
场景 2: Feign/Dubbo 调用未透传 XID 检查点 :
✅ Spring Cloud OpenFeign 默认支持 XID 透传
✅ Dubbo 默认支持 XID 透传
⚠️ 自定义 HTTP 客户端需手动传递
自定义 Feign 配置 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Configuration public class FeignConfig { @Bean public RequestInterceptor requestInterceptor () { return template -> { String xid = RootContext.getXID(); if (xid != null ) { template.header(RootContext.KEY_XID, xid); } }; } }
场景 3: 消息队列消费端 ❌ 错误示例 :
1 2 3 4 5 6 7 8 9 @RocketMQMessageListener (topic = "order-topic" , ...)public class OrderConsumer implements RocketMQListener <OrderDTO > { @Override public void onMessage (OrderDTO orderDTO) { inventoryService.decreaseStock(); } }
✅ 正确示例 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 @RocketMQMessageListener (topic = "order-topic" , ...)public class OrderConsumer implements RocketMQListener <OrderDTO > { @Autowired private TransactionalTemplate transactionalTemplate; @Override public void onMessage (OrderDTO orderDTO) { String xid = getMessageHeaderXid(); transactionalTemplate.execute(new TransactionalExecutor() { @Override public Object execute () throws Throwable { inventoryService.decreaseStock(); return null ; } @Override public UserTransaction getTransactionInfo () { return new UserTransaction(xid, 30000 ); } }); } private String getMessageHeaderXid () { return MessageConst.PROPERTY_TRANSACTION_ID; } }
4.2 事务不回滚 现象 : 异常抛出但数据未回滚
可能原因 :
1. 异常类型不匹配 ❌ 错误示例 :
1 2 3 4 5 6 7 8 9 10 11 @GlobalTransactional (rollbackFor = Exception.class ) public void createOrder () { throw new RuntimeException("失败" ); } @GlobalTransactional public void createOrder2 () { throw new RuntimeException("失败" ); }
✅ 正确示例 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @GlobalTransactional (rollbackFor = Exception.class ) public void createOrder () { throw new Exception("失败" ); } @GlobalTransactional public void createOrder () throws Exception { try { } catch (Exception e) { log.error("失败" , e); throw e; } }
2. 异常被吞掉 ❌ 错误示例 :
1 2 3 4 5 6 7 8 9 @GlobalTransactional public void createOrder () { try { inventoryService.decreaseStock(); } catch (Exception e) { log.error("扣减库存失败" , e); } }
✅ 正确示例 :
1 2 3 4 5 6 7 8 9 @GlobalTransactional public void createOrder () { try { inventoryService.decreaseStock(); } catch (Exception e) { log.error("扣减库存失败" , e); throw new RuntimeException("扣减库存失败" , e); } }
4.3 性能问题 现象 : 事务执行慢,TPS 上不去
优化建议 :
1. Undo Log 配置优化 1 2 3 4 5 6 7 8 9 10 seata: client: undo: only-care-update-columns: true compress: enable: true type: zip threshold: 64k
2. 批量提交优化 1 2 3 4 5 6 7 seata: client: rm: async-commit-buffer-limit: 10000 report-success-enable: false
3. 数据库索引优化 1 2 3 4 5 6 7 ALTER TABLE undo_log ADD INDEX idx_xid_branch (xid, branch_id);DELETE FROM undo_log WHERE log_created < DATE_SUB (NOW (), INTERVAL 7 DAY );
4.4 脏写问题 现象 : Phase 1 提交后,其他事务修改了数据
原因 : AT 模式 Phase 1 提交后立即释放锁,存在脏写风险
解决方案 :
方案 1: 使用全局锁 1 2 3 4 5 6 7 8 seata: client: rm: lock: retry-interval: 10 retry-times: 30 retry-policy-branch-rollback-on-conflict: true
方案 2: 升级 TCC 模式 对隔离性要求高的场景,考虑使用 TCC 模式
五、最佳实践 5.1 开发规范 ✅ 推荐做法 :
1. 事务粒度控制 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @GlobalTransactional (timeoutMills = 60000 )public void createOrder (OrderDTO orderDTO) { orderMapper.insert(orderDTO); inventoryService.decreaseStock(); accountService.decreaseBalance(); } @GlobalTransactional (timeoutMills = 300000 )public void processEverything (OrderDTO orderDTO) { createOrder(orderDTO); sendEmail(); generateReport(); uploadToOSS(); }
原则 :
事务内只包含必要的数据库操作
避免 HTTP 调用、文件处理等耗时操作
单个事务执行时间控制在 3 秒内
2. 超时时间设置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @GlobalTransactional (timeoutMills = 300000 ) public void complexBusiness () { } @GlobalTransactional (timeoutMills = 60000 ) public void simpleBusiness () { } @GlobalTransactional (timeoutMills = 10000 ) public void quickBusiness () { }
建议 :
默认超时时间:60 秒
复杂业务:不超过 5 分钟
简单查询:10-20 秒
3. 异常处理 1 2 3 4 5 6 7 8 9 10 11 12 13 @GlobalTransactional (rollbackFor = Exception.class ) public void businessProcess (OrderDTO orderDTO ) { try { orderService.create(orderDTO); inventoryService.decrease(); } catch (SpecificException e) { log.error("订单创建失败,orderId={}" , orderDTO.getOrderId(), e); throw new BusinessException("订单创建失败" , e); } }
要点 :
明确指定 rollbackFor = Exception.class
捕获异常后必须重新抛出
记录详细的错误日志便于排查
❌ 避免做法 :
1. 避免在事务中执行耗时操作 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @GlobalTransactional public void badPractice (OrderDTO orderDTO) { orderMapper.insert(orderDTO); restTemplate.postForObject( "http://external-service/api/notify" , orderDTO, Response.class ) ; generateLargeExcel(); smsService.sendSms(); }
改进方案 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 @GlobalTransactional public void goodPractice (OrderDTO orderDTO) { orderMapper.insert(orderDTO); inventoryService.decrease(); accountService.decrease(); } @Component public class OrderListener { @RocketMQMessageListener (topic = "order-created" ) public void onMessage (OrderDTO orderDTO) { sendEmailAsync(orderDTO); generateReportAsync(orderDTO); } }
2. 避免事务嵌套 1 2 3 4 5 6 7 8 9 10 @GlobalTransactional public void outerMethod () { innerMethod(); } @GlobalTransactional (propagation = Propagation.REQUIRES_NEW)public void innerMethod () { }
问题 :
内层事务不会开启新事务
内层异常会导致外层整体回滚
性能损耗(两次 TC 交互)
正确做法 :
1 2 3 4 5 6 7 8 9 10 11 12 @GlobalTransactional public void businessProcess () { step1(); step2(); step3(); } private void step1 () { }private void step2 () { }private void step3 () { }
3. 避免跨线程 XID 丢失 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 @GlobalTransactional public void processWithThreads () { String xid = RootContext.getXID(); new Thread(() -> { serviceA.doSomething(); }).start(); CompletableFuture.runAsync(() -> { try { RootContext.bind(xid); serviceB.doSomething(); } finally { RootContext.unbind(); } }); transactionalTemplate.execute(() -> { serviceC.doSomething(); return null ; }); }
5.2 监控告警 关键指标监控 Prometheus 配置 :
1 2 3 4 5 seata: metrics: enabled: true exporter-list: prometheus exporter-prometheus-port: 9898
核心监控指标 :
指标名称
说明
告警阈值
global_transaction_tps
全局事务 TPS
< 100 持续 5 分钟
global_transaction_success_rate
事务成功率
< 99%
global_transaction_latency_avg
平均耗时
> 3 秒
global_transaction_rollback_count
回滚数量
> 10/小时
undo_log_size
undo_log 表大小
> 100 万行
Grafana 看板 导入 Seata 官方 Dashboard:
Dashboard ID : 13393
监控内容 :
全局事务状态分布
分支事务执行情况
TP99/TP95 延迟
回滚趋势图
示例告警规则 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 groups: - name: seata_alerts rules: - alert: SeataTransactionSuccessRateLow expr: rate(seata_global_transaction_success_total[5m]) / rate(seata_global_transaction_total[5m]) < 0.99 for: 5m annotations: summary: "Seata 事务成功率过低" - alert: SeataRollbackCountHigh expr: increase(seata_global_transaction_rollback_total[1h]) > 10 for: 1h annotations: summary: "Seata 回滚事务过多"
5.3 性能调优 JVM 参数优化 1 2 3 4 5 6 7 8 9 10 11 12 13 14 #!/bin/bash JAVA_OPTS="-server \ -Xms4g -Xmx4g \ # 堆内存 (根据服务器配置调整) -Xmn2g \ # 新生代 -XX:MetaspaceSize=256m \ # 元空间初始值 -XX:MaxMetaspaceSize=512m \ # 元空间最大值 -XX:+UseG1GC \ # G1 垃圾回收器 -XX:MaxGCPauseMillis=100 \ # 最大 GC 停顿时间 -XX:+HeapDumpOnOutOfMemoryError \ # OOM 时 dump 堆 -XX:HeapDumpPath=/opt/seata/logs/heapdump.hprof" export JAVA_OPTS
参数说明 :
-Xms/-Xmx: 设置为相同值,避免内存动态伸缩
-Xmn: 新生代大小为堆的 1/2
-XX:+UseG1GC: G1 适合大堆低延迟场景
-XX:MaxGCPauseMillis: 控制 GC 停顿时间
数据库连接池优化 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 spring: datasource: hikari: minimum-idle: 10 maximum-pool-size: 30 connection-timeout: 30000 idle-timeout: 600000 max-lifetime: 1800000 connection-test-query: SELECT 1
优化建议 :
maximum-pool-size: 根据并发量调整 (CPU 核数 * 2 + 1)
connection-timeout: 30 秒足够
定期监控连接池使用率 (建议 < 80%)
Undo Log 优化 1 2 3 4 5 6 7 8 9 10 seata: client: undo: only-care-update-columns: true compress: enable: true type: zip threshold: 64k
效果 :
减少 undo_log 存储空间 50%-70%
降低网络传输开销
提升回滚速度
5.4 数据清理 定期清理策略 方案 1: MySQL 定时任务 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 DELIMITER $$ CREATE PROCEDURE cleanup_seata_undo_log()BEGIN DELETE FROM undo_log WHERE log_created < DATE_SUB (NOW (), INTERVAL 7 DAY ) LIMIT 10000 ; INSERT INTO cleanup_log (cleanup_time, deleted_count) VALUES (NOW (), ROW_COUNT ()); END $$DELIMITER ; CREATE EVENT IF NOT EXISTS cleanup_undo_log_eventON SCHEDULE EVERY 1 DAY STARTS '2024-01-01 02:00:00' DO CALL cleanup_seata_undo_log();
方案 2: Spring Boot 定时任务 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 @Component public class UndoLogCleaner { @Autowired private JdbcTemplate jdbcTemplate; @Scheduled (cron = "0 0 2 * * ?" ) public void cleanUndoLog () { try { int days = 7 ; String sql = "DELETE FROM undo_log WHERE log_created < DATE_SUB(NOW(), INTERVAL ? DAY)" ; int count = jdbcTemplate.update(sql, days); log.info("清理 undo_log 完成,删除 {} 条记录" , count); } catch (Exception e) { log.error("清理 undo_log 失败" , e); } } }
方案 3: Shell 脚本 + Crontab 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 #!/bin/bash DB_HOST="localhost" DB_USER="root" DB_PASS="password" DB_NAME="seata" DAYS=7 mysql -h${DB_HOST} -u${DB_USER} -p${DB_PASS} ${DB_NAME} <<EOF DELETE FROM undo_log WHERE log_created < DATE_SUB(NOW(), INTERVAL ${DAYS} DAY) LIMIT 10000; EOF if [ $? -eq 0 ]; then echo "$(date) : 清理 undo_log 成功" >> /var/log /cleanup.log else echo "$(date) : 清理 undo_log 失败" >> /var/log /cleanup.log fi
Crontab 配置 :
1 2 0 2 * * * /opt/scripts/clean_undo_log.sh
5.5 安全防护 1. Nacos 鉴权 1 2 3 4 5 6 7 8 seata: registry: type: nacos nacos: server-addr: laosan.xin:8848 username: seata_user password: StrongP@ssw0rd namespace: dev-wdg
安全建议 :
为 Seata 创建专用 Nacos 账号
密码长度 >= 12 位,包含大小写 + 数字 + 特殊字符
定期更换密码 (90 天)
2. 数据库权限最小化 1 2 3 4 5 6 7 8 9 10 11 12 13 CREATE USER 'seata' @'%' IDENTIFIED BY 'StrongP@ssw0rd' ;GRANT SELECT , INSERT , UPDATE , DELETE ON seata.* TO 'seata' @'%' ;GRANT CREATE TABLE , DROP , INDEX ON seata.* TO 'seata' @'%' ;REVOKE DROP ON *.* FROM 'seata' @'%' ;REVOKE ALTER ON *.* FROM 'seata' @'%' ;REVOKE GRANT OPTION ON *.* FROM 'seata' @'%' ;FLUSH PRIVILEGES ;
权限原则 :
仅授予业务必需的权限
禁止 DROP、ALTER 等危险操作
禁止访问其他数据库
3. 敏感配置加密 1 2 3 4 5 6 seata: store: db: user: root password: ENC(AbCdEfGhIjKlMnOpQrStUvWxYz)
配置方法 :
1 2 3 4 5 6 <dependency > <groupId > com.github.ulisesbocchio</groupId > <artifactId > jasypt-spring-boot-starter</artifactId > <version > 3.0.5</version > </dependency >
1 2 3 4 5 java -cp jasypt-1.9.3.jar org.jasypt.intf.cli.JasyptPBEStringEncryptionCLI \ input="my_password" \ password="encryption_key" \ algorithm=PBEWITHHMACSHA512ANDAES_256
5.6 故障处理 建立应急预案 预案 1: 事务卡住不提交 1 2 3 4 5 6 7 8 9 10 11 12 13 SELECT xid, status , begin_time, timeout , gmt_modifiedFROM global_tableWHERE status IN (2 , 4 ) AND gmt_modified < NOW () - INTERVAL 5 MINUTE ;UPDATE global_table SET status = 3 , gmt_modified = NOW () WHERE xid = 'xxx' AND status = 2 ;DELETE FROM lock_table WHERE xid = 'xxx' ;
操作流程 :
确认业务数据安全
记录操作日志
执行手动提交
通知业务方验证
事后分析根因
预案 2: 大量事务回滚 处理步骤 :
立即停止相关服务
查看 Seata Server 日志
分析回滚原因
修复 Bug 后重启
数据对账修复
预案 3: Undo Log 表爆满 1 2 3 4 5 6 7 DELETE FROM undo_log WHERE log_status = 1 LIMIT 100000 ;OPTIMIZE TABLE undo_log;
故障演练 定期演练计划 :
演练场景
频率
参与人员
事务超时回滚
每月 1 次
开发 + 运维
数据库宕机切换
每季度 1 次
运维 + DBA
Seata Server 故障
每季度 1 次
运维
数据不一致修复
每半年 1 次
开发 + 测试
演练流程 :
制定演练方案
准备测试环境
执行故障注入
观察系统反应
记录问题清单
优化改进措施
六、总结 6.1 AT 模式优势 ✅ 零侵入 : 无需修改业务代码,仅添加注解 ✅ 简单易用 : 学习成本低,快速上手 ✅ 高性能 : 两阶段非阻塞提交,TPS 高 ✅ 广泛支持 : 兼容 MySQL、Oracle、PostgreSQL 等主流数据库 ✅ 生态完善 : 与 Spring Cloud、Dubbo 无缝集成
6.2 适用场景 ✅ 推荐场景 :
90% 的微服务分布式事务需求
对隔离性要求不苛刻的业务 (可接受短暂脏读)
追求快速上线、降低开发成本的项目
团队技术能力一般,希望降低维护成本
❌ 不推荐场景 :
高并发秒杀系统 (考虑 TCC 模式)
强一致性要求 (考虑 XA 模式)
非关系型数据库场景 (Redis/MongoDB 等)
跨语言调用场景 (Go/Python 等)
6.3 与其他模式对比
特性
AT
TCC
Saga
XA
侵入性
无
高
中
无
性能
⭐⭐⭐⭐
⭐⭐⭐⭐⭐
⭐⭐⭐⭐
⭐⭐
隔离性
读未提交
业务隔离
无隔离
完全隔离
开发成本
低
高
中
低
适用场景
通用
高并发
长流程
强一致
6.4 快速选型指南 1 2 3 4 5 6 7 8 9 需要分布式事务吗? ├─ 否 → 使用本地事务 └─ 是 → 对隔离性要求高吗? ├─ 是 → 选择 TCC 模式 └─ 否 → 是长流程编排吗? ├─ 是 → 选择 Saga 模式 └─ 否 → 需要强一致性吗? ├─ 是 → 选择 XA 模式 └─ 否 → ✅ 选择 AT 模式 (推荐)
6.5 学习资源
作者 : laosan更新日期 : 2024/8/7版本 : Seata 1.6.1版权声明 : 本文采用 CC BY-NC-SA 4.0 许可协议