【案例背景】
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)是否有足够的可用内存?
查看系统内存使用情况
查看当时出故障的服务器资源,可用看到剩余内存还有7.7G左右,说明在操作系统层面还有足够的可用内存。
2)是否设置了Oracle 的限制?
查看 Oracle 中与 PGA 相关的设置:
从上面的内容可以看到 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 文件:
从以上 trace 文件中可以看到,报错的进程实际分配的进程只有111MB,远远未达到420M,说明并非是由于 Oracle 自身的限制引起的 ORA-04030 报错。
3)哪个进程需要的内存过多?
上一个是否是由于 Oracle 自身限制引起的解释中,已经可以从 trace 文件中看到,消耗最多内存的进程就是报 ORA-04030 的进程,消耗的内存为110M,并未发现其他更消耗内存的进程。
4)是否设置了操作系统限制?
查看操作系统限制,Oracle 用户的限制
http://www.cddba.com/root 用户的限制:
从上面 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 用户进行的。
所以在 crs 会继承 root 用户的 limit 属性,当通过 crs 或则 srvctl 命令启动监听时,也会继承 root 用户相应的 limit 属性,即 data(kbytes)为131072。如何验证该推断?
现在通过监听的形式进行连接
通过 dbx 工具查看该进程的 limit 信息
从上面的内容可以看到 data 属性的 limit 值为134217728bytes 即131072kbytes 与 root 的 data(kbytes) 131072 值完全吻合(stack 的33554432bytes 即32768也与 root 的 stack(kbytes) 32768一致),说明是通过监听建立连接进程的 limit 继承于 root 用户。
使用不同监听进行连接
跟踪这个服务器进程
从上面内容可以看到,如果不通过监听连接数据库创建的进程,它的 data 限制为 unlimited 的即无限制。
最后查看 crs 中监听的启动日志:
(/u01/oracle/product/10.2.0/db_1/log/gisdata2/racg 中的日志文件 ora.gisdata2.LISTENER_GISDATA2.lsnr.log):
从以上内容可以看到监听是由 crs(或则是 srvctl 命令)启动的以及监听的运行时间:
监听启动的时间也与日志中的时间对应。
那为什么 CRS 的使用会使用 ROOT 用户的 limit 限制呢?
首先,得明白在 Linux 中,每触发任何一个事件时,系统都会为它定义一个进程,并且给予一个 ID,即 PID,同时会根据触发这个进程的用户与相关属性关系,给这个 PID 设置有效的权限。
从上面可以知道,系统启动以后,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 错误的概率