设为首页 加入收藏

TOP

swap空间不足导致MySQL被OOM kill案例
2015-11-13 01:24:08 来源: 作者: 【 】 浏览:6
Tags:swap 空间 不足 导致 MySQL OOM kill 案例

背景:


调查:



上图为oom kill后的top输出,因为该mysqld变为僵尸进程故一直没有释放内存。


mysql的BP设置为 106G,但是其RES分别达到125G和119G,加起来接近机器物理内存上限,而机器swap只有7G且被消耗完毕。


至此原因已经很清晰,解决方案也很简单,将BP调小90G。


注:自调整截至目前超过10天,没有再发生类似故障。


延伸


1 mysql内存开销


Innodb_buffer_pool_size定义了缓存池的大小,但是缓冲池本身需要额外的数据结构进行管理。


比如,缓冲池每个page都需要一个buf_block_t管理,这部分内存没有计入参数。


各种额外消耗加起来约占整个BP的8%,也有资料说是10%,具体可参看http://mysqlha.blogspot.co.uk/2008/11/innodb-memory-overhead.html。


这些只是global buffer的开销,加上session buffer,Mysql所需的内存只会更高。


2 为什么会发生swap


首先大致说一下linux的内存管理,numa架构下linux内存被分为多个node,非numa则只有1个,由pg_data_t描述,每个node又分为3个zone,由zone_struct结构体描述。


每个zone都有active_lru和 inactive_lru,每个lru又各分为anon匿名页和file cache映射页链表,总计4个LRU;


zone同时定义了pages_low,pages_min和pages_high,当zone可用内存小于pages_low时唤醒kswapd回收内存,而当其小于pages_min时则以同步方式唤醒kswapd,直到zone可用内存达到pages_high为止;


Linux会缓存很多数据,譬如page cache和slab cache,这部分内存在回收时会先同步到磁盘然后直接重用,而对于其他内存页,诸如用户态地址空间的匿名页,以及IPC共享内存区的页,只能将其置换到swap分区,不可直接回收。


OS何时回收内存?


1 定期回收:kswapd定期唤醒,当zone空闲内存小于pages_low则进行页面回收,小于pages_min则以同步方式回收;


2 直接回收:linux为用户进程分配内存或者创建缓冲区,而当前系统又没有足够多物理内存时,则linux会进行页面回收;当OS尝试内存回收后仍无法获取足够多的页面,则调用find_bad_process并进行OOM kill;



不管哪种回收方式,最后都调用shrink_list(),对4个链表的扫描逻辑定义在vmscan.c中的get_scan_count函数内,其变量scan_balance决定了要回收哪个lru的内存,大致逻辑如下:


1. 如果系统禁用了swap或者没有swap空间,则只扫描file based的链表,即不进行匿名页链表扫描


if (!sc->may_swap || (get_nr_swap_pages() <= 0)) {


scan_balance = SCAN_FILE;


goto out;


}


2. 如果当前进行的不是全局页回收,并且swappiness=0,则不进行匿名页链表扫描


if (!global_reclaim(sc) && !vmscan_swappiness(sc)) {


scan_balance = SCAN_FILE;


goto out;


}


3. 如果是全局页回收,并且空闲内存和file based链表page数目相加都小于zone->pages_high,则进行匿名页回收,即便swappiness=0,系统也会进行swap


if (global_reclaim(sc)) {


unsigned long zonefile;


unsigned long zonefree;


zonefree = zone_page_state(zone, NR_FREE_PAGES);


zonefile = zone_page_state(zone, NR_ACTIVE_FILE) +


zone_page_state(zone, NR_INACTIVE_FILE);


if (unlikely(zonefile + zonefree <= high_wmark_pages(zone))) {


scan_balance = SCAN_ANON;


goto out;


}


}


4. 如果系统inactive file链表比较充足,则不考虑进行匿名页的回收,即不进行swap


if (!inactive_file_is_low(lruvec)) {


scan_balance = SCAN_FILE;


goto out;


}


至此,我们可以大致了解swappiness=0的作用,其并不能完全禁止swap。


何时触发OOM kill?


当系统内存被消耗殆尽,swap分区也被填满的时候,内核无法分配到新的空闲内存,便会启动OOM删除程序;


其内核调用路径为out_of_memory() – select_bad_process() – oom_kill_process()


其中select_bad_process()负责挑选待杀死的进程,其扫描系统中的每一个进程并调用oom_badness(),该API逻辑如下:


1 获取进程的oom_score_adj,如果其等于OOM_SCORE_ADJ_MIN即-1000则不Kill,该参数由/proc/NNN/ oom_score_adj记录,可手工修改


adj = (long)p->signal->oom_score_adj;


if (adj == OOM_SCORE_ADJ_MIN) {


task_unlock(p);


return 0;


}


2 根据该进程消耗的内存计算分数,如果是root进程则乘以3%,尽量避免其被Kill,最后将points返回


points = get_mm_rss(p->mm) + get_mm_counter(p->mm, MM_SWAPENTS) +


atomic_long_read(&p->mm->nr_ptes) + mm_nr_pmds(p->mm);


task_unlock(p);


if (has_capability_noaudit(p, CAP_SYS_ADMIN))


points -= (points * 3) / 100;


select_bad_process()通过比较每一个进程的oom_badness()返回值,找出得分最高且不是线程组leader的进程,将其返回给out_of_memory(),由其调用oom_kill_process()发送sigkill信号进行扑杀。


除了杀死进程,Linux可以选择在发生OOM时直接panic,当vm.panic_on_oom=1时成立


结束语


至此我们可以大致了解swappiness=0的意义,以及OOM kill发生的原因,为避免此行为应尽量留出充足的内存给OS,一般应为物理内存的20%左右。


】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇排序sql升级MySQL 5.6变慢原因分析 下一篇MySQL教程:关于 unauthenticated..

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容: