如何處理HGDB被OOM killer kill的情況

1、文檔用途

本文適用于HGDB在Linux下被OOM killer kill的情況,如何進行排查及解決方案。

2、詳細信息

Linux內核管理使用OOM killer(Out Of Memory killer)機制,在系統(tǒng)內存不足時,選擇性殺死一些進程釋放內存,保證系統(tǒng)可以繼續(xù)運行。

OOM killer產生的原因

Linux內核根據(jù)應用程序的要求分配內存,應用程序分配到了內存,但通常情況下,不會全部用光,為了提高性能,這部分沒用的內存可以留作它用,這部分內存屬于所有進程,內核直接回收利用比較麻煩,所以內核采用了過度分配內存(over-commit memory)的方法間接利用這部分“空閑”內存,提高整體內存的使用效率。一般情況下,這樣做沒有問題,但當大多數(shù)應用程序都消耗完自己的內存的時候就會出現(xiàn)問題,因為這樣應用程序的內存需求加起來超過了物理內存(包括swap)的容量,內核必須殺掉一些進程,釋放一部分內存,保障系統(tǒng)正常運行。

OOM killer選擇哪個進程

OOM Killer通過檢查所有正在運行的進程,然后根據(jù)算法給每個進程一個badness分數(shù),擁有最高badness分數(shù)的進程將在內存不足時被殺掉。打分算法如下:

  • 某個進程和它的子進程占用了較多內存,會打一個高分。
  • 優(yōu)先選擇進程號最小的進程。
  • 內核進程和其他重要的進程得分較低。

OOM killer給每個進程打的分數(shù)都存放在/proc/{pid}/oom_score文件中。

與OOM killer相關的分數(shù)還有oom_adj和oom_score_adj,這兩個文件存放的是用戶對進程的打分。oom_adj已經過時,當前存在只是為了兼容舊版本的內核,oom_score_adj是新版官方建議使用的,這兩個參數(shù)任何一個變動,另一個也會跟隨變化,兩個數(shù)值是線性相關的。oom_adj取值范圍是-17+15,設置為-17時,禁止OOM killer殺掉對應進程。oom_score_adj的取值范圍是-1000+1000,設置為-1000時,禁止OOM killer殺掉對應進程。

查詢被OOM killer殺掉的進程

OOM killer殺掉進程后,會將相關日志輸出到操作系統(tǒng)日志中,可以直接查看操作系統(tǒng)日志(/var/log/messages),也可以通過dmesg命令獲取系統(tǒng)日志。查詢命令及查詢結果如下:

[root@registry ~]# dmesg | grep -i "killed process"

[2154261.752448] Killed process 31999 (postgres) total-vm:2356280kB, anon-rss:996kB, file-rss:776kB, shmem-rss:32612kB



#直接查看系統(tǒng)日志

[root@registry ~]# grep -i 'killed process' /var/log/messages

May  8 13:46:53 registry kernel: Killed process 31999 (postgres) total-vm:2356280kB, anon-rss:996kB, file-rss:776kB, shmem-rss:32612kB

如何查詢OOM killer對進程的評分

可以使用如下腳本查詢排名OOM killer對進程的打分,分數(shù)最高的,在內存不足的時候存在被OOM killer殺掉的風險。

#!/bin/bash

# Displays running processes in descending order of OOM score

printf 'PID\tOOM Score\tOOM Adj\tCommand\n'

(ps -e -o pid= -o comm=) |sort -k 2nr|while read -r pid comm; do

[ -f /proc/$pid/oom_score ] && [ $(cat /proc/$pid/oom_score) != 0 ] && printf '%d\t%d\t\t%d\t%s\n' "$pid" "$(cat /proc/$pid/oom_score)" "$(cat /proc/$pid/oom_score_adj)" "$comm"

done

如何防止進程被OOM killer殺掉

以下方式通過

修改內核參數(shù)

在Linux中,內核參數(shù)vm.overcommit_memory控制內存的申請策略,有三個值,如下:

0 – Heuristic overcommit handling. 這是缺省值,它允許overcommit,但過于明目張膽的overcommit會被拒絕,比如malloc一次性申請的內存大小就超過了系統(tǒng)總內存。Heuristic的意思是“試探式的”,內核利用某種算法猜測你的內存申請是否合理,它認為不合理就會拒絕overcommit。

1 – Always overcommit. 允許overcommit,對內存申請來者不拒。內核執(zhí)行無內存過量使用處理。使用這個設置會增大內存超載的可能性,但也可以增強大量使用內存任務的性能。

2 – Don’t overcommit. 禁止overcommit。 內存拒絕等于或者大于總可用 swap 大小以及 overcommit_ratio 指定的物理 RAM 比例的內存請求。

如果希望減小內存過度使用的風險,將overcommit_memory設置為2是最好的,此時單次申請的內存大小不能超過 [free memory + free swap + pagecache的大小 + SLAB中可回收的部分],否則就申請失敗。

怎樣才算是overcommit?kernel設有一個閾值,申請的內存總數(shù)超過這個閾值就算overcommit,在/proc/memifo中可以看到這個閾值的大?。?/p>

# grep -i commit /proc/meminfo 

CommitLimit:     3492144 kB

Committed_AS:    4054564 kB

CommitLimit就是overcommit的閾值,申請的內存總數(shù)超過CommitLimit就是overcommit。這個閾值通過內核參數(shù)vm.overcommit_ratio或vm.overcommit_kbytes間接設置,公式如下:

CommitLimit = (Physical RAM * vm.overcommit_ratio / 100) + Swap

注:vm.overcommit_ratio是內核參數(shù),缺省值是50,表示物理內存的50%。也可以通過內核參數(shù)vm.overcommit_kbytes直接設置字節(jié)數(shù)大小。如果使用了大頁內存,就需要從物理內存中減去,公式變成:CommitLimit = ([total RAM] – [total huge TLB RAM]) * vm.overcommit_ratio / 100 + swap

/proc/meminfo中的 Committed_AS 表示所有進程已經申請的內存總大小,(注意是已經申請的,不是已經分配的),如果 Committed_AS 超過 CommitLimit 就表示發(fā)生了 overcommit,超出越多表示 overcommit 越嚴重。

修改overcommit_memory的方法

1).以root身份登錄Linux,編輯/etc/sysctl.conf ,改vm.overcommit_memory=1,然后sysctl -p使配置文件生效

2).sysctl vm.overcommit_memory=1

3).echo 1 > /proc/sys/vm/overcommit_memory

增加swap

如果當前服務器內存不足,并且暫時無法增加內存,可以考慮增加swap交換區(qū),避免系統(tǒng)波動造成OOM killer殺掉進程。如果增加swap空間可以解決OOM的問題,則說明物理內存不足,需要考慮增加內存。

臨時增加swap的方法

dd if=/dev/zero of=swapadd bs=1024 count=5242880

mkswap swapadd

chmod   600 swapadd  -R

swapon swapadd

free -m

swapoff swapadd

修改進程的OOM分數(shù)值

可以直接修改進程的oom_adj的值,方式如下

pgrep -f "/opt/HighGo4.3.4.7-see/bin/postgres" | while read PID;do echo -17 > /proc/$PID/oom_adj;done

上面的方法僅能修改單個進程的分數(shù),因為其他進程均為主進程產生,通常情況下,保證主進程不被OOM killer殺掉就能確保數(shù)據(jù)庫程序完整。