在Linux系统中部署MySQL服务,经常会遇到MySQL因为OOM(out of memory)的原因被操作系统Kill,导致数据库服务异常。要分析该问题,首先我们需要了解一下Linux内存分配的基本策略。
一、Linux内存分配策略
一般写C语言程序,我们习惯使用malloc动态的申请内存空间(Java由JVM负责内存管理),malloc函数会向操作系统申请一段连续的内存单元,然后返回这段空间的起始地址。如果malloc函数返回为null则表示系统没有可分配的内存空间。这是我们的一般思维,当然这在某些操作系统中确实也是正确的(Solaris)。
但是Linux不是这样的,Linux的内存分配采取的是一种更加积极的分配策略,它假设应用申请了内存空间后并不会立即去使用它,所以允许一定量的超售,当应用真的需要使用它的时候,操作系统可能已经通过回收了其他应用的内存空间而变得有能力去满足这个应用的需求,简单的说,就是允许应用申请比实际可分配空间(包括物理内存和Swap)更多的内存,这个特性称为OverCommit。
这个特性在Linux操作系统里面也是可配的,可以通过设置/proc/sys/overcommit_memory为不同的值来调整OverCommit策略。overcommit_memory可以取3个值:
0:默认值,由Linux内核通过一些启发式算法来决定是否超售和超售的大小,一般允许轻微的超售,拒绝一些明显不可能提供的请求,同时做一些规则限制,比如不同用户overcommit的大小也不一样。
1:允许,不做限制的超售,当然这个也不是无限大,还受到寻址空间的限制,32位系统最大可能只有4G,64位系统大概16T左右。
2:禁止,禁止超售,系统能够分配的内存不会超过swap+实际物理内存*overcommit_ratio,该值可以通过/proc/sys/vm/overcommit_ratio设置,默认50%。
我们在一个1G内存,400M Swap的虚拟机上进行一个小测试,实验一是一个程序不停通过malloc申请空间,实验二是程序在malloc后,即用1填充该空间,对比两个程序申请到的空间大小。
图1
图2
我们观测到实验二(图2)申请的空间与实际物理空间大体一致,而实验一(图1)却申请到远远大于实际物理空间的虚拟空间,这也验证了Linux的超售的内存分配策略。本身这是一个系统的优化,无可厚非。但是我们知道,但凡“超售”都是基于不会有大量程序同时使用资源的假设,这显然也是有风险的。所以Linux又使用了一种OOM Killer(Out Of Memory killer)的机制,在系统可用内存(包括Swap)即将使用完之前,选择性的Kill掉一些进程以求释放一些内存。
二、Linux OOM Killer工作原理
这里就涉及到一个问题,到底Kill掉谁呢?一般稍微了解一些Linux内核的同学第一反应是谁用的最多,就Kill掉谁。这当然是Linux内核首先考虑的一种重要因素,但是也不完全是这样的,我们查一些Linux的内核方面的资料,可以知道其实Kill谁是由/proc//oom_score来决定的,这个值每个进程一个,是由Linux内核的oom_badness()函数负责计算的。那下面我们来仔细读一读badness()函数。
在badness()函数的注释部分,写明了badness()函数的处理思路:
1) we lose the minimum amount of work