MySQL OOM 系統(tǒng)二 OOM Killer
這里就涉及到一個(gè)問(wèn)題,到底Kill掉誰(shuí)呢?一般稍微了解一些Linux內(nèi)核的同學(xué)第一反應(yīng)是誰(shuí)用的最多,就Kill掉誰(shuí)。這當(dāng)然是Linux內(nèi)核首先考慮的一種重要因素,但是也不完全是這樣的,我們查一些Linux的內(nèi)核方面的資料,可以知道其實(shí)Kill誰(shuí)是由/proc/<pid>/oom_score來(lái)決定的,這個(gè)值每個(gè)進(jìn)程一個(gè),是由Linux內(nèi)核的oom_badness()函數(shù)負(fù)責(zé)計(jì)算的。那下面我們來(lái)仔細(xì)讀一讀badness()函數(shù)。
在badness()函數(shù)的注釋部分,寫明了badness()函數(shù)的處理思路:
1) we lose the minimum amount of work done
2) we recover a large amount of memory
3) we don't kill anything innocent of eating tons of memory
4) we want to kill the minimum amount of processes (one)
5) we try to kill the process the user expects us to kill, this algorithm has been meticulously tuned to meet the principle of least surprise ... (be careful when you change it)
總的來(lái)說(shuō)就是Kill掉最小數(shù)量的進(jìn)程來(lái)獲取最大數(shù)量的內(nèi)存,這與我們Kill掉占用內(nèi)存最大的進(jìn)程是吻合的。
/*
* The memory size of the process is the basis for the badness.
*/
points = p->mm->total_vm;
分?jǐn)?shù)的起始是進(jìn)程實(shí)際使用的RAM內(nèi)存,注意這里不包括SWAP,即OOM Killer只會(huì)與進(jìn)程實(shí)際的物理內(nèi)存有關(guān),與Swap是沒有關(guān)系的,并且我們可以看到,進(jìn)程實(shí)際使用的物理內(nèi)存越多,分?jǐn)?shù)就越高,分?jǐn)?shù)越高就越容易被犧牲掉。
/*
* Processes which fork a lot of child processes are likely
* a good choice. We add the vmsize of the childs if they
* have an own mm. This prevents forking servers to flood the
* machine with an endless amount of childs
*/
...
if (chld->mm != p->mm && chld->mm)
points += chld->mm->total_vm;
這段表示子進(jìn)程占用的內(nèi)存都會(huì)計(jì)算到父進(jìn)程上。
s = int_sqrt(cpu_time);
if (s)
points /= s;
s = int_sqrt(int_sqrt(run_time));
if (s)
points /= s;
這表明進(jìn)程占用的CPU時(shí)間越長(zhǎng)或者進(jìn)程運(yùn)行的時(shí)間越長(zhǎng),分?jǐn)?shù)越低,越不容易被Kill掉。
/*
* Niced processes are most likely less important, so double
* their badness points.
*/
if (task_nice(p) > 0)
points *= 2;
如果進(jìn)程優(yōu)先級(jí)低(nice值,正值低優(yōu)先級(jí),負(fù)值高優(yōu)先級(jí)),則Point翻倍。
/*
* Superuser processes are usually more important, so we make it
* less likely that we kill those.
*/
if (cap_t(p->cap_effective) & CAP_TO_MASK(CAP_SYS_ADMIN) ||
p->uid == 0 || p->euid == 0)
points /= 4;
super用戶的進(jìn)程優(yōu)先級(jí)較低。
/*
* We don't want to kill a process with direct hardware access.
* Not only could that mess up the hardware, but usually users
* tend to only have this flag set on applications they think
* of as important.
*/
if (cap_t(p->cap_effective) & CAP_TO_MASK(CAP_SYS_RAWIO))
points /= 4;
直接可以訪問(wèn)原始設(shè)備的進(jìn)程優(yōu)先級(jí)較高。
/*
* Adjust the score by oomkilladj.
*/
if (p->oomkilladj) {
if (p->oomkilladj > 0)
points <<= p->oomkilladj;
else
points >>= -(p->oomkilladj);
}
每個(gè)進(jìn)程有個(gè)oomkilladj 可以設(shè)置該進(jìn)程被kill的優(yōu)先級(jí),這個(gè)參數(shù)看上去對(duì)Point影響還是比較大的,oomkilladj 最大+15,最小是-17,越大越容易被干掉,這個(gè)值由于是移位運(yùn)算,所以影響還是比較大的。
下面我寫個(gè)小程序?qū)嶒?yàn)一下:
#define MEGABYTE 1024*1024*1024
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
void *myblock = NULL;
myblock = (void *) malloc(MEGABYTE);
printf("Currently allocating 1GB\n");
sleep(1);
int count = 0;
while( count < 10)
{
memset(myblock,1,100*1024*1024);
myblock = myblock + 100*1024*1024;
count++;
printf("Currently allocating %d00 MB\n",count);
sleep(10);
}
exit(0);
}
上面的程序先申請(qǐng)一個(gè)1G的內(nèi)存空間,然后100M為單位,填充這些內(nèi)存空間。在一個(gè)2G內(nèi)存,400M Swap空間的機(jī)器上跑3個(gè)上面的進(jìn)程。我們看一下運(yùn)行結(jié)果:

test1、test2、test3分別申請(qǐng)了1G的虛擬內(nèi)存空間(VIRT),然后每隔10s,實(shí)際占用的RAM空間就增長(zhǎng)100M(RES)。

當(dāng)物理內(nèi)存空間不足時(shí),OS開始進(jìn)行Swap,可用的Swap空間開始減少。

當(dāng)內(nèi)存是在沒有可分配的空間時(shí),test1進(jìn)程被操作系統(tǒng)Kill掉了。dmesg 我們可以看到,test1進(jìn)程被OS Kill掉,同時(shí)oom_score為1000。
這3個(gè)進(jìn)程的oom_adj全部都是默認(rèn)值0。下面我們來(lái)實(shí)驗(yàn)一下設(shè)置了oom_adj的效果。重新啟動(dòng)3個(gè)進(jìn)程,然后我們看到test2的PID是12640
我們運(yùn)行一下下面的語(yǔ)句
echo 15 > /proc/12640/oom_adj
一段時(shí)間后,我們看到Swap空間急劇減少,基本上OS OOM_Killer要開動(dòng)了。

果然,不出意料,12640進(jìn)程被kill掉了。
所以為了避免自己需要的進(jìn)程被kill掉,可以通過(guò)設(shè)置進(jìn)程的oom_adj來(lái)實(shí)現(xiàn)。當(dāng)然,有的人會(huì)說(shuō),這一切都是超售引起的,既然Linux提供了overcommit_memory可以禁用overcommit特性,那為什么不禁用呢。這有利也有弊,一旦禁用overcommit,就意味著MySQL根本無(wú)法申請(qǐng)超過(guò)實(shí)際內(nèi)存的空間,而在MySQL中,存在很多動(dòng)態(tài)申請(qǐng)內(nèi)存空間的地方,如果申請(qǐng)不到,MySQL就會(huì)Crash,這大大增加了MySQL宕機(jī)的風(fēng)險(xiǎn),這也是Linux為什么要overcommit的原因。
有了上面的分析,我們不難看出,如果在不設(shè)置oom_adj的前提下,MySQL一般都會(huì)成為OOM_Killer的首選對(duì)象,因?yàn)镸ySQL一般都是內(nèi)存的最大占用者。那作為MySQL,我們?nèi)绾伪M量的去規(guī)避被Kill的風(fēng)險(xiǎn)呢,下一章我們將重點(diǎn)從MySQL的角度分析如何規(guī)避OOM。
相關(guān)文章
mysql中tonumber函數(shù)使用及注意事項(xiàng)
在MySQL中,沒有直接的TO_NUMBER函數(shù),但可以通過(guò)CAST或CONVERT實(shí)現(xiàn)字符串到數(shù)字的轉(zhuǎn)換,轉(zhuǎn)換前需明確數(shù)據(jù)類型,了解轉(zhuǎn)換語(yǔ)法,并注意錯(cuò)誤處理、空值處理、格式合規(guī)性和精度問(wèn)題,本文介紹mysql中tonumber函數(shù)使用及注意事項(xiàng),感興趣的朋友一起看看吧2025-02-02
mysql添加索引方法詳解(Navicat可視化加索引與sql語(yǔ)句加索引)
索引用來(lái)快速地尋找那些具有特定值的記錄,如果沒有索引,執(zhí)行查詢時(shí)MySQL必須從第一個(gè)記錄開始掃描整個(gè)表的所有記錄,直至找到符合要求的記錄,表里面的記錄數(shù)量越多,代價(jià)就越高,下面這篇文章主要給大家介紹了關(guān)于mysql添加索引的相關(guān)資料,需要的朋友可以參考下2022-11-11
mysql創(chuàng)建表設(shè)置表主鍵id從1開始自增的解決方案
在MySQL中用很多類型的自增ID,每個(gè)自增ID都設(shè)置了初始值,一般情況下初始值都是從0開始,然后按照一定的步長(zhǎng)增加(一般是自增 1),下面這篇文章主要給大家介紹了關(guān)于mysql創(chuàng)建表設(shè)置表主鍵id從1開始自增的解決方案,需要的朋友可以參考下2023-04-04
MySQL limit使用方法以及超大分頁(yè)問(wèn)題解決
這篇文章主要給大家介紹了關(guān)于MySQL limit使用方法以及超大分頁(yè)問(wèn)題解決的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者使用MySQL具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-10-10
MySQL 四種事務(wù)隔離級(jí)別詳解及對(duì)比
這篇文章主要介紹了MySQL 四種事務(wù)隔離級(jí)別詳解及對(duì)比的相關(guān)資料,這里對(duì)Mysql 的基礎(chǔ)知識(shí)做了詳細(xì)介紹及對(duì)其事務(wù)隔離進(jìn)行了比較,需要的朋友可以參考下2016-12-12
MySQL實(shí)現(xiàn)統(tǒng)計(jì)過(guò)去12個(gè)月每個(gè)月的數(shù)據(jù)信息
這篇文章主要介紹了MySQL實(shí)現(xiàn)統(tǒng)計(jì)過(guò)去12個(gè)月每個(gè)月的數(shù)據(jù)信息,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-12-12
mysql 5.7.13 winx64安裝配置方法圖文教程(win10)
這篇文章主要為大家分享了mysql 5.7.13 winx64安裝配置方法圖文教程,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-01-01
教你如何恢復(fù)使用MEB備份的MySQL數(shù)據(jù)庫(kù)
這篇文章主要介紹了教你如何恢復(fù)使用MEB備份的MySQL數(shù)據(jù)庫(kù)的具體方法,需要的朋友可以參考下2016-09-09

