基于Spring的多数据源管理实战

发布时间:2026/6/26 5:35:42
基于Spring的多数据源管理实战 一、多数据源业务场景管理后台核心场景先明确落地场景避免盲目选型业务分库用户库、订单库、日志库、配置库独立拆分读写分离主库写、从库读提升后台查询吞吐量多租户隔离SaaS后台不同租户独立数据库外部数据对接后台对接第三方业务库、历史迁移库数据同步/归档生产库查询、归档库写入备份数据二、目前主流4种多数据源实现方案2026最新选型按从简单到复杂、从静态到动态、从原生到框架排序覆盖所有企业落地方式方式一分包多数据源多SqlSessionFactory 静态固定方式二Spring 原生 AbstractRoutingDataSource 动态路由方式三MyBatis-Plus Dynamic-Datasource 注解动态切换企业首选方式四中间件代理Sharding-JDBC/MyCat 分布式层方案三、四种方案原理、优缺点、适用场景完整代码3.1 方式一分包多数据源静态多SqlSessionFactory3.1.1 实现原理通过MapperScan区分不同 Mapper 包路径分别创建独立的DataSource、SqlSessionFactory、TransactionManager不同包下的 Mapper 绑定不同数据源编译期固定数据源运行时不可切换。3.1.2 优缺点分析✅优点原生 Spring 实现无第三方框架依赖零侵入配置简单、结构清晰、新手友好单库事务完全可控无切换异常❌缺点运行时无法动态切换数据源灵活性极差新增数据源需新增配置类、分包维护成本高不支持跨库动态调用仅适合固定分库场景3.1.3 适用场景数据源固定不变、无需动态切换、简单分库的轻量化后台项目。3.1.4 核心实战代码1、yml 多数据源配置spring: datasource: # 主库 master: url: jdbc:mysql://127.0.0.1:3306/db_master?useSSLfalseserverTimezoneAsia/Shanghai username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver # 从库/日志库 slave: url: jdbc:mysql://127.0.0.1:3306/db_slave?useSSLfalseserverTimezoneAsia/Shanghai username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver2、主库配置类Configuration MapperScan(basePackages com.admin.mapper.master, sqlSessionFactoryRef masterSqlSessionFactory) public class MasterDataSourceConfig { Bean(masterDataSource) Primary ConfigurationProperties(prefix spring.datasource.master) public DataSource masterDataSource() { return new DruidDataSource(); } Bean(masterSqlSessionFactory) Primary public SqlSessionFactory masterSqlSessionFactory(Qualifier(masterDataSource) DataSource dataSource) throws Exception { MyBatisSqlSessionFactoryBean factoryBean new MyBatisSqlSessionFactoryBean(); factoryBean.setDataSource(dataSource); return factoryBean.getObject(); } Bean(masterTransactionManager) Primary public PlatformTransactionManager masterTransactionManager(Qualifier(masterDataSource) DataSource dataSource) { return new DataSourceTransactionManager(dataSource); } }3、从库配置类结构一致分包区分Configuration MapperScan(basePackages com.admin.mapper.slave, sqlSessionFactoryRef slaveSqlSessionFactory) public class SlaveDataSourceConfig { Bean(slaveDataSource) ConfigurationProperties(prefix spring.datasource.slave) public DataSource slaveDataSource() { return new DruidDataSource(); } Bean(slaveSqlSessionFactory) public SqlSessionFactory slaveSqlSessionFactory(Qualifier(slaveDataSource) DataSource dataSource) throws Exception { MyBatisSqlSessionFactoryBean factoryBean new MyBatisSqlSessionFactoryBean(); factoryBean.setDataSource(dataSource); return factoryBean.getObject(); } Bean(slaveTransactionManager) public PlatformTransactionManager slaveTransactionManager(Qualifier(slaveDataSource) DataSource dataSource) { return new DataSourceTransactionManager(dataSource); } }3.2 方式二原生 AbstractRoutingDataSource 动态路由3.2.1 实现原理Spring 提供的原生动态数据源抽象类通过ThreadLocal 存储当前数据源标识重写路由方法运行时根据标识动态切换数据源底层为单 SqlSessionFactory 多数据源路由。3.2.2 优缺点分析✅优点原生无依赖轻量化动态切换支持运行时动态切换无需分包适合简单读写分离、少量动态数据源场景❌缺点需要手动写 AOP、手动管理数据源标识、清除上下文原生不支持事务切换开启事务后数据源锁定无法动态切换动态新增数据源繁琐需手动注册 Bean多数据源事务完全无法原生支持3.2.3 适用场景简单读写分离、无跨库事务、少量动态数据源的中小型后台项目。3.2.4 核心精简代码// 1.数据源上下文工具类 public class DataSourceContextHolder { private static final ThreadLocalString DATA_SOURCE_KEY new ThreadLocal(); public static void setDataSource(String key) { DATA_SOURCE_KEY.set(key); } public static String getDataSource() { return DATA_SOURCE_KEY.get(); } public static void clear() { DATA_SOURCE_KEY.remove(); } } // 2.自定义动态数据源路由 public class DynamicRoutingDataSource extends AbstractRoutingDataSource { Override protected Object determineCurrentLookupKey() { return DataSourceContextHolder.getDataSource(); } } // 3.数据源注册配置 Configuration public class DynamicDataSourceConfig { Bean public DynamicRoutingDataSource dynamicDataSource() { MapObject, Object dataSourceMap new HashMap(); // 注册主从数据源 dataSourceMap.put(master, masterDataSource()); dataSourceMap.put(slave, slaveDataSource()); DynamicRoutingDataSource routingDataSource new DynamicRoutingDataSource(); routingDataSource.setTargetDataSources(dataSourceMap); routingDataSource.setDefaultTargetDataSource(masterDataSource()); return routingDataSource; } }3.3 方式三MyBatis-Plus Dynamic-Datasource 动态数据源企业首选3.3.1 实现原理基于 Spring AbstractRoutingDataSource 封装增强注解驱动 AOP 自动切换自动管理 ThreadLocal 上下文、自动清除、支持事务内合理切换、动态刷新数据源配置是目前后台项目使用率最高的方案。3.3.2 优缺点分析✅优点极致简洁一行注解DS(数据源名)完成切换自动上下文管理无内存泄漏风险支持配置中心动态刷新、热更新数据源兼容 MyBatis-Plus 全功能适配读写分离、多租户提供专属事务方案解决大部分跨库事务问题❌缺点依赖第三方开源组件需匹配版本适配 SpringBoot3强一致性分布式事务仍需 Seata 辅助3.3.3 适用场景90% 企业后台、SaaS多租户、读写分离、多动态数据源、需要事务支持的核心业务。3.3.4 完整可上线代码SpringBoot3 适配1、引入最新依赖dependency groupIdcom.baomidou/groupId artifactIddynamic-datasource-spring-boot-starter/artifactId version4.3.0/version /dependency2、yml 多数据源配置spring: datasource: dynamic: primary: master # 默认主库 strict: false datasource: master: url: jdbc:mysql://127.0.0.1:3306/db_master?useSSLfalseserverTimezoneAsia/Shanghai username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver slave: url: jdbc:mysql://127.0.0.1:3306/db_slave?useSSLfalseserverTimezoneAsia/Shanghai username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver3、业务层注解切换核心用法Service public class UserService { // 默认主库 public ListUser getMasterUser() { return userMapper.selectList(null); } // 手动切换从库 DS(slave) public ListUser getSlaveUser() { return userMapper.selectList(null); } }3.4 方式四中间件代理方案Sharding-JDBC/MyCat3.4.1 实现原理通过中间件统一代理数据库层业务层零感知多数据源由中间件自动完成分库分表、读写分离、数据源路由、分布式事务管控。3.4.2 优缺点分析✅优点业务代码无侵入无需手动切换数据源支持分库分表、海量数据分片、自动读写分离原生支持分布式事务、数据容错、负载均衡❌缺点配置复杂、学习成本高轻量后台项目过于臃肿性能有轻微损耗运维成本高不适合简单多数据源场景3.4.3 适用场景大数据量、分库分表、分布式架构、高并发中台级后台系统。四、四种多数据源方案全方位对比选型速查表实现方案动态切换代码侵入事务支持运维成本适用场景分包多SqlSession❌ 静态固定中分包约束单库事务ok跨库不支持低固定分库、简单后台原生RoutingDataSource✅ 动态切换高手动编码事务中无法切换中简单读写分离、无跨库事务Dynamic-Datasource框架✅ 注解动态切换极低注解驱动支持弱一致性跨库事务极低90%企业后台、多租户、读写分离Sharding-JDBC中间件✅ 全自动代理零侵入支持强一致分布式事务高分库分表、高并发分布式系统五、重难点多数据源事务完整解决方案5.1 核心问题为什么原生 Transactional 失效Spring 原生事务管理器DataSourceTransactionManager仅绑定单个数据源多库切换后不同数据库连接不属于同一个事务上下文A库提交成功、B库异常时A库无法回滚直接导致数据不一致。5.2 三种多数据源事务方案按一致性强弱排序5.2.1 方案1Dynamic-Datasource 本地事务最终一致性轻量首选框架内置DSTransactional注解替代原生事务注解实现多数据源事务联动回滚底层基于事务嵌套上下文绑定适合绝大多数后台业务。实战代码Service public class OrderBusinessService { // 多库事务统一管控 DSTransactional public void createOrder() { // 操作主库 orderMapper.insert(order); // 切换从库操作日志库 DS(slave) void saveLog(){ logMapper.insert(log); } saveLog(); // 模拟异常全部回滚 int i 1 / 0; } }✅ 优点零配置、极简、性能高、适配绝大多数后台业务❌ 缺点不支持跨服务分布式事务仅本地多库5.2.2 方案2JTAAtomikos 强一致性事务2PC 本地多库强一致方案1、核心原理详解JTAJava Transaction API是 Java 标准分布式事务规范定义了跨资源多数据库、消息队列的事务统一管控标准底层基于2PC二阶段提交实现强数据一致性。SpringBoot3 原生舍弃老旧 Bitronix默认适配Atomikos开源独立事务管理器无需依赖应用服务器容器可独立运行在微服务、单体项目中。2PC 核心执行流程阶段一准备阶段PrepareAtomikos 作为全局事务管理器统一调度所有参与事务的多数据源校验数据库连接可用性、SQL 合法性、资源锁定所有数据源全部准备成功才进入提交阶段任意一个数据源准备失败直接触发全局回滚。阶段二提交/回滚阶段Commit/Rollback所有数据源准备成功全局统一提交事务任意节点准备失败、业务抛出异常所有数据源统一回滚严格保证多库数据要么全成功、要么全回滚。核心特性要求数据库必须支持XA 协议MySQL、PostgreSQL、Oracle 主流数据库均原生支持是单体项目多数据源无侵入、强一致的原生解决方案。2、主流平台适配场景适配 SpringBoot3 单体管理后台、老旧遗留系统改造适用于低并发、强数据一致的核心业务账单对账、数据归档、批量迁移无需部署中间件纯代码配置即可落地轻量化无运维成本3、完整实战代码SpringBoot3 最新适配第一步引入 Atomikos 官方依赖!-- SpringBoot3 原生JTA-Atomikos分布式事务 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-jta-atomikos/artifactId /dependency第二步YAML 多数据源 XA 配置spring: datasource: # 主库XA配置 master: xa-url: jdbc:mysql://127.0.0.1:3306/db_master?useSSLfalseserverTimezoneAsia/ShanghaiallowMultiQueriestrue username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver # 从库XA配置 slave: xa-url: jdbc:mysql://127.0.0.1:3306/db_slave?useSSLfalseserverTimezoneAsia/ShanghaiallowMultiQueriestrue username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver # Atomikos事务配置 jta: atomikos: properties: max-timeout: 300 default-jta-timeout: 120第三步XA 多数据源事务管理器配置类Configuration public class AtomikosXaDataSourceConfig { // 主库XA数据源 Bean(masterDataSource) Primary ConfigurationProperties(prefix spring.datasource.master) public AtomikosDataSourceBean masterDataSource() { AtomikosDataSourceBean xaDataSource new AtomikosDataSourceBean(); // MySQL XA驱动绑定 xaDataSource.setXaDataSourceClassName(com.mysql.cj.jdbc.MysqlXADataSource); // 唯一资源标识多数据源必须不同 xaDataSource.setUniqueResourceName(master-db); // 连接池配置 xaDataSource.setMaxPoolSize(20); xaDataSource.setMinPoolSize(5); xaDataSource.setBorrowConnectionTimeout(60); return xaDataSource; } // 从库XA数据源 Bean(slaveDataSource) ConfigurationProperties(prefix spring.datasource.slave) public AtomikosDataSourceBean slaveDataSource() { AtomikosDataSourceBean xaDataSource new AtomikosDataSourceBean(); xaDataSource.setXaDataSourceClassName(com.mysql.cj.jdbc.MysqlXADataSource); xaDataSource.setUniqueResourceName(slave-db); xaDataSource.setMaxPoolSize(20); xaDataSource.setMinPoolSize(5); xaDataSource.setBorrowConnectionTimeout(60); return xaDataSource; } // 全局JTA事务管理器统一管控多数据源 Bean public JtaTransactionManager jtaTransactionManager() { UserTransaction userTransaction new UserTransactionImp(); UserTransactionManager transactionManager new UserTransactionManager(); // 关闭强制关闭避免事务中断 transactionManager.setForceShutdown(false); JtaTransactionManager jtaTm new JtaTransactionManager(userTransaction, transactionManager); // 允许自定义事务隔离级别 jtaTm.setAllowCustomIsolationLevels(true); return jtaTm; } }第四步业务层事务实战多库联动回滚Service public class XaTransactionBusinessService { Autowired private UserMapper userMapper; // 主库Mapper Autowired private OperationLogMapper logMapper; // 从库Mapper // 原生Transactional生效全局管控多数据源事务 Transactional(rollbackFor Exception.class) public void multiDbTxTest() { // 1.操作主库新增用户数据 User user new User(); user.setUsername(test-xa); user.setAge(20); userMapper.insert(user); // 2.切换从库新增操作日志 DS(slave) void saveLog(){ OperationLog log new OperationLog(); log.setOperateContent(XA分布式事务测试); logMapper.insert(log); } saveLog(); // 模拟异常触发全局2PC回滚主库、从库数据全部回滚 int error 1 / 0; } }4、深度优缺点分析✅核心优点绝对强一致性标准2PC协议多数据源事务原子性百分百保障无数据不一致风险零中间件依赖无需部署 Seata、MQ 等组件纯本地配置即可实现分布式事务原生适配 Spring 事务Transactional直接生效代码无侵入❌致命缺点生产核心避坑性能极差2PC 存在长时间资源锁定、阻塞等待高并发场景吞吐量暴跌存在事务悬挂风险准备阶段成功、提交阶段宕机会导致数据库资源死锁占用不支持跨服务事务仅局限于本地多数据源微服务架构完全不适用长事务阻塞严重不适合接口类、高并发后台业务生产选型结论仅用于老旧单体系统、低并发离线任务、数据批量迁移新项目、高并发业务禁止使用。5.2.3 方案3Seata 分布式事务企业微服务/多数据源通用首选1、核心原理详解Seata 是阿里开源的一站式分布式事务解决方案彻底解决 JTA 性能短板适配本地多数据源 跨微服务双场景是目前互联网企业、中台后台、SaaS 系统的绝对主流方案。生产默认采用AT 模式无侵入自动事务。Seata AT 模式核心原理四阶段机制阶段1开启事务业务方法执行前Seata 生成全局唯一 XID绑定当前线程上下文所有本地数据源操作自动归属同一全局事务。阶段2快照记录Undo Log执行业务 SQL 时Seata 自动拦截前置查询数据库数据生成前置快照、后置记录更新后快照存入 undo_log 日志表无业务侵入。阶段3本地提交单数据源本地事务优先提交释放数据库锁极大提升并发性能规避 2PC 长阻塞问题。阶段4全局决议所有分支执行成功全局事务提交异步删除 undo_log 日志任意分支异常全局触发回滚通过 undo_log 快照还原数据保证多库数据一致。核心优势牺牲极小最终一致性延迟换取超高并发性能无锁阻塞、适配高并发后台业务完美兼容多数据源动态切换场景。2、主流平台整合方案当前企业主流技术栈整合SpringBoot3 Dynamic-Datasource4.3.0 Seata1.7.x NacosNacos托管 Seata 配置、多数据源动态配置实现热更新Dynamic-Datasource负责多数据源注解切换、上下文管理Seata全局管控多库事务、跨服务事务保障数据一致性适配所有中台后台、SaaS多租户、微服务架构系统3、完整实战整合代码可直接上线第一步核心依赖引入!-- 动态多数据源 -- dependency groupIdcom.baomidou/groupId artifactIddynamic-datasource-spring-boot-starter/artifactId version4.3.0/version /dependency !-- Seata SpringBoot3 适配依赖 -- dependency groupIdio.seata/groupId artifactIdseata-spring-boot-starter/artifactId version1.7.2/version /dependency第二步YAML 统一配置Nacos 可托管spring: # 多数据源配置 datasource: dynamic: primary: master strict: false datasource: master: url: jdbc:mysql://127.0.0.1:3306/db_master?useSSLfalseserverTimezoneAsia/Shanghai username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver slave: url: jdbc:mysql://127.0.0.1:3306/db_slave?useSSLfalseserverTimezoneAsia/Shanghai username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver # Seata全局事务配置 seata: application-id: admin-multi-ds tx-service-group: default_tx_group service: vgroup-mapping: default_tx_group: default config: type: nacos registry: type: nacos第三步数据库前置准备所有数据源执行所有参与事务的数据库master、slave必须新建 undo_log 表Seata 依赖该表实现快照回滚CREATE TABLE IF NOT EXISTS undo_log ( id BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT 主键ID, 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 日志状态 0:正常 1:已回滚, log_created DATETIME NOT NULL COMMENT 创建时间, log_modified DATETIME NOT NULL COMMENT 修改时间, PRIMARY KEY (id), UNIQUE KEY ux_undo_log (xid,branch_id) ) ENGINE InnoDB DEFAULT CHARSET utf8mb4 COMMENT Seata事务回滚日志表;第四步业务层多数据源事务实战核心生产写法Service public class SeataMultiDsTransactionService { Autowired private UserMapper userMapper; Autowired private OrderLogMapper orderLogMapper; // Seata全局事务注解替代原生事务管控多数据源跨服务事务 GlobalTransactional(rollbackFor Exception.class) public void businessMultiDbOperate() { // 1.操作主库新增订单核心数据 User user new User(); user.setUsername(seata-test-user); userMapper.insert(user); // 2.动态切换从库新增订单日志 DS(slave) void saveOrderLog(){ OrderLog log new OrderLog(); log.setOrderNo(TX20260625001); log.setOperateDesc(多数据源Seata事务执行); orderLogMapper.insert(log); } saveOrderLog(); // 模拟业务异常全局事务回滚主从库数据全部撤销 // int i 1 / 0; } }4、深度优缺点 生产适配场景✅核心优点高性能无阻塞基于 AT 模式规避 2PC 长事务阻塞并发性能接近单库事务双场景适配同时支持本地多数据源事务、跨微服务分布式事务代码极低侵入仅需GlobalTransactional注解无需修改数据源逻辑适配动态数据源完美兼容 Dynamic-Datasource 动态切换无事务锁定问题生态完善支持 Nacos 热更新、监控告警、事务日志溯源适配企业中台架构❌缺点存在极小概率空回滚、幂等性问题生产可通过兜底配置规避需要部署 Seata Server 服务有少量运维成本5.3、主流方案最终选型对比面试生产必背事务方案一致性级别并发性能运维成本适用架构生产推荐度DSTransactional框架本地最终一致极高零成本单体轻量后台⭐⭐⭐⭐⭐普通业务首选JTAAtomikos2PC强一致极低低成本老旧单体、离线任务⭐⭐新项目禁用Seata AT弱强一致高中微服务、中台、核心业务⭐⭐⭐⭐⭐分布式标配基于 XA 协议二阶段提交实现多数据源强一致性事务所有库同时成功、同时回滚。✅ 优点数据强一致、无数据不一致风险 ❌ 缺点性能差、配置繁琐、长事务阻塞、不适合高并发5.4 业务选型口诀普通后台、非核心资金业务优先DSTransactional轻量事务核心账务、强一致业务使用 Seata AT 分布式事务老旧单体系统、极致强一致选用 JTAAtomikos不推荐新项目六、生产多数据源避坑总结禁止原生 Transactional做多库事务百分百出现数据不一致事务内谨慎切换数据源原生路由数据源事务中锁定框架版需用专属事务注解读写分离禁止写操作走从库通过注解严格区分读写数据源动态数据源必须配置默认库避免上下文清空导致空指针高并发场景禁用JTA性能瓶颈严重优先Seata/框架事务七、全文总结1、轻量化后台、快速开发首选Dynamic-Datasource 注解动态数据源简洁高效、事务可控2、简单固定分库、无动态切换选用分包多数据源原生稳定零依赖3、大数据量分库分表、分布式架构选用Sharding-JDBC中间件方案4、多库事务优先框架自带DSTransactional核心分布式业务接入Seata彻底解决数据一致性问题。