sockettimeout设为0是最大陷阱,oracle jdbc thin驱动默认值导致线程在resultset.next()等处永久挂起;必须显式设置非零值(建议30000ms起步),并配合setfetchsize(integer.min_value)实现流式读取以防假超时。
sockettimeout设为0是最大陷阱
oracle jdbc thin驱动默认sockettimeout=0,意味着tcp读操作永不超时。一旦数据库进程僵死、网络中间设备静默丢包或rac节点假死,jdbc线程就会卡在resultset.next()或inputstream.read()上,永远不返回也不抛异常——不是慢,是彻底挂起。
必须显式设置非零值,单位毫秒,建议从30000起步。线上环境若存在长查询或LOB读取,可按业务容忍上调至120000(2分钟),但绝不应超过300000(5分钟),否则掩盖SQL性能问题。
-
socketTimeout必须大于queryTimeout(单位秒)+ 预估取消指令往返耗时(建议预留至少5秒) - 老版本ojdbc6不识别
socketTimeout,得用oracle.jdbc.ReadTimeout - URL参数优先级最高:
jdbc:oracle:thin:@//host:port/service?socketTimeout=60000 - Properties传参次之;
Connection.setNetworkTimeout()在ojdbc8中对流式读取常失效
大结果集必须配setFetchSize(Integer.MIN_VALUE)
默认fetch size(如10000)会让JDBC一次性把整批结果加载进内存。若某行含CLOB/BLOB或字段极宽,单次网络读就可能耗时几十秒,直接触发socketTimeout——这不是网络故障,是加载策略导致的假超时。
流式读取才能让驱动用Oracle原生协议分块拉数据,避免单次读压垮超时阈值。
- 必须在
executeQuery()前调用statement.setFetchSize(Integer.MIN_VALUE) - PreparedStatement也一样,执行后再设无效
- 搭配
socketTimeout才有意义;光设fetch size不设超时,只是把挂起延迟到下一批 - 注意:流式读取下
ResultSet.last()、getRow()等方法不可用
connectTimeout对Thin驱动基本无效
connectTimeout=30000写在URL里对Oracle Thin驱动没用——Oracle官方明确不支持该标准JDBC参数。它只管TCP三次握手,而Oracle连接阻塞多发生在认证阶段(监听器响应后、用户登录前),此时connectTimeout早已退出。
真正管用的是Oracle私有参数oracle.net.CONNECT_TIMEOUT,单位毫秒,覆盖DNS解析+TCP建连全过程。
- 必须用TNS描述符格式URL:
jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS=...))(oracle.net.CONNECT_TIMEOUT=10000) - 不能和
connectTimeout混用,否则行为不可预测 - 值太小(如
1000)会导致SCAN多VIP轮询失败;太大(如30000)拖慢故障感知 - 重试需配合
oracle.net.RETRY_COUNT和oracle.net.RETRY_DELAY,JDBC默认只试一次
queryTimeout和cancelQueryTimeout必须协同设置
queryTimeout(单位秒)只控制SQL执行时间,超时后驱动发CANCEL指令,但不关连接。如果socketTimeout太短,CANCEL指令还没发完就触发网络断连,连接状态会进入不可控的半开状态。
Oracle驱动内部用cancelQueryTimeout控制CANCEL指令自身耗时,默认值极短(约1秒),容易被忽略。
-
socketTimeout必须 >queryTimeout * 1000+cancelQueryTimeout(毫秒) - 可通过
OracleDataSource.setConnectionProperties()传oracle.jdbc.cancelQueryTimeout - Spring事务的
@Transactional(timeout = ...)只作用于事务边界,不影响底层JDBC socket行为 - 连接池(如HikariCP)的
connection-timeout管的是取连接等待,和JDBC传输超时无关
高并发下最易被忽略的点:socketTimeout和setFetchSize(Integer.MIN_VALUE)必须同时生效,缺一不可。前者防线程卡死,后者防单次读超限——它们解决的是同一问题的不同层面,单独配置任何一项都留有致命缺口。











