黄远邦小y
作者黄远邦小y·2017-03-29 14:48
技术总监·中亦科技

Oracle非活动会话内存泄露带来的危机

字数 5484阅读 7517评论 0赞 0

前言

做好大型数据中心的运维不是一件容易的事,因为光技术层面,就涉及到众多的技术领域。
日常运维中,有一类问题是大家比较头疼的,就是边界问题,例如数据库内存泄露的问题。
处理该类问题,既需要懂系统DBA的知识,又需要懂开发DBA的知识,还需要对操作系统、中间件的原理有足够的了解。
例如下图,Oracle数据库服务器占用的私有内存(PGA)在不断增长,如果不及时处理,服务器由于内存耗尽甚至无法telnet/ssh登陆。
微信图片_20170329143326.png

微信图片_20170329143326.png

最后,常常陷入到这样一种境地:
运维怀疑是应用程序问题,但拿不出证据,项目组也不承认。
最后的结果是陷入人肉运维,只能定期重启,导致大家苦不堪言,这也是运维喊累的一个很重要的原因,只有凡事必查根因,才会越做越顺,否则问题只会越堆越多,多到大家受不了。
远邦今天要和大家分享的就是这样一个综合、复杂问题的抽丝剥茧的技术分析过程。
文章的最后将从运维与开发层面提炼共性的风险提示和注意事项,希望能对大家有所帮助和启发!

1.问题来了

北京,晴,碧空如洗,万里无云。
正在工位上远程分析一个客户问题的时候,销售L来电话了,来活了。
“小y,有一家潜在的银行客户,关键业务系统出现了一个很严重的问题,困扰他们很久了。已经找好多人查过了,但一直没找到根本原因,如果我们可以找到原因并顺利解决掉该问题,后面的事情就交给我吧”
“好吧,我尽力”。实际上,听到这里,我就一下来了精神,有点迫不及待了。
赶紧要到客户的联系方式,约了第二天到现场进行分析,顺便了解了一下问题的情况。
原来,他们一个重要的业务系统,ORACLE数据库服务器采用的是IBM的小机,内存使用率随着系统运行,呈现逐渐增长的趋势,如果不及时处理,服务器由于内存耗尽甚至无法telnet/ssh登陆。私有内存增长趋势如下图所示:
内存耗尽导致无法登陆服务器的情况已经发生好几次了。一开始,通过定期重启数据库来解决,后来,他们发现重启中间件服务器也可以解决,就一直沿用定期重启中间件的方式了。
前期找的人的分析无非是两种方向:
一种怀疑是oracle本身的BUG,但不知道是哪个BUG;另外一种怀疑是应用程序的问题,但也说不出是哪支程序哪段代码有问题,拿不出证据,项目组也不承认,最后的结果是陷入人肉运维,只能定期重启。只是,如果重启必须在白天完成的话,影响就比较恶劣了,领导很重视这个问题。
难怪,原来是内存泄露的问题。
如前面所提到的,处理该类问题,既需要既懂系统DBA的知识,又需要懂开发DBA的知识,还需要对操作系统、中间件有足够的了解。
系统环境基本信息如下
微信图片_20170329143423.jpg

微信图片_20170329143423.jpg

2.分析过程

2.1 数据库服务器内存配置

(1)问题很严重
微信图片_20170329143543.png

微信图片_20170329143543.png

做好大型数据中心的运维不是一件容易的事,因为光技术层面,就涉及到众多的技术领域。
日常运维中,有一类问题是大家比较头疼的,就是边界问题,例如数据库内存泄露的问题。
处理该类问题,既需要懂系统DBA的知识,又需要懂开发DBA的知识,还需要对操作系统、中间件的原理有足够的了解。
例如下图,Oracle数据库服务器占用的私有内存(PGA)在不断增长,如果不及时处理,服务器由于内存耗尽甚至无法telnet/ssh登陆。
最后,常常陷入到这样一种境地:
运维怀疑是应用程序问题,但拿不出证据,项目组也不承认。
最后的结果是陷入人肉运维,只能定期重启,导致大家苦不堪言,这也是运维喊累的一个很重要的原因,只有凡事必查根因,才会越做越顺,否则问题只会越堆越多,多到大家受不了。
远邦今天要和大家分享的就是这样一个综合、复杂问题的抽丝剥茧的技术分析过程。
文章的最后将从运维与开发层面提炼共性的风险提示和注意事项,希望能对大家有所帮助和启发!
可以看到,数据库服务器配置了24G内存,但是free非小,只有22248*4k=86M.

Topas中可以看到,计算内存占了96%,换页空间使用率高达40%。说明数据库服务器已经由于内存不足发生了大量换页,系统已经处于非常危险的状态,如果不及时处理,将可能出现宕机的情况。解决问题前,可通过定期逐个重启WAS或者数据库服务器来临时解决。

(2)那么内存到底用到了哪里呢
该服务器只部署了数据库。数据库对内存的占用为SGA加PGA,以及每个服务进程在操作系统级的消耗。
SGA是全局的共享内存,PGA是每个服务进程的私有内存。
其中SGA参数设置为10G,PGA参数设置为2560M,服务进程个数为500个,每个进程在操作系统级是固定的,占用大概为4M,即数据库对内存的使用最预期为:10G + 2560M + 500 * 4 =15G左右。
从内存的使用规划上来说,占LPAR总内存大小的62.5%,是一个比较合理的值。
其中SGA是一个硬限制,但是PGA部分不是个硬限制,PGA只限制工作区workarea(Hash Join或排序)的大小,并不限制变量、数组的内存占用。如果应用程序使用了数组,并且数组的元素的个数在持续增大,PGA是限制不了这部分内存的。
下图是PGA内存部分的统计情况,关注红色加框部分,可以看到,虽然PGA参数设置的参数是2560M,但是PGA总分配的大小达到9776680960即9.1G,其中工作区的峰值为372M.也就是说,PGA应该是被变量、数组这些不受工作区限制的部分给占用掉了!
微信图片_20170329143652.png

微信图片_20170329143652.png

可以看到,数据库实际占用的内存大小为SGA+PGA+所有服务进程在操作系统级的内存消耗,即10G + 9.1g + 500 * 4 =21G左右,机器内存是24G,那么出现内存紧张、换页就是必然的了!

2.2 确认PGA内存泄漏

发出下列查询,确认PGA存在随着时间增长出现泄漏的趋势。
微信图片_20170329143758.jpg

微信图片_20170329143758.jpg

将结果制作出曲线图,可以清晰的看到,以一个小时为采样间隔的内存增长情况。PGA确认存在内存泄漏的情况,见下图。
微信图片_20170329143818.png
微信图片_20170329143818.png

2.3 确认内存泄漏的进程信息

发出下列查询,进一步确认,是哪些进程在消耗PGA内存。
微信图片_20170329143848.jpg

微信图片_20170329143848.jpg

其中PGA消耗大于100M的进程如下图所示
微信图片_20170329143908.png
微信图片_20170329143908.png

可以看到,很多会话都已经处于非活动状态即INACTIVE状态,但是所分配的PGA内存并没有释放,多个非活动的进程占用的私有内存依然超过400M.从SQL_ID可以看到,活动的会话在执行的SQL都是
微信图片_20170329143928.jpg
微信图片_20170329143928.jpg

取出package pkg_xx的代码,做进一步分析。

2.4 对PGA内存占用情况进一步分解

通常来说,PGA一般是SQL、存储过程执行结束后就应该立即释放,但是这里为什么执行结束了还不释放呢?此时我们需要继续深入分析,对不释放的进程做heapdump,看看内存中导致存储的是什么内容,做heapdump方式如下
微信图片_20170329143955.jpg

微信图片_20170329143955.jpg

对trace进行分析,方法如下,可以看到,占用内存最大的是"koh-kghu sessi"
微信图片_20170329144015.jpg
微信图片_20170329144015.jpg

进一步查看"koh-kghu sessi"中的内容,如下所示
微信图片_20170329144038.png
微信图片_20170329144038.png

通过分类汇总和计算,占用最大的是“pmuccst:adt/recored”和”pl/sql vc2“,分别是220M和80M,这两个就是存储PL/SQL的集合、数组等内容,之前的分析得到验证。

2.5 为什么非活动会话不释放PGA且PGA随时间增长

通常来说,PGA一般是SQL、存储过程执行结束后就应该立即释放。那么什么情况下会导致内存不释放呢?
答案是使用全局变量的时候。
全局变量的作用域是会话级(进程级)的,不会随着SQL、存储过程执行结束而结束生命周期,之所以是全局的,意味着同一个会话(进程)可以在下次调用时,依然看到该变量的值。
对应到XXX这个具体的CASE,导致内存泄漏的原因如下
应用程序在数据库中存在一个package,即PKG_XX。
该package中的存储过程sp_xx定义的参数p_target 是IN OUT 类型。参数p_target传入的是一个集合,即o_target。该type中包含一个数组ret。
存储过程中参数p_target被定义成了IN OUT,这意味着p_target的生命周期实际上是在存储过程之外,即上述存储过程执行完毕后p_target还会存在。该参数的生命周期并不是存储过程调用结束就释放,而是session级,意味着需要等到会话断开,生命周期才结束。
存储过程里有如下代码:
微信图片_20170329144230.jpg

微信图片_20170329144230.jpg

即如果传入的数组p_target.ret不为空,那么将为数组p_target.ret不断增加元素的个数(对应p_target.ret.EXTEND处代码),并为每个元素赋值(即 p_target.ret(p_target.ret.COUNT) := v_ret)
这意味着参数p_target在上述package的生命周期范围内会不停扩展,虽然存储过程调用结束了,但是由于是一个全局的变量,作用域是会话级,因此尽管存储过程执行结束后,会话已经处于inactive非活动状态,但是内存(PGA)不会释放。随着调用次数的不断增多,数组的元素的个数不断增大,继而导致数据库服务器每个服务进程的PGA内存不断增长,也就导致了数据库服务器的内存紧张。
因此需要开发商修改程序才可以解决该问题。
在调用PKG_XX. Sp_xx这个存储过程的其他存储过程/packgae处,对数组p_target.ret进行重新初始化,而不是让数组无限制扩展下去。

2.6 重现问题的测试案例

以下是利用全局变量/数组(与XXX系统sp_xx存储过程in out参数的作用域一样,全局作用域在会话级)的编程缺陷来重现非活动会话不释放PGA的过程。
可以看到,虽然存储过程执行结束,全局变量/数组被赋予了指,但是再次调用时由于全局变量/数组没有重新初始化,而是不断extend来扩展数组中元素的个数,该编程方式必然会导致PGA不释放,随着执行次数增大,PGA继续增大,既重现了内存泄漏的过程。XXX系统的变成问题与该测试案例类似,因此需要开发团队修改程序,利用重新初始化来限制数组中元素的个数,从而保证内存占用的稳定性。
微信图片_20170329144307.jpg

微信图片_20170329144307.jpg

微信图片_20170329144344.jpg
微信图片_20170329144344.jpg

微信图片_20170329144405.jpg
微信图片_20170329144405.jpg

微信图片_20170329144425.jpg
微信图片_20170329144425.jpg

微信图片_20170329144437.jpg
微信图片_20170329144437.jpg

2.7 真相大白

(1)原因总结如下
经分析,造成内存增长的原因是应用程序未正确初始化数组的BUG导致内存泄漏。
应用程序具体内存泄漏点如下:
应用程序在数据库中存在一个package,即PKG_XX。
该package中的存储过程sp_xx定义的参数p_target 是IN OUT 类型。参数p_target传入的是一个集合,即o_target。该type中包含了一个数组ret。
存储过程中参数p_target被定义成了IN OUT,这意味着p_target的生命周期实际上是在存储过程之外,即上述存储过程执行完毕后p_target还会存在,即还占着内存。该参数的生命周期并不是以存储过程调用结束为周期,而是以session断开做为结束周期,意味着需要等到会话断开,生命周期才结束。因为使用了jdbc连接池技术,因此会话是一个长连接,且连接重用比较频繁,因此会话不会轻易结束。
其中存储过程sp_xx里有如下代码:
微信图片_20170329144515.jpg

微信图片_20170329144515.jpg

即如果传入的数组p_target.ret不为空,那么将为数组p_target.ret不断增加元素的个数(对应p_target.ret.EXTEND处代码),并为每个元素赋值(即p_target.ret(p_target.ret.COUNT) := v_ret)
这意味着参数p_target在上述package的生命周期范围内会不停扩展,虽然存储过程调用结束了,但是由于是一个全局的变量,作用域是会话级,因此尽管存储过程执行结束后,虽然会话已经处于inactive非活动状态,但是内存(PGA)不会释放。随着调用次数的不断增多,数组的元素的个数不断增大,继而导致数据库服务器每个服务进程的PGA内存不断增长,也就导致了数据库服务器的内存紧张。
(2)建议如下:
需要开发商修改程序来修复该问题。修复方式为:
在调用PKG_XX. Sp_xx这个存储过程的其他存储过程/packgae处,对数组p_target.ret进行重新初始化,而不是每次执行该过程,都对数据中的元素做extend,让数组无限制扩展下去。
数组重新初始化后,也就解决了内存随时间增长并且内存使用不可控的问题。
在项目组按照建议修改程序后,问题不再发生。

2.8 风险提示

1)运维需要添加对非活动进程依然占用较多PGA内存的监控。
2)开发在使用全局变量时,务必关注是否存在内存泄露的问题。
建议记住下列查询,不定期监控,如果出现多余1行的返回结果,即非活动进程依然在消耗PGA内存,则可能存在全局变量导致内存泄露的问题,需要提前解决。
微信图片_20170329144626.jpg

微信图片_20170329144626.jpg

如果觉得我的文章对您有用,请点赞。您的支持将鼓励我继续创作!

0

添加新评论0 条评论

Ctrl+Enter 发表

作者其他文章

相关文章

相关问题

相关资料

X社区推广