oracle jdbc连接scan需用tns描述符并配置重试参数,因scan仅dns轮询指向rac节点vip,驱动默认不自动重解析或故障转移;必须使用(description=...)格式url,配合retry_count、retry_delay等参数,并确保dns返回多个a记录及驱动版本≥12.1。
oracle jdbc url里写scan ip为什么连不上
scan(single client access name)ip本身不直接响应连接,它只是dns轮询指向集群中某台rac节点的vip。jdbc驱动不会自动做dns重解析或故障转移,jdbc:oracle:thin:@scan-host:1521/orcl这种写法看似正确,实际只尝试连接dns第一次返回的那个ip,一旦该节点宕机或监听未就绪,就会报io error: the network adapter could not establish the connection。
必须配合Oracle官方推荐的连接描述符(TNS connect descriptor),让驱动理解这是RAC环境并启用内置重试逻辑:
- 用
(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=scan-host)(PORT=1521))(CONNECT_DATA=(SERVICE_NAME=orcl)))替代简单URL格式 - 确保
scan-host能被应用服务器DNS解析,且返回至少3个A记录(对应3个SCAN VIP) - 不要在
HOST里写死单个IP,否则失去SCAN意义 - JDBC驱动版本需≥12.1(11g R2末期版本已支持,但12c+更稳定)
如何开启JDBC驱动的自动重试和故障转移
Oracle JDBC驱动默认不启用透明应用故障转移(TAF)和连接重试,必须显式配置参数,否则SCAN只解决“初始连接分发”,不解决“运行时节点失效”。
关键参数要加在URL末尾或Properties对象里:
-
connectTimeout=10:建立TCP连接超时(秒),避免卡在SYN等待 -
socketTimeout=30:SQL执行超时(秒),防止长事务阻塞连接池 -
oracle.net.CONNECT_TIMEOUT=10000:驱动层DNS+TCP综合超时(毫秒) -
oracle.net.RETRY_COUNT=2:失败后最多重试2次(不是重连次数,是重选SCAN IP+重试连接) -
oracle.net.RETRY_DELAY=1000:每次重试前等待1秒,避免密集探测
完整URL示例:jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=my-scan)(PORT=1521))(CONNECT_DATA=(SERVICE_NAME=orcl)))(CONNECT_TIMEOUT=10)(RETRY_COUNT=2)(RETRY_DELAY=1000)
连接池(如HikariCP)里配SCAN要注意什么
连接池会缓存物理连接,如果初始连接成功但后续某节点下线,池中已有连接可能持续失败,而新连接又可能再次打到坏节点——这不是URL问题,是连接池健康检测没跟上RAC状态变化。
必须调整以下几项:
-
connection-test-query=SELECT 1 FROM DUAL(HikariCP旧版)或改用connection-test-query=/* ping */ SELECT 1 FROM DUAL(新版兼容性更好) -
validation-timeout=3000:验证查询超时不能太长,否则拖慢获取连接 -
idle-timeout=600000(10分钟):避免空闲连接在节点故障后长期滞留 -
max-lifetime=1800000(30分钟):强制刷新连接,让驱动重新走SCAN DNS解析
注意:test-on-borrow在高并发下性能差,优先用test-on-return或定时validation策略。
为什么设置了RETRY_COUNT还是秒报错
常见原因是驱动根本没走到重试逻辑——比如DNS解析失败、防火墙拦截SYN包、或监听器没启动。此时RETRY_COUNT不生效,因为错误发生在“连接前准备阶段”,而非“连接建立后失败”。
排查顺序必须是:
- 用
nslookup my-scan确认返回多个IP,且telnet ip 1521每个都能通 - 检查
sqlnet.ora里没有NAMES.DIRECTORY_PATH= (TNSNAMES, EZCONNECT)这类限制(JDBC不读这个文件,但本地tnsping测试时会干扰判断) - 开启JDBC日志:
-Doracle.jdbc.Trace=true -Djava.util.logging.config.file=logging.properties,看日志里是否出现Retrying connection attempt #1 - 确认没在代码里手动调用
DriverManager.getConnection(url)两次——重试是驱动内部行为,重复调用等于绕过机制
SCAN + JDBC高可用不是配置完就一劳永逸的事,它依赖DNS稳定性、网络路径可达性、以及驱动对RAC语义的理解深度。最容易被忽略的是:你看到的“连接成功”,可能只是连上了某个节点的监听器,不代表那个节点上的实例真正open了。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










