sqlnet.expire_time是Oracle服务器端心跳探测间隔(秒),用于清理死连接;设太小会误杀长事务,设太大则无效,需与应用层超时协同配置且仅对Oracle Net有效。
sqlnet.expire_time 是什么,为什么不能乱设
sqlnet.expire_time 是 oracle 客户端与数据库之间心跳检测的配置项,单位是秒。它让服务器端定期发一个空包(probe)给客户端,如果收不到响应,就主动断开连接——用来清理死连接,比如客户端崩溃、网络中断但 tcp 连接没正常关闭的情况。
但它不是“超时自动登出”开关,也不影响用户会话的 idle_time 或应用层的登录态。设得太小(比如 10 秒),会误杀长事务、大数据导出、报表生成这类本该持续的连接;设得太大(比如 0 或不配),又起不到清理作用。
常见错误现象:ORA-03113: end-of-file on communication channel 频繁出现,但查不到明确异常日志;或连接池里一堆 INACTIVE 状态却始终不释放。
- 只对使用 Oracle Net(即
sqlnet.ora生效的连接)有效,JDBC Thin 驱动默认不走这个机制 - 必须在数据库服务器端的
$ORACLE_HOME/network/admin/sqlnet.ora中配置,客户端配了无效 - 值为 0 表示禁用,不是“立即检测”,别被文档误导
怎么配才真正生效:路径、权限和 reload
改完 sqlnet.ora 不等于立刻生效。Oracle 监听器(lsnrctl)不读这个文件,只有数据库实例在建立新连接时才会加载它。也就是说:已存在的连接不会受新配置影响,必须等它们自然断开或重启实例。
实操建议:
- 确认修改的是数据库服务器上的
$ORACLE_HOME/network/admin/sqlnet.ora,不是客户端的,也不是 RAC 中某节点漏配 - 检查文件权限:必须是
oracle用户可读,且不能有 BOM 或 UTF-8 with signature - 不需要重启监听器或数据库,但新连接从下一次
CONNECT开始才按新值检测 - 验证是否加载成功:在会话中执行
SELECT sys_context('USERENV', 'NETWORK_PROTOCOL') FROM DUAL;,确保是tcp;再查v$session的last_call_et和状态变化节奏
和应用层 timeout 冲突怎么办
很多 Java 应用用了 HikariCP 或 Druid,自己配了 connection-timeout、idle-timeout、max-lifetime。这时候 sqlnet.expire_time 和应用连接池的回收逻辑可能打架。
典型表现:连接池认为连接还健康(没超 max-lifetime),但数据库端因 probe 失败已强制断开,下次取连接就抛 IO Error: Connection reset。
- 推荐策略:应用层
max-lifetime设为sqlnet.expire_time × 2左右,留出探测+断开+清理的时间差 - JDBC Thin 驱动不支持
sqlnet.expire_time,要用oracle.net.CONNECT_TIMEOUT和oracle.net.READ_TIMEOUT替代 - 如果用的是 Oracle Wallet 或 SSL 连接,
sqlnet.expire_time仍有效,但 probe 包会走加密通道,延迟略高,建议值 ≥ 60
替代方案:什么时候该放弃 sqlnet.expire_time
在容器化、Service Mesh 或云数据库(如 ADB、RDS)环境下,sqlnet.expire_time 往往失效或不可控。比如 Kubernetes 的 kube-proxy、NLB 的空闲连接超时、云厂商中间件的连接复用,都会在 Oracle 检测之前先掐断连接。
此时更靠谱的做法是:
- 在应用代码里加连接有效性校验,比如执行
SELECT 1 FROM DUAL前先isValid(2) - 用数据库视图
v$session定期查status = 'INACTIVE'且last_call_et > 1800的会话,配合ALTER SYSTEM KILL SESSION脚本清理 - 对长期运行的批处理任务,显式用
SET TIMED_STATISTICS = TRUE+DBMS_MONITOR.SESSION_TRACE_ENABLE跟踪连接生命周期,而不是依赖被动探测
真正难的从来不是配个数字,而是搞清流量路径上到底有几层连接保活机制,以及谁先动手断开。漏掉任意一层,sqlnet.expire_time 就只是个安静的摆设。

















