MyBatis 配置多数据源的核心在于将数据源、SqlSessionFactory、事务管理器分别独立声明,并通过包扫描或注解方式实现读写分离与业务隔离。 只要理清依赖关系与事务边界,多数据源配置并不复杂,但必须避免事务跨数据源导致的脏读与性能损耗,下文从配置步骤、常见坑点、生产实践三个层次展开。
多数据源的基础配置架构
MyBatis 本身不管理多数据源,需要借助 Spring 的 DataSource 抽象,推荐使用 分包策略(不同 Mapper 接口放到不同包,每个数据源对应独立包),比动态切换更稳定、可读性更强。
主数据源配置
在 Spring Boot 中,通过 @Primary 标注主数据源,避免其他组件自动注入歧义。
spring:
datasource:
primary:
jdbc-url: jdbc:mysql://192.168.1.10:3306/main
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
从数据源配置
从数据源不添加 @Primary,并指定独立前缀。
spring:
datasource:
secondary:
jdbc-url: jdbc:mysql://192.168.1.11:3306/report
username: read_user
password: 789012
driver-class-name: com.mysql.cj.jdbc.Driver
创建两个 SqlSessionFactory
每个数据源对应一个 SqlSessionFactory,通过 @MapperScan 指定扫描包。
@Configuration
public class DataSourceConfig {
@Bean
@ConfigurationProperties("spring.datasource.primary")
public DataSource primaryDataSource() {
return DataSourceBuilder.create().build();
}
@Bean
@ConfigurationProperties("spring.datasource.secondary")
public DataSource secondaryDataSource() {
return DataSourceBuilder.create().build();
}
@Bean
public SqlSessionFactory primarySqlSessionFactory(@Qualifier("primaryDataSource") DataSource dataSource) throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.s
etDataSource(dataSource);
return bean.getObject();
}
@Bean
public SqlSessionFactory secondarySqlSessionFactory(@Qualifier("secondaryDataSource") DataSource dataSource) throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
return bean.getObject();
}
}
然后在两个 Mapper 包上分别绑定:
@MapperScan(basePackages = "com.example.mapper.primary", sqlSessionFactoryRef = "primarySqlSessionFactory") @MapperScan(basePackages = "com.example.mapper.secondary", sqlSessionFactoryRef = "secondarySqlSessionFactory")
核心结论:分包策略让代码结构清晰,每个 Mapper 只属于一个数据源,不会出现运行时切换的复杂性。
事务管理的关键边界
多数据源下,Spring 默认事务管理器只能指向一个数据源,需要为每个数据源单独配置 DataSourceTransactionManager。
@Bean
public DataSourceTransactionManager primaryTransactionManager(@Qualifier("primaryDataSource") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
@Bean
public DataSourceTransactionManager secondaryTransactionManager(@Qualifier("secondaryDataSource") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
重要:如果业务方法同时操作两个库,不能用 @Transactional 跨越两个数据源。 正确做法是拆分事务,或者使用分布式事务方案(如 Seata),但会引入额外复杂度,小项目建议直接避免跨源事务。
常见坑点与解决方案
驱动类加载失败
- 多数据源时必须显式指定
driver-class-name,否则可能自动探测失败。 - 建议:两个数据源尽量使用同一种数据库类型,避免连接池参数不兼容。
MyBatis 的 mapper.xml 扫描冲突
如果两个数据源的 XML 文件都放在同一路径,会被同一个 SqlSessionFactory 加载。

解决方案:将 XML 文件分包存放,并在 spring.sql.init 或配置中指定 mapper-locations 到不同目录。
事务注解失效
同一个类内部调用 @Transactional 方法会导致事务失效,多数据源下更明显。
建议:将跨库操作放到独立 Service 类中,每个 Service 只操作一个数据源,事务边界控制在单个数据源内。
连接池耗尽
从库连接池被慢查询占满时,主库也会被影响(如果使用 Hikari 默认共享连接池配置)。必须为每个数据源设置独立的 maximum-pool-size 和 minimum-idle。
生产环境优化实践
监控数据源健康状态
- 为每个数据源配置独立的健康检查器。
- 建议使用 Actuator 的
/health端点,并自定义HealthIndicator区分不同数据源状态。
读写分离的扩展思路
如果是从库只读、主库读写,可以在 Service 层用 @ReadOnlyConnection 注解配合 AOP 切换数据源,但这种方式容易造成事务与连接绑定错误,不如分包策略稳定。
MyBatis 插件与缓存
分页插件(如 PageHelper)在多数据源下需要确认 dialect 是否正确。建议每个 SqlSessionFactory 都单独配置插件,不要全局共享。
酷番云经验案例
我们使用酷番云的云数据库实例部署两个 MySQL 8.0 节点,分别作为主库和报表库。 两个节点配置了不同的规格:主库选用高 IOPS 型,报表库选用大内存型,在调整过程中,我们遇到两个实际问题:
- 问题一:报表库的慢查询导致从库 CPU 飙升,因为当时两个数据源共用了同一个 Hikari 连接池的默认配置,我们通过酷番云的监控控制台,发现
active-connections持续打满,随即为两个数据源分别设置了maximum-pool-size=20和maximum-pool-size=10,隔离了资源争抢。 - 问题二

:某个定时任务需要把主库的增量数据同步到报表库,原方案写了
@Transactional跨两个数据源,结果刚提交后就遇到主库节点重启,导致报表库数据缺失。后来我们把同步任务改成先读主库、再写报表库的两个独立事务,并利用酷番云的对象存储暂存同步失败的数据文件,通过重试任务补偿,最终实现了 99.95% 的同步成功率。
核心经验:多数据源架构不是技术炫技,而是为了业务隔离和资源隔离,不要追求一个方法操作两个库,而是让数据在不同库里通过任务或消息异步流转,这样系统才更健壮。
相关问答模块
问1:MyBatis 多数据源配置中,如何实现动态切换数据源?
动态切换通常使用 AbstractRoutingDataSource,通过 ThreadLocal 保存当前数据源标识,在运行时动态决定使用哪个数据源。适合场景是请求级切换、单 Mapper 多库操作但又不关心事务隔离的情况。 但如果业务中需要同时操作两个库且要求强一致,动态切换并不能解决问题,反而会让事务管理变得很混乱,因此优先推荐分包策略,只有少数特殊场景(如按租户切换)才使用动态切换。
问2:多数据源下的分页查询为什么有时会失败?
主要原因有两个:一是分页插件没有正确绑定到每个 SqlSessionFactory,导致拦截器失效;二是分页插件自动探测数据库类型时拿到了错误的连接。解决办法:在每个 SqlSessionFactory 的 setPlugins 中显式添加分页插件,并指定 helperDialect 为对应数据库类型,同时确认数据源的 jdbc-url 没有写错,使用分包策略时,保证每个 Mapper 只归属一个数据源,分页查询就不会串库。
欢迎在评论区分享你的多数据源配置经验,或者遇到过哪些奇葩坑? 如果你正在使用酷番云的数据库实例,可以聊聊你是怎么规划主从库规格的,我们会选出优质评论赠送云计算资源体验券!
图片来源于AI模型,如侵权请联系管理员。作者:酷小编,如若转载,请注明出处:https://www.kufanyun.com/ask/727938.html

