转载链接:https://blog.csdn.net/xujinyang/article/details/8880496
某项目,使用红旗DC Server 5.0 for x86_64 SP2,运行Oracle RAC 10.2.0.4。而应用服务器上的应用通过Oracle客户端来连接,为常连接的方式。当进行应用的压力测试时,发现数据库服务器在运行一段时间(约3个小时后),系统会失去响应。当做了大量的系统状态及内存使用情况的分析后,发现系统失去响应的原因是,Oracle不断的申请内存,直到内存消耗完所导致的。深究其原因,是由于系统的核心参数vm.nr_hugepages与SGA的大小不匹配,Oracle并没有使用HugePages来分配SGA,而是在不断的消耗系统其它内存。
一、故障现象
系统环境为:
引用
硬件: 4 x IBM x3950 M2 (两台堆叠 8 x Xeon E7420四核(共32个) 64G内存)
操作系统: 红旗 DC Server 5.0 for x86_64 SP2
应用: Oracle RAC 10.2.0.4
当进行应用压力测试时,发现系统可用内存不断减少,这是一个逐步表现的过程,可通过下面的ps命令间隔一段时间,观察Oracle的某进程对内存的占用是否不断增加:
引用
# ps aux|grep -w 3244
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
oracle 3244 0.2 1.2 30865100 792376 ? Ss May12 0:21 oraclepostdbs1 (LOCAL=NO)
oracle 3244 0.2 1.2 30865100 838360 ? Ss May12 0:23 oraclepostdbs1 (LOCAL=NO)
oracle 3244 0.2 1.3 30865100 902184 ? Ss May12 0:25 oraclepostdbs1 (LOCAL=NO)
系统快失去响应前,内存状态为:
引用
# free -m
total used free shared buffers cached
Mem: 63839 63415 423 0 1 16387
-/+ buffers/cache: 47026 16812
Swap: 16002 971 15030
#vmstat -k 2
procs -----------memory---------- ---swap-- -----io---- --system-- ----cpu----
r b swpd free buff cache si so bi bo in cs us sy id wa
25 0 991516 431400 788 16775636 54 0 59 1269 11004 21412 11 61 28 0
18 0 991444 431976 780 16775648 74 0 85 1551 13373 24064 10 49 42 0
10 0 991388 432848 780 16775200 50 0 117 2374 9521 23511 13 39 48 0
以上面的free显示为例,这里在核心中设置了核心的保留内存为400m:
引用
vm.min_free_kbytes=409600
可见,这时的系统可用内存已几乎消耗殆尽(上面设置的400m为核心保留,其它应用不能使用),并且,从vmstat可知,因物理内存不足,正在不断的进行页面交换,使用swap空间。由于正在进行压力测试,对系统的内存消耗压力很大,因此,从top状态,也会看到kswapd进程会一直占用CPU,以进行页面交换:
系统日志报:
引用
May 9 08:02:19 gdracdb1 kernel: Node 0 HighMem per-cpu: empty
May 9 08:02:19 gdracdb1 kernel:
May 9 08:02:19 gdracdb1 kernel: Free pages: 18728kB (0kB HighMem)
May 9 08:02:19 gdracdb1 kernel: Active:5925372 inactive:2669 dirty:62
writeback:0 unstable:0 free:4682 slab:60603 mapped:5925032
pagetables:6178345
May 9 08:02:19 gdracdb1 kernel: Node 0 DMA free:10472kB min:0kB low:0kB
high:0kB active:0kB inactive:0kB present:16384kB pages_scanned:19479
all_unreclaimable? yes
May 9 08:02:19 gdracdb1 kernel: protections[]: 0 0 0
May 9 08:02:19 gdracdb1 kernel: Node 0 Normal free:8256kB min:8280kB
low:16560kB high:24840kB active:23701520kB inactive:10676kB
present:68665344kB pages_scanned:0 all_unreclaimable? no
May 9 08:02:19 gdracdb1 kernel: protections[]: 0 0 0
May 9 08:02:19 gdracdb1 kernel: Node 0 HighMem free:0kB min:128kB low:256kB
high:384kB active:0kB inactive:0kB present:0kB pages_scanned:0
all_unreclaimable? no
May 9 08:02:19 gdracdb1 kernel: protections[]: 0 0 0
May 9 08:02:19 gdracdb1 kernel: Node 0 DMA: 0*4kB 1*8kB 0*16kB 1*32kB
1*64kB 3*128kB 1*256kB 1*512kB 1*1024kB 0*2048kB 2*4096kB = 10472kB
May 9 08:02:22 gdracdb1 kernel: Node 0 Normal: 20*4kB 4*8kB 5*16kB 2*32kB
27*64kB 1*128kB 0*256kB 0*512kB 0*1024kB 1*2048kB 1*4096kB = 8256kB
May 9 08:02:22 gdracdb1 kernel: Node 0 HighMem: empty
May 9 08:02:22 gdracdb1 kernel: Swap cache: add 87984, delete 87977, find
13381/15259, race 0+0
May 9 08:02:22 gdracdb1 kernel: Free swap: 16143224kB
May 9 08:02:22 gdracdb1 kernel: 17170432 pages of RAM
May 9 08:02:22 gdracdb1 kernel: 827519 reserved pages
May 9 08:02:22 gdracdb1 kernel: 253602825 pages shared
May 9 08:02:23 gdracdb1 kernel: 7 pages swap cached
若应用压力仍不停止,系统将没有内存供Oracle继续使用,系统失去响应的原因有两个:
引用
1、因物理内存不足,系统需由kswapd交换页面以腾出更多的可用物理内存,但swap使用的是磁盘,会给本来压力就大的CPU,增加更多的I/O负载,CPU idle几乎为0,无法响应;
2、因主要问题是供Oracle申请的物理内存不足,即使用swapoff取消swap,仍会因可用内存不足,系统无法响应情况。
二、问题分析
经过多次的测试和分析(可参考附录),最后发现,问题原因与Oracle上设置的SGA大小、系统的vm.nr_hugepages有关。
这里,Oracle的SGA值为30000M:
引用
SQL> show parameter sga;
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
lock_sga boolean FALSE
pre_page_sga boolean FALSE
sga_max_size big integer 30000M
sga_target big integer 30000M
从系统top命令发现,oracle的申请的虚拟内存是29.4G(接近30000M),而Oracle启动后,多个oracle进程的常驻内存在不断的增加,故怀疑其使用的并不是SGA申请的内存:
引用
# top
top - 17:47:48 up 1 day, 3:35, 5 users, load average: 0.27, 0.25, 0.16
Tasks: 444 total, 1 running, 443 sleeping, 0 stopped, 0 zombie
Cpu(s): 0.3% us, 0.2% sy, 0.0% ni, 99.3% id, 0.0% wa, 0.0% hi, 0.1% si
Mem: 65371656k total, 5973772k used, 59397884k free, 213504k buffers
Swap: 16386292k total, 0k used, 16386292k free, 4542984k cached
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
8424 oracle -2 0 29.4g 326m 311m S 2 0.5 0:59.87 oracle
8428 oracle -2 0 29.4g 329m 314m S 2 0.5 1:00.30 oracle
8432 oracle -2 0 29.4g 328m 313m S 1 0.5 1:00.32 oracle
8436 oracle -2 0 29.4g 326m 311m S 1 0.5 1:00.85 oracle
8440 oracle -2 0 29.4g 323m 308m S 1 0.5 1:00.50 oracle
1419 root 16 0 522m 28m 9748 S 1 0.0 0:48.14 crsd.bin
8416 oracle -2 0 29.4g 321m 306m S 1 0.5 0:59.69 oracle
8420 oracle -2 0 29.4g 329m 314m S 1 0.5 0:59.47 oracle
8444 oracle -2 0 29.4g 320m 305m S 1 0.5 0:59.96 oracle
23551 oracle 16 0 6412 1288 772 R 1 0.0 0:00.08 top
1453 oracle RT 0 238m 224m 23m S 0 0.4 0:47.41 ocssd.bin
8476 oracle 16 0 29.4g 64m 60m S 0 0.1 0:02.12 oracle
8860 oracle 16 0 29.4g 64m 58m S 0 0.1 0:01.47 oracle
1 root 16 0 4752 552 460 S 0 0.0 0:03.19 init
根据这份Oracle 11g的资料:H Very Large Memory on Linux,在如此大内存的环境中(64G),系统核心参数vm.nr_hugepages 的大小与Oracle 上设置的SGA是相关的。
而默认情况下,vm.nr_hugepages 是没有设置的,其大小为0。
引用
# cat /proc/meminfo |grep Huge
HugePages_Total: 0
HugePages_Free:
Hugepagesize: 2048 kB
三、解决问题
1、配置合适的vm.nr_hugepages 值
参考H Very Large Memory on Linux提供的一个vm.nr_hugepages 建议值计算脚本。
hugepages_settings.sh
#!/bin/bash
#
# hugepages_settings.sh
#
# Linux bash script to compute values for the
# recommended HugePages/HugeTLB configuration
#
# Note: This script does calculation for all shared memory
# segments available when the script is run, no matter it
# is an Oracle RDBMS shared memory segment or not.
# Check for the kernel version
KERN=`uname -r | awk -F. '{ printf("%d.%d\n",$1,$2); }'`
# Find out the HugePage size
HPG_SZ=`grep Hugepagesize /proc/meminfo | awk {'print $2'}`
# Start from 1 pages to be on the safe side and guarantee 1 free HugePage
NUM_PG=1
# Cumulative number of pages required to handle the running shared memory segments
for SEG_BYTES in `ipcs -m | awk {'print $5'} | grep "[0-9][0-9]*"`
do
MIN_PG=`echo "$SEG_BYTES/($HPG_SZ*1024)" | bc -q`
if [ $MIN_PG -gt 0 ]; then
NUM_PG=`echo "$NUM_PG+$MIN_PG+1" | bc -q`
fi
done
# Finish with results
case $KERN in
'2.4') HUGETLB_POOL=`echo "$NUM_PG*$HPG_SZ/1024" | bc -q`;
echo "Recommended setting: vm.hugetlb_pool = $HUGETLB_POOL" ;;
'2.6') echo "Recommended setting: vm.nr_hugepages = $NUM_PG" ;;
*) echo "Unrecognized kernel version $KERN. Exiting." ;;
esac
# End
在已设置好SGA大小及相关参数的情况下,先启动Oracle,然后运行脚本:
引用
# ./hugepages_settings.sh
Recommended setting: vm.nr_hugepages = 15067
※ 注意:
使用该脚本时,必须先设置好SGA,并启动Oracle,待内存申请稳定后才运行。否则,其中用到的ipcs -m所获取的值并不准确。
把该参数加入/etc/sysctl.conf中,然后重启系统:
引用
vm.nr_hugepages=15067
该值必须重启系统才能生效。
2、配置oracle用户的memlock
修改/etc/security/limits.conf文件,加入:
引用
# cat /etc/security/limits.conf|grep lock
# - memlock - max locked-in-memory address space (KB)
# - locks - max number of file locks the user can hold
oracle soft memlock 30857216
oracle hard memlock 30857216
计算公式为:>=HugePages_Total×1024,这里设置了2倍:15067*1024*2 = 30857216。
重新登录到oracle用户,用ulimit -l 可看到该值。
最后,重新运行Oracle:
# srvctl stop instance -d postdbs -i postdbs1
# srvctl start instance -d postdbs -i postdbs1
从top观察,可看到oracle运行后,会启动一个oracle进程逐步申请内存,直到SGA的大小:
引用
# top
top - 18:12:50 up 12 min, 1 user, load average: 0.21, 0.47, 0.43
Tasks: 406 total, 1 running, 405 sleeping, 0 stopped, 0 zombie
Cpu(s): 0.2% us, 0.1% sy, 0.0% ni, 99.6% id, 0.0% wa, 0.0% hi, 0.1% si
Mem: 65371656k total, 34944692k used, 30426964k free, 127376k buffers
Swap: 16386292k total, 0k used, 16386292k free, 350956k cached
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
22100 oracle -2 0 29.4g 29g 29g S 2 47.0 0:06.28 oracle
22116 oracle -2 0 29.4g 29g 29g S 1 47.0 0:06.36 oracle
22090 oracle -2 0 29.4g 29g 29g S 1 47.0 0:05.99 oracle
22096 oracle -2 0 29.4g 29g 29g S 1 47.0 0:06.20 oracle
22104 oracle -2 0 29.4g 29g 29g S 1 47.0 0:06.23 oracle
22108 oracle -2 0 29.4g 29g 29g S 1 47.0 0:06.23 oracle
22112 oracle -2 0 29.4g 29g 29g S 1 47.0 0:06.30 oracle
22120 oracle -2 0 29.4g 29g 29g S 1 47.0 0:06.24 oracle
从/proc/meminfo也可观察到已经在使用HugePages:
引用
# cat /proc/meminfo |grep Huge
HugePages_Total: 15067
HugePages_Free: 3
Hugepagesize: 2048 kB
oracle使用的HugePages大小为:(15067-3)*2048 = 30851072 KB 。
再经压力测试,从top、ps aux、free等可看到Oracle的进程使用的内存是固定的(从SGA中申请),在没有额外操作的情况下,系统可用内存处于一个稳定值(这里使用ASM文件系统,故对ASM的磁盘I/O也会由SGA分配,系统cached值不大,free值较大),问题解决。
三、注意事项
1、SGA与vm.nr_hugepages的关系
设置vm.nr_hugepages 时必须注意,该值不能比SGA小,否则仍会发生上述的故障问题。我曾经参考这个链接:点击,根据其中的公式:
引用
nr_hugepages>=sga(mb)/Hugepagesize(mb)
把vm.nr_hugepages 设置为:30*1024/2 = 15360 ,但是后来启动Oracle后,发现仍存在故障。最后,发现原来是设置SGA时,把SGA设置为了30G(不是30000M):
引用
SQL> alter system set sga_target=30G scope=spfile sid='postdbs1';
SQL> alter system set sga_max_size=30G scope=spfile sid='postdbs1';
SGA的大小(对比前面的值):
引用
SQL> show parameter sga
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
lock_sga boolean FALSE
pre_page_sga boolean FALSE
sga_max_size big integer 30G
sga_target big integer 30G
可见,这里的vm.nr_hugepages 刚好等于SGA的大小,但是,vm.nr_hugepages还用于如ASM实例等,每个ASM实例约需100M左右的空间。所以,vm.nr_hugepages =15360 是不够的,故障仍会出现。
◎ 在SGA为30G时,vm.nr_hugepages应该设置为:
引用
# ./hugepages_settings.sh
Recommended setting: vm.nr_hugepages = 15428
在上面“解决问题”部分,我们设置的SGA大小是30000M,那么(30000+100)/2 = 15050 < 15067,所以“解决问题”中是成功的。
※ 注意:vm.nr_hugepages 的设置是否正确,可从HugePages的使用量来观察。
下面是分配不足的情况:
引用
# cat /proc/meminfo |grep Huge
HugePages_Total: 15067
HugePages_Free: 15004
Hugepagesize: 2048 kB
Oracle 启动后,仅因ASM实例的原因,使用了很少一部分的HugePages(由/etc/init.d/init.crs服务占用),对内存的使用不正常。
但是,因为使用HugePages部分的共享内存不能被swap,也不能被其他进程使用,所以,如果该值比Oracle需要的值大了,所多出来的部分就是浪费。因此,HugePages 应该选择一个最佳值,以让HugePages_Free接近或等于0。(可参考上面提供的脚本)
2、vm.nr_hugepages 的调整
调整vm.nr_hugepages 的大小后,必须重启系统才能生效。
3、Oracle RAC启动方式
当用alert调整SGA的大小后,不能单纯的用shutdown immediate关闭数据库实例,而必须用srvctl 重启节点的方式才能生效。
四、附录
开始,因从top上观察到,最后时刻是kswapd在进行页面交换,占用大量的CPU资源。所以,首先想到的是swap的问题,故曾对系统核心参数进行了不少的调整工作,特把一些较重要的参数记录下来:
1、/proc/sys/vm/min_free_kbytes
该值设定的是系统保留给核心自己使用的内存大小,其他应用程序不能使用这部分的内容,包括系统cache也不可以。即free命令首行free部分的保留值。默认情况下,系统在启动时会根据物理内存的大小自动计算出该值。单位是(KB):
引用
# cat /proc/sys/vm/min_free_kbytes
8286
该值不应该太大,太大了,其他应用可用的内存就会减少,提高kswapd交换的频率;也不应该太小,太小了,供核心调度的内存范围将不足,可能会因内存不够,发生OOM。该值与kswapd等存在以下关系:
更详细的信息,请参考:这里。
2、/proc/sys/vm/vfs_cache_pressure
该值用于决定虚拟内存directory和i-node缓冲回收的倾向,这个值越大,回收的倾向越严重。默认值是100:
引用
# cat /proc/sys/vm/vfs_cache_pressure
100
提高该值,可以让系统更快的回收缓存,释放更多的物理内存。
3、/proc/sys/vm/swappiness
该值用于决定核心使用swap空间的频率。默认值为60:
引用
# cat /proc/sys/vm/swappiness
60
该值越大,将越早使用kswapd进行页面交换,即使用swap空间;该值越小,越不希望使用swap空间。但该值<10时,除非可用内存(物理内存-核心保留的内存)真的已几乎耗尽,将会尽量不使用swap空间。注意,若该值设定太低(<10)时,一旦在cached较大,而发生突发的大内存申请时,可能会导致kswapd不断的进行页面交换,占用大量的CPU资源,最后导致机器响应不过来(hang住了)。
4、/proc/sys/vm/pagecache
该值决定核心回收内存钱,将会把内存用于pagecahe的最大百分比。默认值为100,即所有的系统内存都可以用于pagecache 。该值在2.4核心的时候曾经提供过,但后来被取消了,直到DC 5.0 SP3或Asianux 3.0 SP1以后更新算法后,可再次设置,这时,只有一个参数,代表百分比。详细请见:这里。
5、 /proc/sys/vm/drop_caches
默认值为0,可以使用下面的命令强制清空cache:
# echo 3 > /proc/sys/vm/drop_caches
用free可看到效果,而剩余的cached值是有程序正在占用的内存,除非进程退出,否则无法删除该部分的内容。当然,该动作清空缓存后,会影响部分性能。另外,若多次执行该动作后,cached仍在不断的增长,说明有程序在不断的消耗内存,并且没有释放出来,也可作为一个判断内存使用情况的依据。(该参数在DC 5.0 SP3以后才能使用,详细可见:这里)
正常情况下,除非应用对内存的使用有问题,否则是不需要手动执行该动作的。
※ 注意:
虽然以上核心参数都很重要,当初我也受DBA日记 修订版、kswapd0 hangs the system 等文章的影响,修改了这些参数。但是测试结果告诉我们,原因并不在这里,加入这些参数或许可以缓解宕机的时间,但是并不能彻底解决问题。因为故障的根源在于Oracle不断的申请内存,但又没有释放,直到可用内存被消耗完。所以,判断类似问题的办法,仍必须靠top、ps aux、free等监控手段,逐步分析,寻找故障根源。