Seata AT模式代理数据源失效剖析:为何RM不写undo_log而global_table却有记录?

张开发
2026/4/16 21:01:40 15 分钟阅读

分享文章

Seata AT模式代理数据源失效剖析:为何RM不写undo_log而global_table却有记录?
1. Seata AT模式核心机制解析先来聊聊Seata AT模式的基本工作原理。AT模式全称Auto Transaction是Seata最常用的分布式事务解决方案。它的核心思想是通过对业务SQL的解析自动生成反向回滚日志undo_log实现事务的自动回滚。在实际运行中AT模式会经历几个关键阶段第一阶段业务SQL执行时RMResource Manager会先查询数据的前镜像before image并存入undo_log第二阶段执行业务SQL修改数据第三阶段生成后镜像after image当需要回滚时根据undo_log中的前镜像数据恢复原始状态这里有个关键点所有数据修改操作都必须经过Seata的DataSourceProxy代理。只有通过这个代理Seata才能拦截SQL并生成undo_log。如果代理没生效就会出现文章开头描述的现象——global_table有记录说明事务已开启但undo_log表空空如也。2. 代理数据源失效的典型表现我在实际项目中遇到过好几次代理失效的情况症状都非常相似事务注解GlobalTransactional已正确添加Seata Server日志显示全局事务已创建global_table有记录业务SQL正常执行但undo_log表没有任何记录事务回滚时数据无法恢复这种问题最迷惑人的地方在于看起来分布式事务已经生效了因为有global_table记录但实际上RM根本没参与事务管理。就像你去餐厅吃饭服务员给你上了菜单开启事务但厨师根本没收到点单没有undo_log。通过DEBUG跟踪我发现这种情况下SQL执行完全绕过了Seata的拦截器。也就是说你的应用直接使用了原生数据源而不是被Seata增强过的DataSourceProxy。3. 自动代理失效的五大常见原因经过多次踩坑和源码分析我总结了自动代理失效的几个常见原因3.1 Spring Boot自动配置冲突这是最常见的问题。当项目同时存在以下配置时容易出问题spring-boot-starter-jdbcdruid-spring-boot-starterseata-spring-boot-starter它们的自动配置可能存在加载顺序问题。我遇到过druid数据源先初始化导致Seata来不及代理的情况。3.2 多数据源场景配置不当在多个数据源的场景下如果没正确处理Primary注解Spring可能选择了错误的数据源。我曾经在一个项目里因为漏加Primary注解导致Seata代理了错误的数据源。3.3 版本兼容性问题Seata的不同版本对Spring Boot的支持度不同。比如Seata 1.4.x与Spring Boot 2.4.x的兼容性较好Seata 1.5.x开始对Spring Boot 2.6.x有更好支持用错版本组合可能导致自动代理失效。3.4 配置属性覆盖不当有些项目为了优化会手动设置seata: enable-auto-data-source-proxy: false这直接关闭了自动代理功能当然就不会生成undo_log了。3.5 Bean加载顺序问题当使用Configuration手动配置数据源时如果加载顺序不当可能导致Seata的自动配置失效。这种情况在复杂的多模块项目中特别常见。4. 手动配置DataSourceProxy的正确姿势当自动代理失效时手动配置是最可靠的解决方案。但这里有几个关键点需要注意4.1 基础配置方法Configuration public class DataSourceConfig { Bean ConfigurationProperties(prefix spring.datasource) public DruidDataSource druidDataSource() { return new DruidDataSource(); } Primary Bean public DataSource dataSource(DataSource druidDataSource) { return new DataSourceProxy(druidDataSource); } }这里必须注意原生数据源不要加PrimaryDataSourceProxy必须加Primary建议使用ConfigurationProperties统一管理配置4.2 多数据源场景处理对于多数据源需要为每个数据源创建代理Bean Primary public DataSource routingDataSource( Qualifier(masterDataSource) DataSource master, Qualifier(slaveDataSource) DataSource slave) { MapObject, Object targetDataSources new HashMap(); targetDataSources.put(master, new DataSourceProxy(master)); targetDataSources.put(slave, new DataSourceProxy(slave)); AbstractRoutingDataSource routing new AbstractRoutingDataSource() { Override protected Object determineCurrentLookupKey() { return DynamicDataSourceContextHolder.getDataSourceKey(); } }; routing.setTargetDataSources(targetDataSources); routing.setDefaultTargetDataSource(new DataSourceProxy(master)); return routing; }4.3 必须关闭自动代理在application.yml中一定要加上seata: enable-auto-data-source-proxy: false否则会出现双重代理导致奇怪的异常。5. Spring Cloud Alibaba集成时的特殊问题在使用Spring Cloud Alibaba时有几个额外的坑需要注意5.1 Nacos配置冲突当使用Nacos作为配置中心时确保seata配置没有被意外覆盖。建议在bootstrap.yml中明确指定spring: cloud: nacos: config: shared-configs[0]: >Bean public FilterRegistrationBean seataFilterRegistration() { FilterRegistrationBean registration new FilterRegistrationBean(); registration.setFilter(new SeataFilter()); registration.addUrlPatterns(/*); registration.setOrder(Ordered.HIGHEST_PRECEDENCE); return registration; }5.3 Feign拦截器配置使用Feign时需要确保事务上下文正确传递Configuration public class FeignConfig { Bean public RequestInterceptor seataFeignInterceptor() { return template - { String xid RootContext.getXID(); if (StringUtils.isNotBlank(xid)) { template.header(RootContext.KEY_XID, xid); } }; } }6. 问题排查的四步定位法当遇到undo_log不生成的问题时可以按照以下步骤排查检查代理是否生效在DataSource.getConnection()处打断点查看返回的是原生Connection还是ConnectionProxy检查配置加载顺序添加--debug启动参数查看Seata自动配置是否生效检查事务传播在业务方法开始处打印RootContext.getXID()确认事务上下文已绑定检查SQL拦截开启Seata的debug日志观察是否输出AT mode transaction begin等日志7. 最佳实践建议根据我的项目经验总结几个关键建议生产环境建议始终使用手动配置DataSourceProxy比自动代理更可靠多数据源场景下确保每个数据源都被正确代理统一管理数据源配置避免散落在多个配置类中定期检查Seata版本更新及时修复已知问题重要业务系统建议添加事务监控及时发现代理失效情况我在金融级项目中实践发现手动配置严格测试的组合最可靠。曾经有一个电商项目因为自动代理失效导致库存数据不一致后来全面改用手动配置后再没出现过类似问题。

更多文章