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分钟)
代码规范 确保所有 Connectionfinally 块或 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');