Oracle数据库连接数耗尽事故(ORA-00020)
Oracle数据库连接数耗尽事故(ORA-00020)
一、故障现象
- 13:23 接到业务方紧急反馈:无法建立新连接,部分服务报错。
- 初步排查发现数据库出现 ORA-00020 错误:
ORA-00020: maximum number of processes (10000) exceeded - 登录数据库验证:
PROCESSES参数上限:10,000- 当前进程使用量:节点1达 9,800+,节点2达 8,500+
- 活动会话(
STATUS = 'ACTIVE')仅 8个,CPU/IO 负载正常 - 大量会话处于
INACTIVE状态
✅ 判断:非性能瓶颈,而是连接资源被无效会话占满,导致新连接无法建立。
二、根因定位
2.1 会话来源分析
执行以下SQL定位异常连接:
SELECT username, machine, program, COUNT(*) AS session_count
FROM gv$session
WHERE type = 'USER'
GROUP BY username, machine, program
ORDER BY session_count DESC;
关键发现:
| 用户名 | 程序 | 主机模式(MACHINE) | 会话数 |
|---|---|---|---|
| TBCS | JDBC Thin Client | xxx-xxxxx-d4d889979-xxxxx | ~5,800 |
| TBCS | JDBC Thin Client | xxxxx-xxx-xxxxx | ~3,200 |
- 共涉及 60+ 个不同 Pod 实例(Kubernetes 部署)
- 每个 Pod 建立 90~190 个数据库连接
- 99% 的会话状态为
INACTIVE last_call_et(空闲时长)普遍超过 30分钟~数小时
2.2 根本原因结论
微服务应用存在严重连接泄漏:
- 应用使用 JDBC 连接池,但未正确关闭数据库连接
- 或连接池配置不当(如
maxPoolSize过大 + 无空闲回收机制)- 导致每个 Pod 持续累积 INACTIVE 会话,最终耗尽数据库
PROCESSES上限
三、应急处置过程
3.1 紧急清理僵尸会话
为快速恢复业务,立即批量终止问题会话:
-- 示例:生成 kill 语句(实际可能用 PL/SQL 循环执行)
SELECT 'ALTER SYSTEM KILL SESSION ''' || sid || ',' || serial# || ''' IMMEDIATE;'
FROM gv$session
WHERE username = 'TBCS'
AND machine LIKE 'hiboss-tiboss%'
AND status = 'INACTIVE';
3.2 业务恢复验证
- 14:05:连接数回落至 < 1,000
- 新连接可正常建立,业务系统恢复正常
- 监控确认无新增异常连接
3.3 同步通知
- 立即联系应用团队(DevOps & 开发):
- 提供受影响 Pod 列表
- 要求暂停相关服务滚动发布
- 要求彻查连接池配置与代码逻辑
四、长期改进措施 (如果有必要)
4.1 应用侧整改建议
| 项目 | 建议方案 |
|---|---|
| 连接池配置 | 检查 maxPoolSize 是否合理(建议单 Pod ≤ 20) |
| 连接泄漏防护 | 启用 HikariCP leakDetectionThreshold=60000(1分钟) |
| 空闲连接回收 | 设置 idleTimeout=300000(5分钟) |
| 代码规范 | 确保所有 Connection 在 finally 块或 try-with-resources 中关闭 |
4.2 数据库侧加固
-
为
TBCS用户创建资源限制 Profile:CREATE PROFILE app_tbc_limit LIMIT SESSIONS_PER_USER 100; ALTER USER TBCS PROFILE app_tbc_limit; -
在
sqlnet.ora启用死连接检测(DCD):SQLNET.EXPIRE_TIME=10 -
配置监控告警:
- 当
processes使用率 > 80% 时触发企业微信/邮件告警
- 当
五、经验总结
教训
仅依赖 PROCESSES 上限无法防止连接滥用
未对微服务连接行为做容量评估
缺少长时间 INACTIVE 会话自动清理机制
改进方向
上线前需进行连接数压测与模型验证
部署 DCD + 定期巡检脚本
核心原则:
“连接数暴增”本质是应用问题,DBA 的角色是快速止损 + 推动根治,而非无限扩容。
附录:关键查询语句
1. 实时连接数监控
SELECT inst_id, COUNT(*)
FROM gv$process
GROUP BY inst_id;
2. 查找长时间空闲会话
SELECT sid, serial#, machine, program, last_call_et/60 AS idle_min
FROM gv$session
WHERE status = 'INACTIVE'
AND last_call_et > 1800 -- 空闲超过30分钟
AND username = 'TBCS';
3. 查看资源使用上限
SELECT resource_name, current_utilization, max_utilization, limit_value
FROM v$resource_limit
WHERE resource_name IN ('processes', 'sessions');
- 感谢你赐予我前进的力量
赞赏者名单
因为你们的支持让我意识到写文章的价值🙏
本文是原创文章,采用 CC BY-NC-ND 4.0 协议,完整转载请注明来自 龙羽

