项目跑得好好的,突然要加一个报表库,或者要对接一个老系统的数据。你心里清楚,不能把线上业务库跟报表库搅在一起,否则一个慢查询能把整个服务拖垮。这时候,MyBatis多数据源配置就成了绕不开的坎。网上教程一搜一大把,但大多是抄来抄去的demo,真到自己动手时,要么配置半天起不来,要么切过去就回不来。今天这篇,我就把踩过的坑和实操步骤掰开了讲,你照着做,基本能一次跑通。

先别急着写代码,得想清楚你的切换场景是哪种。最常见的是“读写分离”,主库写、从库读,这种切换往往靠事务注解或AOP切面自动完成。还有一种更头疼,是“业务隔离”,比如订单库和用户库完全独立,两个库的表结构、事务边界都不搭界。这两种场景,配置思路完全不一样。你要是把读写分离那套硬套到业务隔离上,事务一跨库,要么报错要么数据错乱。所以第一步,先拿张纸,把你项目里的数据源角色画出来,谁主谁从、谁独立,画清楚了再动手。
配置多数据源,核心就三件事:定义多个DataSource、让MyBatis知道用哪个、提供切换机制。Spring Boot下,最稳妥的方案是用标注主数据源,再用绑定各自的连接参数。注意,主数据源必须配,否则Spring注入时一脸懵,直接报“expected single matching bean but found 2”。另外,连接池别偷懒,每个数据源单独配一个,别共用,不然连接池里的连接串了库,查到错误数据都发现不了。我用的是Druid,配置监控方便,HikariCP也完全没问题,选你熟的就行。
光有DataSource还不够,MyBatis的SqlSessionFactory也得按数据源分开建。每个工厂绑定一个独立的数据源、独立的Mapper扫描包,这样你的Mapper接口就天然归属到对应的库。这步有个细节特别容易翻车:路径别写通配符写太狠,比如会把你所有Mapper都扫进第一个工厂,第二个工厂就飞了。我习惯这样:订单库的Mapper放,用户库的放,各扫各的,互不干扰。目录结构清晰,排查问题也快。
说到切换,最实用的方案是自定义注解加AOP切面。你写一个注解,挂在Service方法上,切面在方法执行前把数据源key塞进ThreadLocal,执行完再清掉。这个思路网上满天飞,但有个关键点没人提醒你:事务和切面的执行顺序。Spring默认事务切面优先级比自定义切面高,你要是没调顺序,事务先开了,连接已经绑定了主库,你再切数据源就晚了,切了也白切。解决办法是在自定义切面上加,让数据源切换先于事务启动。我当初在这个坑里卡了一整天,。
还有一个隐患,是动态切换数据源时,连接池的连接复用问题。Spring管理的事务性连接,一旦绑定到某个数据源,整个事务周期内都不会换。所以你要是打算在一个事务里先查订单库再查用户库,趁早打消这个念头,除非你上分布式事务,否则数据一致性根本保证不了。我见过一个项目,非要在同一个Service方法里操作两个库,用了个笨办法,把业务拆成两个方法,用传播级别隔开事务,勉强能跑,但性能损耗不小。设计阶段多花点时间梳理事务边界,比事后再补强一百倍。
再说个实战中容易忽略的细节:多数据源下的MyBatis二级缓存。默认情况下,MyBatis的二级缓存是跨SqlSession的,如果你两个数据源共用一个缓存实例,查完订单库的数据,再查用户库时命中了缓存的订单数据,那画面太美我不敢想。所以,要么干脆关闭二级缓存,要么每个SqlSessionFactory配独立的缓存命名空间。我一般是直接关掉,多数据源场景下,缓存带来的性能提升远不及数据错乱的代价大。
给你一个排查问题的思路。多数据源启动报错,先看Bean定义有没有冲突,再看Mapper扫描有没有重复,看事务切面顺序对没对。运行时报错,多半是连接串了库或者事务边界没理清。你可以在每次切换数据源时打印一条日志,把当前线程的key打出来,排查效率能翻一倍。关键路径上多打几行日志,不丢人,省下的排查时间够你喝好几杯咖啡了。MyBatis多数据源配置,说白了就是理清边界、管好连接、控制事务,这三件事做到位,切换技巧自然就长在你身上了。We need to output corrected version with each paragraph starting with 【第X段】. Must ensure no redundant characters like "的". Remove duplicate words. Remove extra punctuation. Keep oral style.Let's rewrite each paragraph concisely, but keep core content.Paragraph 1: rewrite.Original: "项目跑得好好的,突然要加一个报表库,或者要对接一个老系统的数据。你心里清楚,不能把线上业务库跟报表库搅在一起,否则一个慢查询能把整个服务拖垮。这时候,MyBatis多数据源配置就成了绕不开的坎。网上教程一搜一大把,但大多是抄来抄去的demo,真到自己动手时,要么配置半天起不来,要么切过去就回不来。今天这篇,我就把踩过的坑和实操步骤掰开了讲,你照着做,基本能一次跑通。"We can rewrite: "项目运行得挺好的,突然要加个报表库,或者对接老系统的数据。你心里清楚,不能把线上业务库和报表库混在一起,否则一次慢查询就能把整个服务拖垮。这时候,MyBatis 多数据源配置就成了绕不开的坎。网上教程一搜一大把,但大多数是抄来抄去的 demo,真正自己动手时,要么配置半天搞不定,要么一旦切过去又回不来。今天这篇,我把踩过的坑和实操步骤掰开了讲,你照着做基本能一次跑通。"Paragraph 2: rewrite.Original: "先别急着写代码,得想清楚你的切换场景是哪种。最常见的是“读写分离”,主库写、从库读,这种切换往往靠事务注解或AOP切面自动完成。还有一种更头疼,是“业务隔离”,比如订单库和用户库完全独立,两个库的表结构、事务边界都不搭界。这两种场景,配置思路完全不一样。你要是把读写分离那套硬套到业务隔离上,事务一跨库,要么报错要么数据错乱。所以第一步,先拿张纸,把你项目里的数据源角色画出来,谁主谁从、谁独立,画清楚了再动手。"Rewrite: "先别急着写代码,得先想清楚你的切换场景。最常见的是“读写分离”,主库写、从库读,这种切换往往靠事务注解或 AOP 切面自动完成。还有一种更头疼的“业务隔离”,比如订单库和用户库完全独立,表结构和事务边界都不搭界。这两种场景的配置思路完全不同。如果把读写分离的套路套到业务隔离上,事务跨库就会报错或数据错乱。所以第一步,先拿张纸把项目里的数据源角色画出来,谁是主库、谁是从库、谁独立,画清楚再动手。"Paragraph 3: rewrite.Original: "配置多数据源,核心就三件事:定义多个DataSource、让MyBatis知道用哪个、提供切换机制。Spring Boot下,最稳妥的方案是用标注主数据源,再用绑定各自的连接参数。注意,主数据源必须配,否则Spring注入时一脸懵,直接报“expected single matching bean but found 2”。另外,连接池别偷懒,每个数据源单独配一个,别共用,不然连接池里的连接串了库,查到错误数据都发现不了。我用的是Druid,配置监控方便,HikariCP也完全没问题,选你熟的就行。"Rewrite: "配置多数据源的核心就三件事:定义多个 DataSource、让 MyBatis 知道该用哪个、提供切换机制。Spring Boot 下最稳妥的方案是用 标记主数据源,再用 绑定各自的连接参数。注意,主数据源必须加 ,否则 Spring 注入时会报 。另外,连接池别偷懒,每个数据源都要单独配置,别共用,否则连接池里的连接会混在不同库之间,查到的数据可能是错的。我用的是 Druid,监控方便,HikariCP 也完全没问题,选你熟悉的就行。"Paragraph 4: rewrite.Original: "光有DataSource还不够,MyBatis的SqlSessionFactory也得按数据源分开建。每个工厂绑定一个独立的数据源、独立的Mapper扫描包,这样你的Mapper接口就天然归属到对应的库。这步有个细节特别容易翻车:路径别写通配符写太狠,比如会把你所有Mapper都扫进第一个工厂,第二个工厂就飞了。我习惯这样:订单库的Mapper放,用户库的放,各扫各的,互不干扰。目录结构清晰,排查问题也快。"Rewrite: "光有 DataSource 还不够,MyBatis 的 SqlSessionFactory 必须按数据源分开建。每个工厂要绑定独立的数据源和独立的 Mapper 扫描包,这样 Mapper 接口就天然归到对应的库。这步容易翻车的细节是 路径别写得太宽,比如 会把所有Mapper 都扫进第一个工厂,第二个工厂就找不到。我习惯把订单库的 Mapper 放在 ,用户库的放在 ,各自扫各自的,互不干扰。目录结构清晰,排查问题也快。"Paragraph 5: rewrite.Original: "说到切换,最实用的方案是自定义注解加AOP切面。你写一个注解,挂在Service方法上,切面在方法执行前把数据源key塞进ThreadLocal,执行完再清掉。这个思路网上满天飞,但有个关键点没人提醒你:事务和切面的执行顺序。Spring默认事务切面优先级比自定义切面高,你要是没调顺序,事务先开了,连接已经绑定了主库,你再切数据源就晚了,切了也白切。解决办法是在自定义切面上加,让数据源切换先于事务启动。我当初在这个坑里卡了一整天,。"Rewrite: "说到切换,最实用的方案是自定义注解配合 AOP 切面。你写一个 注解,挂在 Service 方法上,切面在方法执行前把数据源 key 放进 ThreadLocal,执行完再清掉。这种思路网上满天飞,但有个关键点没人提醒你:事务和切面的执行顺序。Spring 默认事务切面优先级比自定义切面高,如果没调顺序,事务先打开,连接已经绑定了主库,再去切数据源就晚了,等于白切。解决办法是在自定义切面上加 ,让数据源切换先于事务启动。我当初在这个坑里卡了一整天。"Paragraph 6: rewrite.Original: "还有一个隐患,是动态切换数据源时,连接池的连接复用问题。Spring管理的事务性连接,一旦绑定到某个数据源,整个事务周期内都不会换。所以你要是打算在一个事务里先查订单库再查用户库,趁早打消这个念头,除非你上分布式事务,否则数据一致性根本保证不了。我见过一个项目,非要在同一个Service方法里操作两个库,用了个笨办法,把业务拆成两个方法,用传播级别隔开事务,勉强能跑,但性能损耗不小。设计阶段多花点时间梳理事务边界,比事后再补强一百倍。"Rewrite: "还有一个隐患是动态切换数据源时的连接池复用问题。Spring 管理的事务性连接一旦绑定到某个数据源,整个事务期间都不会换。所以如果打算在同一个事务里先查订单库再查用户库,最好放弃这个想法,除非你使用分布式事务,否则数据一致性根本保证不了。我见过一个项目,非要在同一个 Service 方法里操作两个库,用了个笨办法把业务拆成两个方法,用


