MySQL OOM 系统二 OOM Killer

这里就涉及到一个问题,到底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个上面的进程。我们看一下运行结果:

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

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

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

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

我们运行一下下面的语句

echo 15 > /proc/12640/oom_adj

一段时间后,我们看到Swap空间急剧减少,基本上OS 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。

(0)

相关推荐

  • MySQL Slave 触发 oom-killer解决方法

    最近经常有收到MySQL实例类似内存不足的报警信息,登陆到服务器上一看发现MySQL 吃掉了99%的内存,God ! 有时候没有及时处理,内核就会自己帮我们重启下MySQL,然后我们就可以看到 dmesg 信息有如下记录: Mar 9 11:29:16 xxxxxx kernel: mysqld invoked oom-killer: gfp_mask=0x201da, order=0, oom_adj=0, oom_score_adj=0 Mar 9 11:29:16 xxxxxx kerne

  • Mysql使用kill命令解决死锁问题(杀死某条正在执行的sql语句)

    在使用mysql运行某些语句时,会因数据量太大而导致死锁,没有反映.这个时候,就需要kill掉某个正在消耗资源的query语句即可, KILL命令的语法格式如下: KILL [CONNECTION | QUERY] thread_id 每个与mysqld的连接都在一个独立的线程里运行,您可以使用SHOW PROCESSLIST语句查看哪些线程正在运行,并使用KILL thread_id语句终止一个线程. KILL允许自选的CONNECTION或QUERY修改符:KILL CONNECTION与不

  • MySQL OOM 系列三 摆脱MySQL被Kill的厄运

    前面两章,我们分析了Linux内存分配的策略以及Linux通过使用 OOM_Killer的机制解决了"超售"引起的风险,MySQL同其他的应用程序一样,在操作系统允许的范围内也是可以超售的,一般人理解,Innodb_buffer_pool必须小于实际物理内存,否则MySQL会启动失败.其实这是一个误区,这个不是MySQL层控制的,这个是操作系统(OS)层控制的,就是前面提到的/proc/sys/overcommit_memory控制OS是否允许"超售".如果允许&q

  • 批量 kill mysql 中运行时间长的sql

     KILL语法 KILL [CONNECTION | QUERY] thread_id 每个与mysqld的连接都在一个独立的线程里运行,您可以使用SHOW PROCESSLIST语句查看哪些线程正在运行,并使用KILL thread_id语句终止一个线程. KILL允许自选的CONNECTION或QUERY修改符: · KILL CONNECTION与不含修改符的KILL一样:它会终止与给定的thread_id有关的连接. · KILL QUERY会终止连接当前正在执行的语句,但是会保持连接的

  • percona-toolkit之pt-kill 杀掉mysql查询或连接的方法

    pt-kill 是一个非常简单的 杀mysql线程和查询的 工具. 主要是为了防止一些长的查询 长时间占用 系统资源,而对线上业务造成影响的情况. 主要作用: 从show processlist 中获取满足条件的连接或者从包含show processlist的文件中读取满足条件的连接并打印或者杀掉或者执行其他操作. 我们这里主要用来防止某些select操作时间过长,从而影响其他线上SQL. 安装: 安装percona-toolkit即可 使用范例: pt-kill --log-dsn D=tes

  • MySQL OOM 系统二 OOM Killer

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

  • mysql日志系统的简单使用教程

    前言 MySQL是一个关系型数据库管理系统,由瑞典MySQL AB 公司开发,属于 Oracle 旗下产品.MySQL 是最流行的关系型数据库管理系统之一,在 WEB 应用方面,MySQL是最好的 RDBMS (Relational Database Management System,关系数据库管理系统) 应用软件之一. 在任何一种数据库中,都会有各种各样的日志,记录着数据库工作的方方面面,以帮助数据库管理员追踪数据库曾 经发生过的各种事件. 在 MySQL 中,有 4 种不同的日志,分别是

  • PHP+MYSQL会员系统的登陆即权限判断实现代码

    复制代码 代码如下: <?php error_reporting(0); session_start(); //数据库连接 $conn = mysql_connect('localhost', 'root', ''); mysql_select_db('chenkun', $conn); mysql_query('SET NAMES UTF-8'); //定义常量 define('ALL_PS', 'PHP'); function user_shell($uid, $shell, $m_id)

  • PHP实现将MySQL重复ID二维数组重组为三维数组的方法

    本文实例讲述了PHP实现将MySQL重复ID二维数组重组为三维数组的方法.分享给大家供大家参考,具体如下: 应用场景 MYSQL在使用关联查询时,比如 产品表 与 产品图片表关联,一个产品多张产品图片,关联查询结果如下: $arr=[ ['id'=>1,'img'=>'img1'], ['id'=>1,'img'=>'img2'], ['id'=>1,'img'=>'img3'], ['id'=>2,'img'=>'img1'], ['id'=>2,'

  • MySQL系列之二 多实例配置

    什么是MySQL多实例? 简单地说,MySQL多实例就是在一台服务器上同时开启多个不同的服务端口(如:3306,3307),同时运行多个MySQL服务进程,这些服务进程通过不同的socket监听不同的服务端口来提供服务. 这些MySQL多实例共用一套MySQL安装程序,使用不同的my.cnf(也可以相同)配置文件.启动程序(也可以相同)和数据文件.在提供服务时,多实例MySQL在逻辑上看来是各自独立的,它们根据配置文件的对应设定值,获得服务器相应数量的硬件资源. 打个比方吧,MySQL多实例就相

  • mysql日志系统redo log和bin log介绍

    首先,我们先来看看一次查询/更新语句流程图 本文会将重点放在执行器<->存储引擎之间的交互. mysql不是每次数据更改都立刻写到磁盘,而是会先将修改后的结果暂存在内存中,当一段时间后,再一次性将多个修改写到磁盘上,减少磁盘io成本,同时提高操作速度. mysql通过WAL(write-ahead logging)技术保证事务: 在同一个事务中,每当数据库进行修改数据操作时,将修改结果更新到内存后,会在redo log添加一行记录记录“需要在哪个数据页上做什么修改”,并将该记录状态置为prep

  • PHP+MYSQL会员系统的开发实例教程

    本文通过一个简单的实例完成了完整的PHP+MySQL会员系统功能.是非常实用的一个应用.具体实现步骤如下: 一.会员系统的原理:     登陆-->判断-->保持状态(Cookie或Session)-->验证状态及其权限 二.会员系统的安全: 1.学会使用常量提高md5安全性 2.Cookie/ Session 少用明文信息 3.Session安全性要大于Cookie 4.使用Cookie/ Session读取信息 尽量增加判断信息 5.Cookie/ Session内容要精简 6.对于

  • MySQL日志系统详细资料分享

    做过大型系统的都知道,日志的作用不用小觑,往往到了项目中后期,对项目进行优化升级都是依据日志做出升级优化的决策的.那么学习MySQL,日志部分当然不能错过.我们面试中实际应用的所谈到的优化都是要从日志中得出来的.系统的学习mysql的日志,有助于我们准确的定位问题,提高自己的工作水平.此外,后面的一系列日志会重点从DBA的运维方面进行着手,系统的去理解MySQL各方面的配置,做到知己知彼,让MySQL成为自己得心应手的数据仓库. 一.MySQL的日志类型 默认情况下,所有的MySQL日志以文件的

  • PHP+MySQL投票系统的设计和实现分享

    系统不大,完成这个系统的过程我分了三个步骤 •数据库设计 •系统框架设计 •前端美化 数据库的设计 设计三张表:投票结果统计表(count_voting),投票人记录表(ip_votes),用户表(user) 投票结果统计表用于统计最后的投票记录,我给它弄了4个字段:被投票项的名称(SelectName),被投票项标签名(LabelName)(起到分类的作用),票数(CountVotes). 投票人记录表用于登记投票人的ip(IP),地理位置(Location),投票时间(VoteTime),被

  • MySql服务器系统变量和状态变量介绍

    服务器系统变量 服务器将维护许多表示其配置的系统变量.所有变量均有默认值.可以在命令行中或选项文件设置选项在服务器启动时对它们进行设置.大多数可以在运行时使用SET语句来设置. mysqld服务器维护两种变量.全局变量影响服务器的全局操作.会话变量影响具体客户端连接相关操作. 服务器启动时,将所有全局变量初始化为默认值.可以在选项文件或命令行中指定的选项来更改这些默认值.服务器启动后,通过连接服务器并执行SET GLOBAL var_name语句可以更改动态全局变量.要想更改全局变量,必须具有S

随机推荐