Oracle的SGA与系统vm.nr_hugepages不匹配导致的故障

某项目,使用红旗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等监控手段,逐步分析,寻找故障根源。

猜你喜欢

转载自blog.csdn.net/qyq88888/article/details/117304757