为什么需要多数据源读写分离是最常见的场景。主库扛写从库扛读流量分摊性能翻倍。另一个场景是多租户不同客户的数据物理隔离各连各的库。还有遗留系统整合新业务用MySQL老系统跑Oracle谁也离不开谁。需求摆在那里你不可能用一套数据源硬扛。需求驱动架构而不是架构迁就习惯。古人说兵来将挡水来土掩。多数据源就是那块挡水的土。核心原理AbstractRoutingDataSourceSpring提供了一个抽象类AbstractRoutingDataSource它本身不连数据库而是持有一个Mapkey是数据源标识value是真实的DataSource。每次请求数据库时它调用determineCurrentLookupKey()方法问你“这次用哪个库”你返回一个key它就从Map里取出对应的数据源。切换的时机在方法调用前而不是连接建立后。这个设计把“选哪个库”的逻辑从业务代码里抽离出来你只需要在合适的地方设置key剩下的交给框架。懂了这个你就知道多数据源不是什么黑魔法只是路由表加一个决策函数。配置三步走别想复杂了第一步在application.yml里配多套数据源用spring.datasource.master和spring.datasource.slave区分各自设好url、用户名、密码、连接池参数。第二步写一个配置类用ConfigurationProperties把两套配置分别绑定到两个DataSourceBean上然后用Primary标记主数据源。第三步继承AbstractRoutingDataSource实现determineCurrentLookupKey()把两个数据源塞进Map设好默认数据源。最后用AOP加自定义注解在Service方法上标注DataSource(slave)切面里把key设置到ThreadLocal。三步走完多数据源就能跑了剩下的都是调优。最大的坑事务管理多数据源最容易被忽略的是事务。Spring的Transactional默认只认一个事务管理器你切了数据源事务不会跟着切。主库写、从库读如果读操作也在事务里可能读到脏数据或者直接报错。解决办法是给每个数据源配独立的事务管理器在Transactional里指定transactionManager。事务边界和数据源边界必须对齐否则数据一致性就是空中楼阁。另外分布式事务不要硬扛能用本地事务加最终一致性解决就别上Seata。连接池和默认数据源别让细节翻车每个数据源都要配连接池HikariCP的maximum-pool-size别用默认值根据业务量算。默认数据源一定要设否则路由key为空时直接抛异常。AOP切面要注意执行顺序别被其他切面覆盖了ThreadLocal的值。细节不坑人忽略细节才坑人。多数据源配置本身不复杂复杂的是你没想清楚为什么要用、什么时候切、切错了怎么办。想清楚这三个问题配置就是水到渠成的事。
