记一次特殊的“ORA-04030”故障处理

时间:2022-01-02 12:40:09

【案例背景】

2015年7月19日客户现场人员在通过 PLSQLDeveloper 工具在创建索引时报 ORA-04030 错误,导致索引创建失败;但是通过 splplus 重新执行创建索引语句成功。


故障发生后,云和恩墨工程师协助进行故障排查工作,在 sqlplus 中重新执行创建索引语句成功。


经进一步分析,故障原因是由于数据库监听是通过 crs 进行启动,所以继承了 root 用户的 ulimit 限制,在 root 的 ulimit 限制中 data(kbytes) 的限制为 131072kb,即每个通过监听连接的进程能分配的内存资源不能超过 131072kb,所以在通过 PLSQL Developer 工具连接数据库(需要通过 Oracle 的监听建立连接)创建索引时,该操作申请的内存资源达到该限定时就会报 ORA-04030 的错误。

 

为什么会出现这种告警呢?


首先,了解下 ORA-04030 错误的原因。

 

ORA-04030 错误引起的原因大概有以下几种情况:


1)是否有足够的可用内存?

查看系统内存使用情况


记一次特殊的“ORA-04030”故障处理
 

查看当时出故障的服务器资源,可用看到剩余内存还有7.7G左右,说明在操作系统层面还有足够的可用内存。

 

2)是否设置了Oracle 的限制?

查看 Oracle 中与 PGA 相关的设置:


记一次特殊的“ORA-04030”故障处理

记一次特殊的“ORA-04030”故障处理


 从上面的内容可以看到 PGA 设置的大小为 8400M,根据单个会话使用 PGA 的期望尺寸(也可以认为是实际分配的最大尺寸)计算公式是:min(5%*pga_aggregate_target,50%*_pga_max_size,_smm_max_size),可以简单计算下 min(5%*8400M,50%1680M,840M)=420M(其中_pga_max_size 的单位为 bytes,_smm_max_size 的单位为 kb),即单个会话能使用 PGA 的期望尺寸为 420M,那报错的会话是否超过了该限制喃?


查看 ORA-04030 报错的 trace 文件:


记一次特殊的“ORA-04030”故障处理

记一次特殊的“ORA-04030”故障处理
记一次特殊的“ORA-04030”故障处理

 

从以上 trace 文件中可以看到,报错的进程实际分配的进程只有111MB,远远未达到420M,说明并非是由于 Oracle 自身的限制引起的 ORA-04030 报错。


3)哪个进程需要的内存过多?

上一个是否是由于 Oracle 自身限制引起的解释中,已经可以从 trace 文件中看到,消耗最多内存的进程就是报 ORA-04030 的进程,消耗的内存为110M,并未发现其他更消耗内存的进程。

 

4)是否设置了操作系统限制?

查看操作系统限制,Oracle 用户的限制


记一次特殊的“ORA-04030”故障处理


http://www.cddba.com/root 用户的限制:


记一次特殊的“ORA-04030”故障处理


从上面 root 和 Oracle 的 limit 限制来看,root 用户的 data(kbytes) 的限定值得关注,该属性的意义是 soft data segmentsize in blocks(进程数据段大小限制)。

 

现在做一个反向测试:利用 SQL*PLUS 工具创建索引,创建成功。


问题来了,为何 sqlplus 会成功,PLSQL Developer 却会失败?


通过 PLSQLDeveloper 工具创建索引时报 ORA-04030 错误,但是通过 SQLPLUS 创建却能成功,两者除了使用的工具不同(PLSQL Developer 和 sqlplus),还有就是连接的方式不同(PLSQL Developer 是通过监听程序建立的进程连接;sqlplus 是在数据库服务器上直接创建创建的连接,绕过了监听程序建立的进程)。


从连接工具和方式的不同得到了不一样的结果,如何来验证到底是连接工具的问题或则是连接方式的引起的报错?


由于 PLSQLDeveloper 只能通过监听的连接方式进行连接,但是 sqlplus 可以通过监听或则直接连接两种方式进行,所以先对连接方式进行测试。


通过 sqlplus 以 tnsnames.ora 标签名的方式通过监听进行连接,并执行创建索引报错的语句,发现错误依然存在,但是如果不通过监听而直接连接是不会报错的,说明跟是否通过监听进行连接有很大的关系。


为何会受监听的影响?

在 Oracle RAC 环境中,由于 crs 的启停是通过 root 用户进行的。


记一次特殊的“ORA-04030”故障处理


所以在 crs 会继承 root 用户的 limit 属性,当通过 crs 或则 srvctl 命令启动监听时,也会继承 root 用户相应的 limit 属性,即 data(kbytes)为131072。如何验证该推断?


现在通过监听的形式进行连接


记一次特殊的“ORA-04030”故障处理


通过 dbx 工具查看该进程的 limit 信息


记一次特殊的“ORA-04030”故障处理


从上面的内容可以看到 data 属性的 limit 值为134217728bytes 即131072kbytes 与 root 的 data(kbytes) 131072 值完全吻合(stack 的33554432bytes 即32768也与 root 的 stack(kbytes) 32768一致),说明是通过监听建立连接进程的 limit 继承于 root 用户。

 

使用不同监听进行连接


记一次特殊的“ORA-04030”故障处理

 

跟踪这个服务器进程


记一次特殊的“ORA-04030”故障处理


从上面内容可以看到,如果不通过监听连接数据库创建的进程,它的 data 限制为 unlimited 的即无限制。


最后查看 crs 中监听的启动日志:


(/u01/oracle/product/10.2.0/db_1/log/gisdata2/racg 中的日志文件 ora.gisdata2.LISTENER_GISDATA2.lsnr.log):


记一次特殊的“ORA-04030”故障处理


从以上内容可以看到监听是由 crs(或则是 srvctl 命令)启动的以及监听的运行时间:


记一次特殊的“ORA-04030”故障处理


监听启动的时间也与日志中的时间对应。

 

那为什么 CRS 的使用会使用 ROOT 用户的 limit 限制呢?


首先,得明白在 Linux 中,每触发任何一个事件时,系统都会为它定义一个进程,并且给予一个 ID,即 PID,同时会根据触发这个进程的用户与相关属性关系,给这个 PID 设置有效的权限。


记一次特殊的“ORA-04030”故障处理

从上面可以知道,系统启动以后,CRS 会自动启动,启动主要由 /etc/init.d 的几个脚本完成。而这些脚本的执行用户和用户组是 root,也就是说当 CRS 启动时,Linux 系统会根据 UID/GID 来判断资源属性和环境变量,那么这个进程所衍生的进程,也会沿用这些属性。


由此可以得到结论,由于监听是通过 crs 进行的启动,继承了 root 用户的 limit 限制,每个会话所能持有的内存大小最大不能超过128M,当通过监听进行数据库连接时,由监听创建的用户进程也将继承该 limit 限制,所以当通过 PLSQL Developer 连接数据库(包括 sqlplus等工具需要通过监听建立用户进程的情况),在创建索引过程中,当所请求的内存达到或非常接近该限制时,就会由于无法进一步申请更多的内存资源,抛出 ORA-04030 错误。

 

提示:

如果是在 Linux 系统中,可以通过 cat /proc/PID/limits 的方法进行查看单个进程的 limit 属性值,其中 PID 为要查看进程的进程号。

 

这次的案例分析过程完毕,我们可以得出一些建议:

以 ORA-04030 此案例延伸扩展,为避免该错误可参考以下建议:

1)在安装 Oracle 软件创建数据库之前应该对主机层面的内核参数、limit 限制等进行规范的修改,以避免类似问题的发生。

2)配置合理的内存,例如物理内存和交换空间

3)使用自动 PGA 内存管理可降低 ORA-04030 错误的概率