mybatis多数据源动态切换核心在于按需路由而非静态配置,推荐使用dynamic-datasource-starter实现:通过@ds注解声明数据源,配合yaml分组配置与自动负载均衡;手动继承abstractroutingdatasource适用于深度定制场景,需结合threadlocal、aop及动态刷新能力支撑读写分离、故障降级与多租户扩展。

Java 中 MyBatis 多数据源动态切换,核心是让一次请求能按需路由到不同数据库,不改业务逻辑、不硬编码数据源。关键不在“配多少”,而在“怎么切”和“谁来管”。
用 dynamic-datasource-starter 快速落地
这是目前最主流、最省心的方式,尤其适合 Spring Boot 项目:
- 引入依赖:
com.baomidou:dynamic-datasource-spring-boot-starter(推荐 3.5.0+ 版本) - 在 application.yml 中分组定义数据源,例如:
spring: datasource: dynamic: primary: master datasource: master: url: jdbc:mysql://db1:3306/app_main username: root password: 123 slave_1: url: jdbc:mysql://db2:3306/app_read username: readonly password: 456 slave_2: url: jdbc:mysql://db3:3306/app_read2 username: readonly password: 456 - 在 Service 方法或 Mapper 接口上加 @DS("slave_1") 注解,执行时自动走对应库
- 支持默认 fallback、负载均衡(轮询/随机)、健康检查,开箱即用
手动实现 AbstractRoutingDataSource 路由
适合需要深度控制切换逻辑,或不能引入第三方 starter 的场景:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 定义多个 DataSource Bean(如 masterDataSource、slaveDataSource),用
@ConfigurationProperties绑定不同前缀配置 - 写一个类继承 AbstractRoutingDataSource,重写
determineCurrentLookupKey() - 搭配 ThreadLocal
存储当前线程的数据源标识(如 "master" 或 "slave_1") - 通过 AOP 拦截带自定义注解(如
@DS("slave"))的方法,在执行前调用DataSourceContextHolder.set("slave") - 确保 SqlSessionFactory 使用的是这个动态数据源 Bean,而非单个 DataSource
读写分离的语义要对齐业务逻辑
切换不是目的,合理分流才是重点:
- 写操作(INSERT/UPDATE/DELETE)必须走主库;加了
@Transactional的方法默认也走主库 - 读操作(SELECT)可走从库,但注意:若事务中读取刚写入的数据,必须走主库,否则可能查不到(主从延迟)
- 避免在事务内强制切从库;如确需,用
@Transactional(propagation = Propagation.NOT_SUPPORTED)暂停当前事务 - 查询类接口建议显式标注
@DS("slave"),写操作方法不标,默认走主库
运行时管理与扩展能力
不只是启动时配好,还要支持后续灵活调整:
- 动态新增数据源:监听配置中心(如 Nacos)变更,重新构建
targetDataSourcesMap 并刷新路由 - 故障自动降级:配置
seata或自定义健康检查,连接失败时自动 fallback 到备用库 - 日志追踪:在 AOP 切面中记录每次切换的 key 和耗时,便于排查路由异常
- 多租户场景下,可将租户 ID 作为 lookupKey,从数据库或缓存中查出对应数据源名再设置
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










