根本原因是连接池复用已断开的旧连接且未启用有效性校验、ons通知或tns刷新机制。具体包括:testonborrow未开启导致失效连接被重复使用;ons未配置致使无法接收rac节点状态变更事件;tnsnames.ora或dns缓存未更新使新连接仍指向下线实例;abstractroutingdatasource路由逻辑未适配切换后状态。

Oracle RAC切换后连接池无法恢复,根本原因不是连接池本身坏了,而是连接池持有的旧连接全部失效,且没有触发重建或自动剔除机制——它还在反复重用已断开的连接,直到超时或主动清理。
连接池复用失效连接,导致持续报错
切换(如RAC实例宕机、DG主备切换、服务failover)后,原连接池中大量连接底层TCP已断开,但连接对象仍处于“已分配未关闭”状态。连接池默认不主动探测连接有效性,每次取连接时直接返回,应用一执行SQL就抛SQLException: IO Error: Connection reset或ORA-03113: end-of-file on communication channel。
- 多数连接池(HikariCP、UCP、Druid)默认
testOnBorrow=false,即借出时不校验连接有效性 -
validationQuery=SELECT 1 FROM DUAL必须显式配置,且需配合testOnBorrow=true或testWhileIdle=true - 若使用
testWhileIdle,还需设置合理的timeBetweenEvictionRunsMillis(建议30000–60000),否则空闲连接长期不检测
TNS解析未刷新,新连接仍打向旧地址
RAC切换后,SCAN或VIP地址背后的服务注册可能已变更,但客户端TNS解析结果缓存未更新,新建连接仍尝试连到已下线的实例IP,表现为ORA-12514: TNS:listener does not currently know of service requested或ORA-12170: TNS:Connect timeout occurred。
- Java进程启动后,
tnsnames.ora被JDBC驱动一次性加载,后续不自动重读;修改文件后必须重启应用 - DNS层面的SCAN IP缓存(如Linux的
/etc/resolv.conf或systemd-resolved)也需手动清空:sudo systemd-resolve --flush-caches - JDBC URL若用Easy Connect格式(如
@//scan:1521/orcl),不走tnsnames解析,也无法触发LOAD_BALANCE=on或FAILOVER_MODE
ONS未启用或配置错误,故障通知失效
Oracle ONS(Oracle Notification Service)是RAC实现秒级连接池清理的关键——它让数据库主动推送节点状态变更事件给JDBC客户端。没配ONS,连接池只能靠被动超时(默认30s+)才发现连接失效。
- 必须在JDBC URL中显式配置
oracle.net.onsConfiguration,例如:oracle.net.onsConfiguration=nodes=rac1:6200,rac2:6200 - 确保所有RAC节点的ONS守护进程运行:
crsctl stat res ora.ons -t,状态应为ONLINE - 防火墙需放行6200端口(TCP),且客户端能
telnet rac1 6200通 - ojdbc8+驱动才完整支持ONS事件回调;ojdbc6/7即使配置了也不会注册监听器
AbstractRoutingDataSource路由逻辑未适配切换后状态
如果用了自定义路由(如继承AbstractRoutingDataSource),切换后若未同步更新数据源健康状态或路由键映射,会导致流量继续发往不可用实例。
- 不能只依赖静态
lookupKey,必须结合实时健康检查(如定时执行SELECT 1 FROM DUAL)动态维护可用节点列表 - 事务方法拦截切面中,若目标数据源不可达,应抛
DataSourceUnavailableException而非静默fallback,避免事务语义错乱 - ThreadLocal绑定的路由键(如
"rac-node1")在切换后未重置,会导致后续请求持续命中已下线节点
真正卡住恢复的,往往不是数据库端切换失败,而是应用侧连接池对“连接已死”毫无感知,又没配ONS、没启校验、没刷新TNS——它只是在一遍遍重试同一个错误路径。











