MySQL OOM 系统二 OOM Killer

 更新时间:2016年07月16日 23:05:56   投稿:mdxy-dxy  
前面一节重点分享了Linux的内存分配策略,基于上述的分配策略,为了规避超售的风险,Linux采了一种OOM Killer的机制,即系统可用内存(包括Swap)即将使用完之前,选择性的Kill掉一些进程以求释放一些内存

这里就涉及到一个问题,到底Kill掉谁呢?一般稍微了解一些Linux内核的同学第一反应是谁用的最多,就Kill掉谁。这当然是Linux内核首先考虑的一种重要因素,但是也不完全是这样的,我们查一些Linux的内核方面的资料,可以知道其实Kill谁是由/proc/<pid>/oom_score来决定的,这个值每个进程一个,是由Linux内核的oom_badness()函数负责计算的。那下面我们来仔细读一读badness()函数。  

在badness()函数的注释部分,写明了badness()函数的处理思路:

         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)

总的来说就是Kill掉最小数量的进程来获取最大数量的内存,这与我们Kill掉占用内存最大的进程是吻合的。

        /*
         * The memory size of the process is the basis for the badness.
         */

         points = p->mm->total_vm;

分数的起始是进程实际使用的RAM内存,注意这里不包括SWAP,即OOM Killer只会与进程实际的物理内存有关,与Swap是没有关系的,并且我们可以看到,进程实际使用的物理内存越多,分数就越高,分数越高就越容易被牺牲掉。

        /*
         * 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;

这段表示子进程占用的内存都会计算到父进程上。

        s = int_sqrt(cpu_time);
        if (s)
                points /= s;
        s = int_sqrt(int_sqrt(run_time));
        if (s)
                points /= s;

 这表明进程占用的CPU时间越长或者进程运行的时间越长,分数越低,越不容易被Kill掉。

       /*
        * Niced processes are most likely less important, so double
        * their badness points.
        */
        if (task_nice(p) > 0)
                points *= 2;

          如果进程优先级低(nice值,正值低优先级,负值高优先级),则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用户的进程优先级较低。

        /*
         * 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;

          直接可以访问原始设备的进程优先级较高。

        /*
         * Adjust the score by oomkilladj.
         */
        if (p->oomkilladj) {
                if (p->oomkilladj > 0)
                        points <<= p->oomkilladj;
                else
                        points >>= -(p->oomkilladj);

        }

每个进程有个oomkilladj 可以设置该进程被kill的优先级,这个参数看上去对Point影响还是比较大的,oomkilladj 最大+15,最小是-17,越大越容易被干掉,这个值由于是移位运算,所以影响还是比较大的。

下面我写个小程序实验一下:

 #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);
 }

上面的程序先申请一个1G的内存空间,然后100M为单位,填充这些内存空间。在一个2G内存,400M Swap空间的机器上跑3个上面的进程。我们看一下运行结果:

   MySQL OOM 系统二 OOM Killer - 网易杭研后台技术中心 - 网易杭研后台技术中心的博客

     test1、test2、test3分别申请了1G的虚拟内存空间(VIRT),然后每隔10s,实际占用的RAM空间就增长100M(RES)。

    MySQL OOM 系统二 OOM Killer - 网易杭研后台技术中心 - 网易杭研后台技术中心的博客

    当物理内存空间不足时,OS开始进行Swap,可用的Swap空间开始减少。

   MySQL OOM 系统二 OOM Killer - 网易杭研后台技术中心 - 网易杭研后台技术中心的博客
    

     当内存是在没有可分配的空间时,test1进程被操作系统Kill掉了。dmesg 我们可以看到,test1进程被OS Kill掉,同时oom_score为1000。

     这3个进程的oom_adj全部都是默认值0。下面我们来实验一下设置了oom_adj的效果。重新启动3个进程,然后我们看到test2的PID是12640

   MySQL OOM 系统二 OOM Killer - 网易杭研后台技术中心 - 网易杭研后台技术中心的博客    

   我们运行一下下面的语句

   echo 15 > /proc/12640/oom_adj

  一段时间后,我们看到Swap空间急剧减少,基本上OS OOM_Killer要开动了。 

   MySQL OOM 系统二 OOM Killer - 网易杭研后台技术中心 - 网易杭研后台技术中心的博客

     果然,不出意料,12640进程被kill掉了。

     所以为了避免自己需要的进程被kill掉,可以通过设置进程的oom_adj来实现。当然,有的人会说,这一切都是超售引起的,既然Linux提供了overcommit_memory可以禁用overcommit特性,那为什么不禁用呢。这有利也有弊,一旦禁用overcommit,就意味着MySQL根本无法申请超过实际内存的空间,而在MySQL中,存在很多动态申请内存空间的地方,如果申请不到,MySQL就会Crash,这大大增加了MySQL宕机的风险,这也是Linux为什么要overcommit的原因。

      有了上面的分析,我们不难看出,如果在不设置oom_adj的前提下,MySQL一般都会成为OOM_Killer的首选对象,因为MySQL一般都是内存的最大占用者。那作为MySQL,我们如何尽量的去规避被Kill的风险呢,下一章我们将重点从MySQL的角度分析如何规避OOM。

相关文章

  • MYSQL数据库基础之Join操作原理

    MYSQL数据库基础之Join操作原理

    这篇文章主要给大家介绍了关于MYSQL数据库基础之Join操作原理的相关资料,连接(join)查询是将两个查询的结果以“横向对接”的方式合并起来的结果,需要的朋友可以参考下
    2021-07-07
  • mysql 查询重复的数据的SQL优化方案

    mysql 查询重复的数据的SQL优化方案

    这篇文章主要介绍了mysql 查询重复的数据的SQL优化方案,非常不错的方案推荐给大家。
    2015-02-02
  • Mysql中group by 使用中发现的问题

    Mysql中group by 使用中发现的问题

    当使用MySQL的GROUP BY语句时,根据指定的列对结果进行分组,这种情况通常是由于在 GROUP BY 中选择的字段与其他非聚合字段不兼容,或者在 SELECT 子句中没有正确使用聚合函数所导致的,本文给大家介绍Mysql中group by 使用中发现的问题,感兴趣的朋友跟随小编一起看看吧
    2024-06-06
  • MySQL中select...for update锁表

    MySQL中select...for update锁表

    select…for update在MySQL中,是一种悲观锁的用法,一般情况下,会锁住一行数据,但如果没有使用正确的话,也会把整张表锁住,本文就来介绍一下,感兴趣的可以了解一下
    2023-10-10
  • 初学者从源码理解MySQL死锁问题

    初学者从源码理解MySQL死锁问题

    这篇文章主要讲的是如何通过调试 MySQL 源码,知道一条 SQL 真正会拿哪些锁,不再抓虾,瞎猜或者何登成大神没写过的场景就不知道如何处理了,下面小编来和大家一起学习学习
    2019-05-05
  • mysql中Update未加索引导致的微服务模块不可用

    mysql中Update未加索引导致的微服务模块不可用

    本文主要介绍了mysql中Update未加索引导致的微服务模块不可用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-06-06
  • 通过Query Profiler查看MySQL语句运行时间的操作方法

    通过Query Profiler查看MySQL语句运行时间的操作方法

    这篇文章主要介绍了通过Query Profiler查看MySQL语句运行时间的操作方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-03-03
  • MySQL索引下推index condition pushdown

    MySQL索引下推index condition pushdown

    索引下推是MySQL 5.6版本引入的一种数据库查询优化技术,本文主要介绍了MySQL索引下推index condition pushdown,具有一定的参考价值,感兴趣的可以了解一下
    2024-08-08
  • MySQL全局锁和表锁的深入理解

    MySQL全局锁和表锁的深入理解

    这篇文章主要给大家介绍了关于MySQL全局锁和表锁的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用MySQL具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2019-06-06
  • mysql 8.0.20 winx64.zip压缩版安装配置方法图文教程

    mysql 8.0.20 winx64.zip压缩版安装配置方法图文教程

    这篇文章主要为大家详细介绍了mysql 8.0.20 winx64.zip压缩版安装配置方法图文教程,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-05-05

最新评论