connection request timed out 是连接池耗尽而非网络故障,因 oracleconnection 等未显式释放导致连接长期占用;odp.net 严格按连接字符串逐字符匹配分池,空格、大小写等差异会分裂池;connection lifetime=60 废弃复用,应设为0。

Connection request timed out 不是网络问题,而是连接池里没连接可用了。 它发生在 OracleConnection.Open() 调用处,线程卡住等待,直到超时抛异常。数据库本身能连、tnsping 通、sqlplus 登录正常——说明监听器和网络都没毛病。
为什么连接池会“空”了却报超时?
ODP.NET 的连接池不会自动回收未释放的连接。只要 OracleConnection、OracleCommand 或 OracleDataReader 没被显式关闭或释放,它就一直占着池里的一个 slot,不归还。
-
OracleDataReader是独占连接的,用完不关(reader.Close()或using),连接就锁死 -
using (var conn = new OracleConnection(cs)) { if (x) return; }—— 提前return,conn根本没走到Dispose() - 在
catch块里吞掉异常后直接return,忘了conn.Close()或conn.Dispose() - 把
OracleConnection存在类字段或静态变量里,长期持有不释放
结果就是 v$session 里大量 STATUS = 'INACTIVE',LAST_CALL_ET > 1800;Windows 性能计数器中 NumberOfFreeConnections 长期为 0,NumberOfActiveConnections 卡在 Max Pool Size 附近。
连接字符串细微差异会分裂连接池
ODP.NET 判断是否复用池,靠的是连接字符串**逐字符比对**:大小写、空格、分号前后有没有空格、换行符,全算。
-
Max Pool Size=100✅;Max Pool Size = 100❌(空格导致新池,且该参数被忽略) - 服务名写成
ORCL和orcl,会被视为两个不同池 - 拼接字符串(如
$"Data Source={host}")极易引入不可见差异
后果是:本该共用 100 连接的场景,变成两个池各占 50,但每个池上限仍是 100 —— 监控看到总活跃连接 200,而每个池的空闲连接都是 0。
Connection Lifetime=60 是性能陷阱,不是保命符
设 Connection Lifetime=60 表面看是“防老化”,实际让每个归还池的连接,只要存活满 60 秒就强制关闭。这等于废掉了连接复用价值,频繁重建 TCP 连接,加重数据库负担,还可能触发防火墙空闲断连。
- 正确做法是设
Connection Lifetime=0(默认值),让连接长期复用 - 若需应对集群节点上下线,应由运维控制负载均衡策略,而非靠这个参数“轮换”连接
- 防火墙空闲超时(如 30 分钟)的问题,应配
Validate Connection=true+Connection Timeout=15,并在获取连接时做有效性检查
真正难缠的点不在配置,而在代码路径:所有 OracleDataReader 必须显式关,所有 return 和 throw 前必须确保连接释放,哪怕只有一条分支漏掉,几小时后就会爆池。这不是“大概率出错”,是“必然堆积”。











