Java中数据库连接池不负责主备切换,仅管理连接;主备路由由AbstractRoutingDataSource等动态数据源机制实现,各数据源需配置独立连接池(如HikariCP/Druid),并通过注解或拦截器控制切换时机与事务一致性。

Java 中数据库连接池本身不直接处理主备切换,它只负责管理连接的创建、复用与回收。主备切换是数据访问层的路由决策问题,连接池(如 HikariCP、Druid)只是被路由后的“执行单元”。关键在于:连接池要配合动态数据源机制,而不是自己实现切换逻辑。
连接池不参与路由,只服务选定的数据源
每个真实数据源(如主库 masterDataSource、备库 backupDataSource)都应配置独立的连接池实例。这些连接池在应用启动时就初始化完成,保持各自健康连接。AbstractRoutingDataSource 不持有连接,也不管理连接池,它只根据当前上下文(如 ThreadLocal 中的 key)返回对应的数据源引用——这个引用背后,才是 HikariCP 等连接池在工作。
- 主库连接池:配置 url=jdbc:mysql://master:3306/db,最大连接数设为较高值(如 20)
- 备库连接池:配置 url=jdbc:mysql://slave:3306/db,最大连接数可略低(如 10),并开启 testWhileIdle 和 validationQuery 防止空闲失效
- 两个连接池都启用 connectionInitSql(如 SELECT 1)和 keepAlive(HikariCP)确保连接活跃
切换动作必须前置到路由层,不能后置到 catch 块
一旦 SQL 执行抛出 SQLException(如 Connection refused、Communications link failure),说明当前连接池已不可用或网络中断。此时在 DAO 的 catch 块里调用 DataSourceContextHolder.set("slave") 并重试,会导致事务边界破坏、连接状态混乱、主从延迟数据误读等问题。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 正确做法是:将“主→备”降级逻辑封装在重试拦截器中,首次请求用 master 路由键,失败后由 @Retryable 触发第二次执行,并自动切换为 slave 键
- 连接池对切换无感知——它只响应 AbstractRoutingDataSource 返回的那个 DataSource 实例的 getConnection() 调用
- 因此,连接池无需 reload 或 closeAll,只要其 underlying url 可达,就能持续提供连接
连接池健康检测需与主备状态解耦
不要让连接池自己去探测“主库是否宕机”。HikariCP 的 connection-test-query 只验证单个连接有效性,不能替代高可用判断。主备状态应由外部机制维护:
立即学习“Java免费学习笔记(深入)”;
- 通过定时心跳(如 SELECT @@read_only)+ 自定义 HealthIndicator 上报主从角色
- 结合 Spring Cloud LoadBalancer 或 Nacos 实例健康状态,动态更新路由策略
- MyCat / ShardingSphere 等中间件已内置 switchType=2(基于 show slave status)机制,Java 应用只需消费其统一 JDBC URL
写操作绝不走备库连接池
即使你为备库配了完全相同的 HikariCP 连接池,也必须禁止写请求使用它。MySQL 备库默认 read_only=ON,强行执行 INSERT/UPDATE 会直接抛出 SQLState 25000 或 ERROR 1290;PostgreSQL 备库则根本拒绝写连接。
- Service 层写方法应强制绑定 @Master 注解(通过 AOP 设置路由键),且该注解不可被运行时覆盖
- 备库连接池仅用于读请求的降级执行,且需配合 @ReadOnlyTransaction 或显式设置 readOnly=true
- 连接池配置中建议设置 connection-properties=readOnly=true,从驱动层降低误写风险

















