Oracle服务端主动关闭空闲连接导致HikariCP连接失效,需配置keepalive-time、max-lifetime、idle-timeout等参数匹配数据库超时,并启用泄漏检测与连接验证。
Oracle 默认会通过 SQLNET.EXPIRE_TIME 或数据库参数 idle_time / resource_limit 主动断开长时间空闲的连接。一旦连接被 Oracle 单方面关闭,HikariCP 若未及时感知,下次取出该连接执行 SQL 就会抛出 SQLRecoverableException: IO Error: Connection reset 或 ORA-17800: 读取调用中连接中断。
这不是网络问题,而是服务端“静默下线”了连接。常见于以下情况:
sqlnet.ora 中配置了 SQLNET.EXPIRE_TIME = 10(单位分钟),每 10 分钟发一个探测包,若客户端无响应则 kill sessionresource_limit=TRUE 并设置了 IDLE_TIME,超时后自动断连HikariCP v5.x 默认不启用连接借用前验证(connection-test-before-use=false),也不发心跳——它只依赖 keepalive-time 周期性探测空闲连接,且该机制有前提条件:
keepalive-time 必须 > 0,且建议设为 240000(4 分钟),必须小于 Oracle 的 SQLNET.EXPIRE_TIME 或实际空闲断连阈值minimum-idle 必须 > 0(如设为 5),否则没有空闲连接可保活ojdbc8.jar)需支持 isValid(),但实际它常返回 false 或抛异常;所以更可靠的是用 connection-init-sql=SELECT 1 FROM DUAL 替代validation-timeout 必须 ≤ connection-timeout,否则验证失败不会触发重连逻辑HikariCP 的 max-lifetime 是连接在池中最大存活时间,但它不感知数据库侧的真实生命周期。若设为 7200000(2 小时),而 Oracle 的 wait_timeout(实为 idletime 或 SQLNET.EXPIRE_TIME)是 10 分钟,那大量连接会在池里“带病续命”,直到业务使用时才暴露断连。
正确做法是让 max-lifetime 明显小于服务端断连阈值:
SELECT resource_name, limit FROM dba_profiles WHERE profile='DEFAULT' AND resource_name IN ('IDLE_TIME', 'CONNECT_TIME');
SQLNET.EXPIRE_TIME=10(分钟),则 max-lifetime 建议 ≤ 540000(9 分钟),留出缓冲idle-timeout=300000(5 分钟),确保空闲连接早于服务端被池主动回收看似“自动断开”,实则是连接池已无可用连接,只能把本该归还却没归还的“脏连接”重复分配出去。典型信号是日志里反复出现 Connection pool exhausted,伴随大量 ORA-17800 或 Connection reset。
要确认是否泄漏,必须打开 HikariCP 的泄漏检测:
leak-detection-threshold=30000(30 秒),不是默认的 0
com.zaxxer.hikari 设为 DEBUG 级别,否则只打 WARN 日志且无堆栈@Select 方法没加 @Transactional、手动 SqlSession 未 close、JDBC ResultSet/Statement 未在 finally 块中释放Oracle 场景下,leak-detection-threshold 容易误报——PL/SQL 批处理或隐式游标可能让连接卡在“半关闭”状态,所以阈值不宜低于 30 秒,也不宜高于 60 秒。