Oracle报ORA-00020连接打满?从专用进程模型到静态参数,一次生产排查全记录

各位好,我是路远。搞了十五年数据库,我最怕听到的话就是"这个很简单"。

周二下午两点,一天里最忙的时候,值班的兄弟在群里喊:接口全断了,报错刷屏。我第一个念头是库崩了,登进去一查,库好端端的,CPU、内存、响应都正常。卡住的地方在门口。新连接挤不进来,客户端报的是 ORA-12516,翻数据库的告警日志,旁边还并排着一句 ORA-00020:

复制代码
ORA-12516: TNS:listener could not find available handler with matching protocol stack
ORA-00020: maximum number of processes (5000) exceeded

这两句放一起,很多人会当成两个毛病。它俩其实是同一件事的两面。Oracle 的连接为什么这么容易顶到天花板,得从它的连接模型说起。今天不聊怎么杀连接,那是最表层的招。我带你往下拆一层,看清楚一个连接背后,Oracle 到底付了什么代价。

两道关卡:先撞 listener,再撞数据库

一个客户端要连上库,得先过两道关。第一道在监听器,listener 守着 1521 端口,收到连接请求,得给它找个"handler",也就是一个能接手干活的服务器进程。第二道在数据库内部,进程总数不能超过 processes 这个上限。任何一道满了,连接就进不来。

我从主机侧走 OS 认证登进去。这个点应用账号已经进不去了,只能这么进:

复制代码
sqlplus / as sysdba

进来先看 Oracle 自己那本配额账,v$resource_limit:

sql 复制代码
SELECT resource_name, current_utilization, max_utilization, limit_value
FROM v$resource_limit
WHERE resource_name IN ('processes','sessions');
复制代码
RESOURCE_NAME  CURRENT_UTIL  MAX_UTIL  LIMIT_VALUE
-------------  ------------  --------  -----------
processes              5000      5000         5000
sessions               5480      5495         5505

processes 和 sessions 双双顶格。这俩数不是随便定的,sessions 的上限是从 processes 推出来的。11g 及更早,口径是 processes 乘 1.1 再加 5;12c 起改成了 1.5 乘 processes 再加 22。这台库走的是老口径,5000 乘 1.1 加 5,正好 5505,跟表里对上。版本不一样,sessions 能放多大差得不小,规划的时候得照手上这版的公式来。

客户端来连,listener 想给它拉一个进程出来,发现数据库这边名额已经用光,报 ORA-00020;listener 拿不到 handler,转头把 ORA-12516 甩给客户端。一个在数据库里记,一个在门口喊,根子是一个。

顺带说一句,这 5000 个名额不是全留给业务的,库自己的后台进程先占掉四五十个,真能分给用户连接的,比 5000 还得再少一截。而这些后台进程一个没少,服务照常转,所以业务看着"库活着",实际早被挡在门外了。这类故障最唬人,里里外外都正常,就门口堵死了。

一个连接一个专用进程,Oracle的"重"在哪

要搞懂名额为什么这么容易满,得先看清 Oracle 的连接长什么样。默认的部署方式叫专用服务器,一个用户连接,对应操作系统里一个实实在在的进程。

我把这些连接进程拎出来数一数:

sql 复制代码
SELECT COUNT(*) AS user_procs,
       ROUND(SUM(pga_alloc_mem)/1024/1024) AS total_pga_mb
FROM v$process p
WHERE p.addr IN (SELECT paddr FROM v$session WHERE type = 'USER');
复制代码
USER_PROCS  TOTAL_PGA_MB
----------  ------------
      4950         13120

近五千个进程,PGA 加起来十三个 G 左右。这还只是内存。每个服务器进程,都在操作系统进程表里占一个格子,还各自揣着一份 PGA,会话私有的排序区、哈希区都搁在里头。

所以 Oracle 的连接是"重"的。它不是一个轻量对象,是操作系统层面一个完整的进程。连接数往上走,压的不是数据库一个地方,是整台机器的进程表和内存。进程表要是被吃满,别说数据库,连你 ssh 上去敲命令、crontab 起个定时任务,都可能起不来。我见过一次,DBA 想上去救火,结果连 ssh 都登不进,只能重启机器。

这也解释了另一个老问题:为什么 Oracle 上到一两千连接,大家就开始紧张,而 MySQL 挂几千连接还挺淡定。MySQL 一条连接是一个线程,线程比进程轻得多,内存落点、调度成本都不是一个量级。同样是"连接数",两边的账本根本不是一回事。

processes 是静态参数,现场根本改不动

看到名额满,很多人的第一反应是去把 processes 调大。这条路,在事故现场是堵死的。

我先确认了一下它的脾气:

sql 复制代码
SELECT name, value, issys_modifiable
FROM v$parameter
WHERE name = 'processes';
复制代码
NAME       VALUE  ISSYS_MODIFIABLE
--------   -----  ----------------
processes   5000  FALSE

issys_modifiable 是 FALSE,意思是这个参数改不了实时值。要改,得写进参数文件:

sql 复制代码
ALTER SYSTEM SET processes = 8000 SCOPE = SPFILE;

SCOPE 必须写 SPFILE,落进参数文件,然后重启实例才认数。事故现场,库正忙着一堆没提交的事务,你不可能为了腾名额去重启它。重启本身,就是另一起生产事故。

这就是 Oracle 独有的死结。MySQL 那边,max_connections 一个 SET GLOBAL 就变了,顶不住临时调大顶一阵,过后再调回来。Oracle 的 processes 你想都别想,静态参数四个字,就是把现场自救的路焊死了。名额一旦满了,你能动的,只有把已经占着名额的人请出去。

Oracle 其实留过后门:Shared Server

说到这儿,得替 Oracle 说句公道话。它不是只会"一个连接一个进程"。早年它就设计过另一套连接模型,叫 Shared Server,老名字叫多线程服务器,MTS。

这套模型里,连接不再各绑一个进程。客户端连的是一个叫 dispatcher 的分发进程,请求丢进 SGA 里一个公共的请求队列,一小撮共享服务器进程从队列里取活干,干完把结果放回响应队列。你在库里看的还是几千个会话,但背后真正干活的进程,可能就几十个。

我查了一下现在这套参数,看它到底启没启:

sql 复制代码
SELECT name, value FROM v$parameter
WHERE name IN ('shared_servers','dispatchers','max_shared_servers');
复制代码
NAME                VALUE
------------------  ---------------
shared_servers      0
dispatchers         (PROTOCOL=TCP)
max_shared_servers  20

shared_servers 是 0,等于这台库压根没启用共享服务器,走的全是专用进程那一套。要是当初把 Shared Server 配起来,几千个连接压下来,进程数也不至于这么难看。

但 Shared Server 不是银弹,它有取舍。连接不独占进程了,省的是进程和内存,代价是响应要过队列,单会话的吞吐会打折,大排序、大哈希这种吃私有大内存的操作,在共享进程里会互相挤。所以它适合的是那种连接数极高、但单个连接大部分时间闲着发慌的场景,比如一堆移动端长连接。真到 OLTP 高吞吐的核心里,大家还是老老实实配专用服务器,把连接治理挪到应用侧的连接池去做。

这条后门,知道的人不多,真正用对的更少。所以连接"重"这事儿,别全赖架构,有一半是当年部署的时候,模型就选偏了。

现场能做的,只有腾名额

回到事故现场。processes 动不了,Shared Server 没开,唯一能松名额的动作,是把占着不放的连接请出去。

我没敢全杀。杀应用连接是有代价的,杀到正在处理的事务,单子就断了。我先跟应用侧确认了业务影响,挑两类下手:确认已经是死连接的,和非核心业务的。批量把 kill 语句生成出来:

sql 复制代码
SELECT 'ALTER SYSTEM KILL SESSION ''' || sid || ',' || serial# || ''' IMMEDIATE;'
FROM v$session
WHERE username = 'APP_USER' AND status = 'INACTIVE';

生成一条执行一条,inactive 会话往下掉,名额一点点松出来,新连接紧跟着就建上了。同时让应用把连接池上限临时压小,滚动重启一部分实例,从源头把连接总量降下去。两头一起收,十来分钟业务回来了。

这里有个别人容易漏的动作。杀之前,得先搞清楚哪些连接是真死了。应用和库之间隔着防火墙,防火墙对空闲 TCP 连接有超时,时间一到就把连接掐了,可两端不会马上知道。库这边还当它活着,进程僵在那儿,名额白白占着。能在库侧探活清僵尸连接的是 SQLNET.EXPIRE_TIME,配在 sqlnet.ora 里:

复制代码
# sqlnet.ora
SQLNET.EXPIRE_TIME = 10

配上它,库每隔几分钟主动探一次,探不通的连接直接清掉,不给僵尸占名额。这一条,是 Oracle 连接治理里最容易被忘的一块。

换个库,这套账完全不一样

这单收尾的时候,顺手把这几个库的连接模型摆一块儿看了看,差别比想象中大:

维度 Oracle(专用服务器) MySQL 国产库(兼容Oracle)
连接模型 一连接一进程 一连接一线程 多为进程模型
上限参数 processes/sessions max_connections 沿用processes/sessions
能否热改 否,改后要重启 能,SET GLOBAL即生效 多数要重启,各家不一
内存落点 UGA在PGA,随进程走 线程栈,相对轻 多随进程
打满报错 ORA-00020/ORA-12516 ERROR 1040 类似Oracle
现场自救 只能腾名额(杀会话) 可临时调大上限 视参数能否热改而定

国产库这边,跟 Oracle 兼容的居多,连接的模型、processes 和 sessions 这套口径基本照搬了过来。会 Oracle 这套,上手不难,但要留意两个地方。一是各家的参数能不能热改不完全一样,有的跟 Oracle 一样得重启;二是进程模型压内存的方式各有各的算法,不能拿 Oracle 的经验直接套。上线前把手册翻明白,别想当然。

复盘:连接的容量账,得按进程算

这单给我最大的教训,说到底就俩字:算账。Oracle 的连接容量,不是应用随手填个连接池大小就能兜住的,得按操作系统能扛多少进程倒着算。

一批应用总共能开多少连接,要把实例数、每个实例的池上限、后台任务、运维登录、突发流量全乘进去,再留两成余量,这就是 processes 该给的数。定完还得回头核两样硬件:内存,每个进程一份 PGA,进程数乘上去不能把物理内存压爆;还有操作系统的进程表上限。

这笔账最怕的,反而是失控的池子。应用一扩容,实例数蹭蹭涨,池子各自为政,谁都不知道加起来到底开了多少连接。200 个实例,每个池子配 30,就是 6000,早把 5000 的 processes 顶穿了,可这个乘法,配置阶段根本没人做过。

避坑清单

下面三条,是我把这套系统盘过之后给自己立的规矩,各位对着自己的环境看看。

应用各实例的连接池上限加起来,必须小于库的 processes,还得留出两成给后台任务、运维登录和突发流量。上线前把这个乘法过一遍,别等高峰被挤爆才发现。

processes 是静态参数,要调就安排维护窗口,改完重启才算数。应急的时候它帮不上忙,应急靠清连接、压应用顶过去。

最后一条,是我这次才彻底想明白的:动 processes 之前,先算内存,别只顾着进程数。早年我给一台库加过 processes,从 1500 加到 3000,进程数是够用了,可内存没跟着加。进程一铺开,PGA 把物理内存吃光,机器开始换页,库反而更慢,业务比加之前还卡。名额的账、内存的账,得一起算,只加一边,等于换个地方翻车。

写在最后

回头看这单,现场操作其实就两下:分清是哪道关卡满了,再一手清连接、一手压应用。真正花时间的,是往下拆清楚 Oracle 的连接为什么这么容易满、为什么满了现场还救不动。

Oracle 这套连接模型,是它那个年代的设计,重、稳、吃资源。放到今天动辄几百上千实例的云场景,processes 改不动、连接一贵就不敢多开,这套静态脾气就成了负担。所以国产库在兼容 Oracle 的同时,怎么让参数更灵活、连接模型更省,是绕不开的一课。

你们那边的 processes,是算出来的还是拍脑袋填的?踩过为扩连接去重启库、结果停机的坑吗?评论区聊聊,互相避坑。

我是路远。数据库这事儿,生产环境说了算。

相关推荐
瀚高PG实验室1 小时前
瀚高数据库序列介绍、使用及值不连续、重复写入等现象处理
数据库·postgresql·瀚高数据库
野生数据人2 小时前
日志平台运维速查表:巡检命令、阈值、参数,一张贴墙
大数据·数据库
数据库小学妹2 小时前
向量检索最小可运行实现:HNSW索引参数与召回率/延迟权衡
数据库·后端
发量惊人的中年网工2 小时前
SD-WAN 组网方案怎么选?跨地域与多分支企业的选型指南(含成本对比)
服务器·网络·数据库
xiaogai_gai2 小时前
物料主数据从ERP同步到电商中台:基于轻易云的编码映射与增量全量双轨实践
大数据·数据库
SelectDB2 小时前
信创适配速查手册:国产 CPU / OS 对照表、认证许可速查与部署检查项清单
大数据·数据库·数据分析
亚川楼宇自控系统数据中心厂家2 小时前
机房 3D 可视化技术:从平面监控到立体化管理的升级
大数据·数据库·人工智能
SelectDB2 小时前
部署形态与存算分离速查:形态对照、配置项清单与关键命令片段
大数据·数据结构·数据库
SelectDB2 小时前
Workload Group 参数速查:排队与并发配置、CPU 软硬限切换与常见配置问题清单
大数据·数据库·数据分析