记redis程序oom的排查过程

钉钉群收到告警,redis服务两个挂了。登录服务器通过dmesg命令查看进程挂掉原因,看到以下日志

[21367253.038320] Node 0 DMA free:15908kB min:8kB low:8kB high:12kB active_anon:0kB inactive_anon:0kB active_file:0kB inactive_file:0kB unevictable:0kB isolated(anon):0kB isolated(file):0kB present:15992kB managed:15908kB mlocked:0kB dirty:0kB writeback:0kB mapped:0kB shmem:0kB slab_reclaimable:0kB slab_unreclaimable:0kB kernel_stack:0kB pagetables:0kB unstable:0kB bounce:0kB free_pcp:0kB local_pcp:0kB free_cma:0kB writeback_tmp:0kB pages_scanned:0 all_unreclaimable? yes
[21367253.046752] lowmem_reserve[]: 0 2825 96503 96503
[21367253.048506] Node 0 DMA32 free:376572kB min:1976kB low:2468kB high:2964kB active_anon:2441228kB inactive_anon:16kB active_file:12kB inactive_file:16kB unevictable:0kB isolated(anon):0kB isolated(file):0kB present:3129212kB managed:2893660kB mlocked:0kB dirty:0kB writeback:0kB mapped:0kB shmem:16kB slab_reclaimable:49568kB slab_unreclaimable:2944kB kernel_stack:448kB pagetables:16104kB unstable:0kB bounce:0kB free_pcp:0kB local_pcp:0kB free_cma:0kB writeback_tmp:0kB pages_scanned:8 all_unreclaimable? no
[21367253.057509] lowmem_reserve[]: 0 0 93677 93677
[21367253.059225] Node 0 Normal free:85808kB min:65592kB low:81988kB high:98388kB active_anon:94857072kB inactive_anon:256kB active_file:3492kB inactive_file:3120kB unevictable:0kB isolated(anon):0kB isolated(file):128kB present:97517568kB managed:95925820kB mlocked:0kB dirty:0kB writeback:0kB mapped:996kB shmem:620kB slab_reclaimable:72616kB slab_unreclaimable:35820kB kernel_stack:4752kB pagetables:227860kB unstable:0kB bounce:0kB free_pcp:2560kB local_pcp:0kB free_cma:0kB writeback_tmp:0kB pages_scanned:9970 all_unreclaimable? yes
[21367253.069788] lowmem_reserve[]: 0 0 0 0
[21367253.071491] Node 0 DMA: 1*4kB (U) 0*8kB 0*16kB 1*32kB (U) 2*64kB (U) 1*128kB (U) 1*256kB (U) 0*512kB 1*1024kB (U) 1*2048kB (M) 3*4096kB (M) = 15908kB
[21367253.075622] Node 0 DMA32: 2355*4kB (UEM) 2103*8kB (UEM) 584*16kB (UEM) 293*32kB (UEM) 131*64kB (UEM) 5*128kB (EM) 58*256kB (UM) 275*512kB (UM) 163*1024kB (M) 0*2048kB 0*4096kB = 376548kB
[21367253.080198] Node 0 Normal: 21012*4kB (UEM) 251*8kB (UEM) 21*16kB (EM) 2*32kB (EM) 1*64kB (M) 0*128kB 0*256kB 0*512kB 0*1024kB 0*2048kB 0*4096kB = 86520kB
[21367253.084547] Node 0 hugepages_total=0 hugepages_free=0 hugepages_surp=0 hugepages_size=1048576kB
[21367253.086812] Node 0 hugepages_total=0 hugepages_free=0 hugepages_surp=0 hugepages_size=2048kB
[21367253.089058] 2129 total pagecache pages
[21367253.090883] 0 pages in swap cache
[21367253.092648] Swap cache stats: add 0, delete 0, find 0/0
[21367253.094588] Free swap  = 0kB
[21367253.096303] Total swap = 0kB
[21367253.098010] 25165693 pages RAM
[21367253.099735] 0 pages HighMem/MovableOnly
[21367253.101522] 456846 pages reserved

[21367253.103259] [ pid ]   uid  tgid total_vm      rss nr_ptes swapents oom_score_adj name
[21367253.105522] [ 3086]     0  3086    20024       87      45        0             0 systemd-journal
[21367253.107824] [ 5969]     0  5969     6718      217      18        0             0 systemd-logind
[21367253.110105] [ 5970]    81  5970    14564      179      31        0          -900 dbus-daemon
[21367253.112372] [ 5983]     0  5983   615231    10802      91        0             0 CmsGoAgent.linu
[21367253.114683] [ 6054]     0  6054    27523       33      11        0             0 agetty
[21367253.116926] [ 6058]     0  6058    27523       33      11        0             0 agetty
[21367253.119135] [ 6316]     0  6316    26839      498      51        0             0 dhclient
[21367253.121366] [ 7035]     0  7035   138962     1919      39        0             0 ops-updater
[21367253.123756] [ 7079]     0  7079   589598     6289      85        0             0 falcon-agent
[21367253.126011] [13594]  1000 13594 11667269  6644047   19394        0             0 redis-server
[21367253.128270] [13599]  1000 13599  8243013  6664731   15518        0             0 redis-server
[21367253.130502] [15685]  1000 15685  6917957  5449843   13398        0             0 redis-server
[21367253.132724] [15693]  1000 15693  5857093  5411665   11080        0             0 redis-server
[21367253.134943] [32218]  1000 32218   127813    52828     152        0             0 redis-server
[21367253.137165] [32223]  1000 32223   113477    56705     139        0             0 redis-server
[21367253.139349] [32736]  1000 32736    28295       67      13        0             0 sh
[21367253.141429] [20078]     0 20078    78343     3993     104        0             0 salt-minion
[21367253.143574] [20083]     0 20083   237123    11662     163        0             0 salt-minion
[21367253.145663] [20087]     0 20087   103223     6598     113        0             0 salt-minion
[21367253.147739] [14046]     0 14046    10494      216      13        0             0 aliyun-service
[21367253.149802] [ 3366]     0  3366   109273      676      28        0             0 AliHips
[21367253.151773] [ 9827]     0  9827     6614      359      18        0             0 AliYunDunUpdate
[21367253.153774] [ 9944]     0  9944    32536     1953      59        0             0 AliYunDun
[21367253.155691] [  340]     0   340    28230      258      57        0         -1000 sshd
[21367253.157612] [  483]    28   483   148240      135      43        0             0 nscd
[21367253.159441] [  557]     0   557    13882      112      26        0         -1000 auditd
[21367253.161233] [  691]   998   691    24120       95      18        0             0 chronyd
[21367253.163013] [  729]     0   729    31573      157      18        0             0 crond
[21367253.164794] [  814]   999   814   153061     2128      63        0             0 polkitd
[21367253.166505] [28544]     0 28544    11152      105      23        0         -1000 systemd-udevd
[21367253.168272] [28629]     0 28629   160279      185     142        0             0 rsyslogd
[21367253.169980] Out of memory: Kill process 13599 (redis-server) score 270 or sacrifice child
[21367253.171663] Killed process 13599 (redis-server) total-vm:32972052kB, anon-rss:26658828kB, file-rss:88kB, shmem-rss:0kB

日志内容显示 Out of memory: Kill process 13599 (redis-server),所以是由于内存不足,被系统给结束了进程。

原因分析

1). redis-server进程触发了oom killer,既redis要申请的内存大于了系统可用的物理内存大小。/proc/sys/vm/min_free_kbytes 参数来控制,当系统可用内存(不包含buffer和cache)小于这个值的时候,系统会启动内核线程kswapd来对内存进行回收。而还是触发了oom killer,则表明内存真的不够用了或者在内存回收前或者回收中直接触发了oom killer。

2). 如下的输出表明了申请了3次内存都没有成功

[21367253.038320] Node 0 DMA free:15908kB min:8kB low:8kB high:12kB active_anon:0kB inactive_anon:0kB active_file:0kB inactive_file:0kB unevictable:0kB isolated(anon):0kB isolated(file):0kB present:15992kB managed:15908kB mlocked:0kB dirty:0kB writeback:0kB mapped:0kB shmem:0kB slab_reclaimable:0kB slab_unreclaimable:0kB kernel_stack:0kB pagetables:0kB unstable:0kB bounce:0kB free_pcp:0kB local_pcp:0kB free_cma:0kB writeback_tmp:0kB pages_scanned:0 all_unreclaimable? yes
[21367253.046752] lowmem_reserve[]: 0 2825 96503 96503
[21367253.048506] Node 0 DMA32 free:376572kB min:1976kB low:2468kB high:2964kB active_anon:2441228kB inactive_anon:16kB active_file:12kB inactive_file:16kB unevictable:0kB isolated(anon):0kB isolated(file):0kB present:3129212kB managed:2893660kB mlocked:0kB dirty:0kB writeback:0kB mapped:0kB shmem:16kB slab_reclaimable:49568kB slab_unreclaimable:2944kB kernel_stack:448kB pagetables:16104kB unstable:0kB bounce:0kB free_pcp:0kB local_pcp:0kB free_cma:0kB writeback_tmp:0kB pages_scanned:8 all_unreclaimable? no
[21367253.057509] lowmem_reserve[]: 0 0 93677 93677
[21367253.059225] Node 0 Normal free:85808kB min:65592kB low:81988kB high:98388kB active_anon:94857072kB inactive_anon:256kB active_file:3492kB inactive_file:3120kB unevictable:0kB isolated(anon):0kB isolated(file):128kB present:97517568kB managed:95925820kB mlocked:0kB dirty:0kB writeback:0kB mapped:996kB shmem:620kB slab_reclaimable:72616kB slab_unreclaimable:35820kB kernel_stack:4752kB pagetables:227860kB unstable:0kB bounce:0kB free_pcp:2560kB local_pcp:0kB free_cma:0kB writeback_tmp:0kB pages_scanned:9970 all_unreclaimable? yes
[21367253.069788] lowmem_reserve[]: 0 0 0 0
[21367253.071491] Node 0 DMA: 1*4kB (U) 0*8kB 0*16kB 1*32kB (U) 2*64kB (U) 1*128kB (U) 1*256kB (U) 0*512kB 1*1024kB (U) 1*2048kB (M) 3*4096kB (M) = 15908kB
[21367253.075622] Node 0 DMA32: 2355*4kB (UEM) 2103*8kB (UEM) 584*16kB (UEM) 293*32kB (UEM) 131*64kB (UEM) 5*128kB (EM) 58*256kB (UM) 275*512kB (UM) 163*1024kB (M) 0*2048kB 0*4096kB = 376548kB
[21367253.080198] Node 0 Normal: 21012*4kB (UEM) 251*8kB (UEM) 21*16kB (EM) 2*32kB (EM) 1*64kB (M) 0*128kB 0*256kB 0*512kB 0*1024kB 0*2048kB 0*4096kB = 86520kB
[21367253.084547] Node 0 hugepages_total=0 hugepages_free=0 hugepages_surp=0 hugepages_size=1048576kB
[21367253.086812] Node 0 hugepages_total=0 hugepages_free=0 hugepages_surp=0 hugepages_size=2048kB
[21367253.089058] 2129 total pagecache pages
[21367253.090883] 0 pages in swap cache
[21367253.092648] Swap cache stats: add 0, delete 0, find 0/0
[21367253.094588] Free swap  = 0kB
[21367253.096303] Total swap = 0kB
[21367253.098010] 25165693 pages RAM
[21367253.099735] 0 pages HighMem/MovableOnly
[21367253.101522] 456846 pages reserved

各个zone的情况如何?

[21367253.038320] Node 0 DMA free:15908kB min:8kB low:8kB high:12kB active_anon:0kB inactive_anon:0kB active_file:0kB inactive_file:0kB unevictable:0kB isolated(anon):0kB isolated(file):0kB present:15992kB managed:15908kB mlocked:0kB dirty:0kB writeback:0kB mapped:0kB shmem:0kB slab_reclaimable:0kB slab_unreclaimable:0kB kernel_stack:0kB pagetables:0kB unstable:0kB bounce:0kB free_pcp:0kB local_pcp:0kB free_cma:0kB writeback_tmp:0kB pages_scanned:0 all_unreclaimable? yes
lowmem_reserve[]: 0 2825 96503 96503

[21367253.048506] Node 0 DMA32 free:376572kB min:1976kB low:2468kB high:2964kB active_anon:2441228kB inactive_anon:16kB active_file:12kB inactive_file:16kB unevictable:0kB isolated(anon):0kB isolated(file):0kB present:3129212kB managed:2893660kB mlocked:0kB dirty:0kB writeback:0kB mapped:0kB shmem:16kB slab_reclaimable:49568kB slab_unreclaimable:2944kB kernel_stack:448kB pagetables:16104kB unstable:0kB bounce:0kB free_pcp:0kB local_pcp:0kB free_cma:0kB writeback_tmp:0kB pages_scanned:8 all_unreclaimable? no
lowmem_reserve[]: 0 0 93677 93677

[21367253.059225] Node 0 Normal free:85808kB min:65592kB low:81988kB high:98388kB active_anon:94857072kB inactive_anon:256kB active_file:3492kB inactive_file:3120kB unevictable:0kB isolated(anon):0kB isolated(file):128kB present:97517568kB managed:95925820kB mlocked:0kB dirty:0kB writeback:0kB mapped:996kB shmem:620kB slab_reclaimable:72616kB slab_unreclaimable:35820kB kernel_stack:4752kB pagetables:227860kB unstable:0kB bounce:0kB free_pcp:2560kB local_pcp:0kB free_cma:0kB writeback_tmp:0kB pages_scanned:9970 all_unreclaimable? yes
lowmem_reserve[]: 0 0 0 0

可以看到Normal还有85808KB,DMA32还有376572 KB,DMA还有15908KB,
各个zone的free都大于min,分配是有链条的,Normal不够了,会从DMA32以及DMA去请求分配,所以为什么分配失败了呢?

虽然说分配内存会按照Normal、DMA32、DMA的顺序去分配,但是低端内存相对来说更宝贵些,为了防止低端内存被高端内存用完,,linux设计了保护机制,也就是lowmen_reserve,从上面的日志看,他们的值是这样的:

  • DMA(index=0): lowmem_reserve[]:0 2825 96503 96503
  • DMA32(index=1)owmem_reserve[]: 0 0 93677 93677
  • DMA32(index=2)lowmem_reserve[]: 0 0 0 0

lowmen_reserve的值是一个数组,当Normal(index=2)像DMA32申请内存的时候,需要满足条件:
DMA32 high+lowmem_reserve[2] < free,才能申请,来算下:

  • Normal:从自己这里申请,free(85808kB) > min(65592kB ),可以申请,但是只能申请 85808kB-65592kB = 20216kb,不够申请的内存,所以申请失败了(watermark[min]以下的内存属于系统的自留内存,用以满足特殊使用,所以不会给用户态的普通申请来用)

  • Normal转到DMA32申请:high(2964kB) + lowmem_reserve2*4 > DMA32 Free(376572kB),不允许申请

  • Normal转到DMA申请:high(12kB) + lowmem_reserve2*4 > DMA Free(15908kB),不允许申请,所以….最终失败了

min_free_kbytes
这里属于扩展知识了,和分析oom问题不大。我们知道了每个区都有min、low、high,那他们是怎么计算出来的,就是根据min_free_kbytes计算出来的,他本身在系统初始化的时候计算,最小128K,最大64M

  • watermark[min] = min_free_kbytes换算为page单位即可,假设为min_free_pages。(因为是每个zone各有一套watermark参数,实际计算效果是根据各个zone大小所占内存总大小的比例,而算出来的per zone min_free_pages)
  • watermark[low] = watermark[min] * 5 / 4
  • watermark[high] = watermark[min] * 3 / 2

min 和 low的区别

  • min下的内存是保留给内核使用的;当到达min,会触发内存的direct reclaim
  • low水位比min高一些,当内存可用量小于low的时候,会触发 kswapd回收内存,当kswapd慢慢的将内存 回收到high水位,就开始继续睡眠

3)被干掉进程信息

[21367253.169980] Out of memory: Kill process 13599 (redis-server) score 270 or sacrifice child
[21367253.171663] Killed process 13599 (redis-server) total-vm:32972052kB, anon-rss:26658828kB, file-rss:88kB, shmem-rss:0kB

被kill的进程pid是13599.进程所占用的内存页是 6664731,换算内存大概占用 换算为内存占用量是6664731*4096 = 26GB和 anon-rss:26658828kB显示占用的内存差不多。

[21367253.103259] [ pid ]   uid  tgid total_vm      rss nr_ptes swapents oom_score_adj name
[21367253.105522] [ 3086]     0  3086    20024       87      45        0             0 systemd-journal
[21367253.107824] [ 5969]     0  5969     6718      217      18        0             0 systemd-logind
[21367253.110105] [ 5970]    81  5970    14564      179      31        0          -900 dbus-daemon
[21367253.112372] [ 5983]     0  5983   615231   

进程输出的含义是:

  • pid进程ID。
  • uid用户ID。
  • tgid线程组ID。
  • total_vm虚拟内存使用(单位为4 kB内存页)
  • rss 常驻内存使用(单位4 kB内存页)
  • nr_ptes页表项
  • swapents交换条目
  • oom_score_adj通常为0;较低的数字表示当调用OOM杀手时,进程将不太可能死亡。 值越高越被系统有限结束进程。

(4)解决办法
这个redis进程占用了近26G的内存,而redis配置文件最大配置的内存是20G,也就是除了20g数据占用的内存,程序进程本身占用了近6G的数据。我们服务器可用内存是98G,之所以这次内存会不够用的原因是这台服务器部署了5个redis节点,每个节点的最大可用内存设置都是20G,这次由于大量写入数据导致redis节点内存暴增。所以解决办法就是减少部署的节点,比如机器总共内存是98G,最多部署4个节点,还有就是增减内存占用阈值报警监控,另外就是大量数据写入前检查机器节点内存是否够用。

扩展

1.什么是oom killer

他是Linux内核设计的一种机制,在内存不足的会后,选择一个占用内存较大的进程并kill掉这个进程,以满足内存申请的需求(内存不足的时候该怎么办,其实是个两难的事情,oom killer算是提供了一种方案吧)

2.在什么时候触发?

在内存不足的时候触发,再往细节看,主要牵涉到【linux的物理内存结构】和【overcommit机制】
2.1 内存结构 node、zone、page、order
对于物理内存内存,linux会把它分很多区(zone),zone上还有node的概念,zone下面是很多page,这些page是根据buddy分配算法组织的

上面的概念做下简单的介绍,对后面分析oom killer日志很有必要:

Node:每个CPU下的本地内存节点就是一个Node,如果是UMA架构下,就只有一个Node0,在NUMA架构下,会有多个Node
Zone:每个Node会划分很多域Zone,大概有下面这些

  1. ZONE_DMA:定义适合DMA的内存域,该区域的长度依赖于处理器类型。比如ARM所有地址都可以进行DMA,所以该值可以很大,,或者干脆不定义DMA类型的内存域。而在IA-32的处理器上,一般定义为16M。
  2. ZONE_DMA32:只在64位系统上有效,为一些32位外设DMA时分配内存。如果物理内存大于4G,该值为4G,否则与实际的物理内存大小相同。
  3. ZONE_NORMAL:定义可直接映射到内核空间的普通内存域。在64位系统上,如果物理内存小于4G,该内存域为空。而在32位系统上,该值最大为896M。
  4. ZONE_HIGHMEM:只在32位系统上有效,标记超过896M范围的内存。在64位系统上,由于地址空间巨大,超过4G的内存都分布在ZONE_NORMA内存域。
  5. ZONE_MOVABLE:伪内存域,为了实现减小内存碎片的机制。

分配价值链

  • 除了只能在某个区域分配的内存(比如ZONE_DMA),普通的内存分配会有一个“价值”的层次结构,按分配的“廉价度”依次为:ZONE_HIGHMEM > ZONE_NORMAL > ZONE_DMA。
  • 即内核在进行内存分配时,优先从高端内存进行分配,其次是普通内存,最后才是DMA内存

Page zone下面就是真正的内存页了,每个页基础大小是4K,他们维护在一个叫free_area的数组结构中。

  • order:数组的index,也叫order,实际对应的是page的大小,比如order为0,那么就是一堆1个空闲页(4K)组成的链表,order为1,就是一堆2个空闲页(8K)组成的链表,order为2,就是一堆4个空闲页(16K)组成的链表

2.2 overcommit
根据2.1,已经知道物理内存的大概结构以及分配的规则,不过实际上还有虚拟内存的存在,他的overcommit机制和oom killer有很大关系:

在实际申请内存的时候,比如申请1G,并不会在物理区域中分配1G的真实物理内存,而是分配1G的虚拟内存,等到需要的时候才去真正申请物理内存,也就是说申请不等于分配

所以说,可以申请比物理内存实际大的内存,也就是overcommit,这样会面临一个问题,就是当真的需要这么多内存的时候怎么办—>oom killer!

vm.overcommit_memory 接受三种值:

  • 0 – Heuristic overcommit handling. 这是缺省值,它允许overcommit,但过于明目张胆的overcommit会被拒绝,比如malloc一次性申请的内存大小就超过了系统总内存
  • 1 – Always overcommit. 允许overcommit,对内存申请来者不拒。
  • 2 – Don’t overcommit. 禁止overcommit。

3.oom killer 怎么挑选进程?

linux会为每个进程算一个分数,最终他会将分数最高的进程kill

/proc//oom_score_adj

  • 在计算最终的 badness score 时,会在计算结果是中加上 oom_score_adj,取值范围为-1000到1000
  • 如果将该值设置为-1000,则进程永远不会被杀死,因为此时 badness score 永远返回0。

/proc//oom_adj

  • 取值是-17到+15,取值越高,越容易被干掉。如果是-17,则表示不能被kill
  • 该设置参数的存在是为了和旧版本的内核兼容

/proc//oom_score

  • 这个值是系统综合进程的内存消耗量、CPU时间(utime + stime)、存活时间(uptime - start time)和oom_adj计算出的,消耗内存越多分越高,存活时间越长分越低

子进程内存:
Linux在计算进程的内存消耗的时候,会将子进程所耗内存的一半同时算到父进程中。这样,那些子进程比较多的进程就要小心了。
关闭 OOM killer

  • sysctl -w vm.overcommit_memory=2
  • echo “vm.overcommit_memory=2” >> /etc/sysctl.conf

3.1 找出最优可能被杀掉的进程

vi oomscore.sh

#!/bin/bash
for proc in $(find /proc -maxdepth 1 -regex '/proc/[0-9]+'); do
    printf "%2d %5d %s\n" \
        "$(cat $proc/oom_score)" \
        "$(basename $proc)" \
        "$(cat $proc/cmdline | tr '\0' ' ' | head -c 50)"
done 2>/dev/null | sort -nr | head -n 10

chmod +x oomscore.sh
./oomscore.sh

3.2 避免的oom killer的方案

  • 直接修改/proc/PID/oom_adj文件,将其置位-17
  • 修改/proc/sys/vm/lowmem_reserve_ratio
  • 直接关闭oom-killer

参考:

http://bhsc881114.github.io/2018/06/24/oom-killer%E7%90%86%E8%A7%A3%E5%92%8C%E6%97%A5%E5%BF%97%E5%88%86%E6%9E%90-%E6%97%A5%E5%BF%97%E5%88%86%E6%9E%90/

http://bhsc881114.github.io/2018/06/24/oom%20killer%E7%90%86%E8%A7%A3%E5%92%8C%E6%97%A5%E5%BF%97%E5%88%86%E6%9E%90:%E7%9F%A5%E8%AF%86%E5%82%A8%E5%A4%87/

https://blog.51cto.com/leejia/1952482

你可能感兴趣的:(记redis程序oom的排查过程)