oracle rac负载均衡需client-side与server-side协同:client-side要求load_balance=on置于每个description内并用description_list包裹多地址,server-side依赖remote_listener指向正确地址且pmon实时上报v$servicemetric.current_load,缺一则连接全打首节点。

不是客户端语言的问题,而是连接初始化方式和TNS解析行为的差异导致负载卡死在首节点。 Java(JDBC)和 .NET(ODP.NET)默认不启用客户端负载均衡,且连接池预热会放大地址选择偏差——哪怕配置了 LOAD_BALANCE=on,只要放错位置或没配 DESCRIPTION_LIST,所有连接仍会落到第一个解析出的地址上。
LOAD_BALANCE=on 必须写在 DESCRIPTION 块内部
很多人把 LOAD_BALANCE=on 放在 CONNECT_DATA 里,或者塞到 DESCRIPTION_LIST 外层,TNS 解析器直接忽略它:
- 错误写法:
(DESCRIPTION=(ADDRESS=...)(CONNECT_DATA=...)(LOAD_BALANCE=on))→ 不生效 - 正确写法(多 VIP 场景):
(DESCRIPTION_LIST=(DESCRIPTION=(ADDRESS=(HOST=rac1-vip))...(LOAD_BALANCE=on))(DESCRIPTION=(ADDRESS=(HOST=rac2-vip))...(LOAD_BALANCE=on))) - 只用 SCAN 地址时,
LOAD_BALANCE=on无效——TNS 没多个地址可选,自动退化为直连第一个返回的 IP
JDBC URL 和 ODP.NET 连接字符串容易漏掉 ADDRESS_LIST 封装
JDBC 的 jdbc:oracle:thin:@(DESCRIPTION_LIST=...) 和 ODP.NET 的 Data Source=(DESCRIPTION_LIST=...) 必须显式包裹多个 DESCRIPTION,否则即使写了 LOAD_BALANCE=on,Oracle Net 也只看到一个地址入口:
- JDBC 示例(关键在括号嵌套):
jdbc:oracle:thin:@(DESCRIPTION_LIST=(LOAD_BALANCE=on)(DESCRIPTION=(ADDRESS=(HOST=rac1-vip)(PORT=1521)))(DESCRIPTION=(ADDRESS=(HOST=rac2-vip)(PORT=1521)))) - ODP.NET 示例需同样结构,且
Load Balancing=true是 .NET 驱动层开关,但底层仍依赖 TNS 解析结果;若 TNS 没提供多地址,驱动层开关无意义 - Spring Boot + HikariCP 默认开启
minimumIdle,启动时批量建连——此时 DNS 缓存未刷新、SCAN 返回 IP 顺序固定,所有连接全打第一个实例
Server-side 负载信息未同步,SCAN Listener 只能 round-robin
即使客户端配置正确,如果服务端没打通负载上报链路,SCAN Listener 根本不知道各节点真实负载,只能按轮询分发:
-
REMOTE_LISTENER必须指向集群内所有节点的 VIP 列表(如LISTENERS_RAC),不能指向单个 VIP、localhost 或错误 GNS 地址 - 改完
REMOTE_LISTENER后必须立即执行ALTER SYSTEM REGISTER,否则 PMON 延迟数分钟才推送V$SERVICEMETRIC.CURRENT_LOAD - 验证:运行
lsnrctl status LISTENER_SCAN1,服务条目中应含load=xx字段;查v$services确认LOAD_BALANCE列为YES
最常被忽略的是连接池冷启动阶段的“时间窗口”问题:配置全对,但第一次建连潮发生在 PMON 上报完成前,或 DNS 缓存未更新,导致全部连接扎堆。这不是配置失效,而是分布式系统固有的状态收敛延迟——得用 srvctl stop service/start service 强制重注册,再配合连接池的 connectionInitSql 延迟触发,才能绕过这个坑。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











