最近有位 VPS 客戶抱怨 MySQL 無緣無故挂掉,還有位客戶抱怨 VPS 經常當機,登陸到終端看了一下,都是常見的 Out of memory 問題。這通常是因為某時刻應用程式大量請求記憶體導緻系統記憶體不足造成的,這通常會觸發 Linux 核心裡的 Out of Memory (OOM) killer,OOM killer 會殺掉某個程序以騰出記憶體留給系統用,不緻于讓系統立刻崩潰。如果檢查相關的日志檔案(/var/log/messages)就會看到下面類似的 Out of memory: Kill process 資訊:
Linux 核心根據應用程式的要求配置設定記憶體,通常來說應用程式配置設定了記憶體但是并沒有實際全部使用,為了提高性能,這部分沒用的記憶體可以留作它用,這部分記憶體是屬于每個程序的,核心直接回收利用的話比較麻煩,是以核心采用一種過度配置設定記憶體(over-commit memory)的辦法來間接利用這部分 “空閑” 的記憶體,提高整體記憶體的使用效率。一般來說這樣做沒有問題,但當大多數應用程式都消耗完自己的記憶體的時候麻煩就來了,因為這些應用程式的記憶體需求加起來超出了實體記憶體(包括 swap)的容量,核心(OOM killer)必須殺掉一些程序才能騰出空間保障系統正常運作。用銀行的例子來講可能更容易懂一些,部分人取錢的時候銀行不怕,銀行有足夠的存款應付,當全國人民(或者絕大多數)都取錢而且每個人都想把自己錢取完的時候銀行的麻煩就來了,銀行實際上是沒有這麼多錢給大家取的。
我們可以通過一些核心參數來調整 OOM killer 的行為,避免系統在那裡不停的殺程序。比如我們可以在觸發 OOM 後立刻觸發 kernel panic,kernel panic 10秒後自動重新開機系統。
從上面的 oom_kill.c 代碼裡可以看到 oom_badness() 給每個程序打分,根據 points 的高低來決定殺哪個程序,這個 points 可以根據 adj 調節,root 權限的程序通常被認為很重要,不應該被輕易殺掉,是以打分的時候可以得到 3% 的優惠(adj -= 30; 分數越低越不容易被殺掉)。我們可以在使用者空間通過操作每個程序的 oom_adj 核心參數來決定哪些程序不這麼容易被 OOM killer 選中殺掉。比如,如果不想 MySQL 程序被輕易殺掉的話可以找到 MySQL 運作的程序号後,調整 oom_score_adj 為 -15(注意 points 越小越不容易被殺):
當然,如果需要的話可以完全關閉 OOM killer(不推薦用在生産環境):
我們知道了在使用者空間可以通過操作每個程序的 oom_adj 核心參數來調整程序的分數,這個分數也可以通過 oom_score 這個核心參數看到,比如檢視程序号為981的 omm_score,這個分數被上面提到的 omm_score_adj 參數調整後(-15),就變成了3:
下面這個 bash 腳本可用來列印目前系統上 oom_score 分數最高(最容易被 OOM Killer 殺掉)的程序: