根本原因是TNS解析结构错误、SCAN监听器未获取实例实时负载、连接池预热导致初始连接扎堆三者叠加;LOAD_BALANCE=on必须置于DESCRIPTION内紧邻ADDRESS,JDBC的loadBalance=true仅控制新建连接随机选址,不保证均衡分布。

Java连接Oracle RAC后负载不均,根本原因不是JDBC URL里加了loadBalance=true就能解决的——它只影响新建连接时的地址随机选择,而真正决定连接分布的是TNS解析结构 + 服务端注册状态 + 连接池行为三者叠加的结果。
tnsnames.ora或URL中LOAD_BALANCE=on位置错了
Oracle客户端负载均衡由TNS解析器驱动,LOAD_BALANCE=on必须放在DESCRIPTION块内、且紧邻ADDRESS之后,不能塞进CONNECT_DATA,也不能挂在DESCRIPTION_LIST外层。
- 错误写法:
(DESCRIPTION=(ADDRESS=...)(CONNECT_DATA=(...))(LOAD_BALANCE=on))→ 不生效 - 正确写法(多实例直连):
(DESCRIPTION=(ADDRESS=(HOST=rac1-vip)...)(LOAD_BALANCE=on)(CONNECT_DATA=(SERVICE_NAME=mydb))) - 若用
DESCRIPTION_LIST包裹多个DESCRIPTION,每个都要单独配LOAD_BALANCE=on,漏一个就只剩第一个地址参与轮选 - JDBC URL里写
jdbc:oracle:thin:@(DESCRIPTION=...)时,整个括号内容就是TNS字符串,同样要满足上述嵌套结构
SCAN监听器没拿到各节点实时负载数据
即使客户端随机选了地址,最终连接落到哪个实例,取决于SCAN Listener是否能返回“负载最低”的节点——这依赖PMON每3秒向REMOTE_LISTENER上报V$SERVICEMETRIC.CURRENT_LOAD。
- 检查
lsnrctl status LISTENER_SCAN1输出中是否有service_handler="DEDICATED", load=XX字段;没有说明实例未成功注册 -
REMOTE_LISTENER必须指向SCAN地址(如scan.example.com:1521),不能是VIP或localhost - 改完
REMOTE_LISTENER后必须立刻执行ALTER SYSTEM REGISTER,否则PMON可能延迟数分钟才推送新负载 - 若用GNS,
REMOTE_LISTENER需指向GNS VIP,否则静默失败
连接池预热导致初始连接全扎堆
HikariCP、UCP等连接池默认开启minimumIdle或执行connectionInitSql,会在启动时批量建连。此时若DNS缓存未刷新或TNS解析结果固定(比如SCAN返回IP顺序不变),所有连接会落在同一实例。
立即学习“Java免费学习笔记(深入)”;
- 现象:应用刚启动时
v$session里inst_id全一样,跑一段时间后才慢慢分散 - 临时验证:启动前清空DNS缓存(Linux:
sudo systemd-resolve --flush-caches;Windows:ipconfig /flushdns) - 长期规避:调低
minimumIdle为0,让连接随需创建;或在应用启动后延时几秒再初始化连接池 -
loadBalance=true只作用于连接建立阶段,连接复用后所有SQL都走原链路,和负载均衡无关
Service配置没对齐业务粒度
纯技术型负载均衡(随机选节点)容易引发Cache Fusion风暴——同一业务请求被分到不同实例,反复跨节点同步buffer cache。
- 真实瓶颈常不在连接分布,而在业务逻辑与Service绑定不合理
- 应按业务模块拆分Service(如
report_svc、trade_svc),并通过srvctl modify service设置-preferred和-available实例 - Java应用连接时指定对应Service名(
SERVICE_NAME=report_svc),而非通用服务名 - 查
dba_services确认clb_goal设为LONG或SHORT,影响goodness值计算逻辑
最容易被忽略的点是:LOAD_BALANCE=on和loadBalance=true只是“允许随机”,不是“保证均衡”;只要SCAN注册失败、DNS缓存未更新、或连接池预热过猛,三者任一出问题,就会立刻回归单点连接。验证必须在应用刚启动后立即查v$session,而不是等几分钟后再看。


















