vm.swappiness=0在最新内核中可能会OOM,MySQL被意外kill

 

转载自:http://www.woqutech.com/?p=1397

 

请使用RHEL/CentOS 6.4及更新版本内核的MySQL同志们注意,vm.swappiness = 0的默认行为修改了,如果继续设置vm.swappiness = 0,有可能导致系统内存溢出,从而导致MySQL被意外kill掉。

 

在之前的《LINUX上MYSQL优化三板斧》中,我们建议大家把 vm.swappiness = 0 设置好。来尽量避免MySQL的服务器内存被交换出去。这样Linux在把内存交换出去时更偏向于将cache页交换出去,而不是将inactive页交换出去。详细描述请参考:http://www.woqutech.com/?p=1200

 

经常有人会问, vm.swappiness = 0会不会导致Linux在有swap空间的时候也不交换出去,从而导致内存溢出(OOM)。参照《LINUX上MYSQL优化三板斧》介绍,我们知道,这个值只是一个Linux在判断是否交换内存(swap)的一个“倾向”参考值,而并不是说,设置为0以后,Linux就完全不会使用内存交换空间。

 

但是,在较新的内核中(2.6.32-303.el6及以后),vm.swappiness = 0 的默认行为修改掉了,这个说法不再成立。设置该参数vm.swappiess=0,有可能导致MySQL数据库所在的系统出现内存溢出。一般来说,MySQL数据库占用的内存是整个服务器中最大的,根据Linux的策略,它会会首先把MySQL给Kill掉(调整 /proc/(pidof -s mysqld)/oom_adj可以改变OOM时kill的优先级),从而导致应用故障等。

 

这个修改是在内核3.5-rc1中提交的,并且合并到了2.6.32-303.el6及之后的各个版本。先让我们来看看这个patch:

http://gitorious.ti.com/ti-linux-kernel/ti-linux-kernel/commit/fe35004fbf9eaf67482b074a2e032abb9c89b1dd?format=patch

 

From fe35004fbf9eaf67482b074a2e032abb9c89b1dd Mon Sep 17 00:00:00 2001
From: Satoru Moriya <satoru.moriya@hds.com>
Date: Tue, 29 May 2012 15:06:47 -0700
Subject: [PATCH] mm: avoid swapping out with swappiness==0
Sometimes we'd like to avoid swapping out anonymous memory.  In
particular, avoid swapping out pages of important process or process
groups while there is a reasonable amount of pagecache on RAM so that we
can satisfy our customers' requirements.
OTOH, we can control how aggressive the kernel will swap memory pages with
/proc/sys/vm/swappiness for global and
/sys/fs/cgroup/memory/memory.swappiness for each memcg.
But with current reclaim implementation, the kernel may swap out even if
we set swappiness=0 and there is pagecache in RAM.
This patch changes the behavior with swappiness==0.  If we set
swappiness==0, the kernel does not swap out completely (for global reclaim
until the amount of free pages and filebacked pages in a zone has been
reduced to something very very small (nr_free + nr_filebacked < high
watermark)).
Signed-off-by: Satoru Moriya <satoru.moriya@hds.com>
Acked-by: Minchan Kim <minchan@kernel.org>
Reviewed-by: Rik van Riel <riel@redhat.com>
Acked-by: Jerome Marchand <jmarchan@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
---
mm/vmscan.c |    6 +++---
1 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/mm/vmscan.c b/mm/vmscan.c
index 67a4fd4..ee97530 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -1761,10 +1761,10 @@ static void get_scan_count(struct mem_cgroup_zone *mz, struct scan_control *sc,
   * proportional to the fraction of recently scanned pages on
   * each list that were recently referenced and in active use.
   */
-   ap = (anon_prio + 1) * (reclaim_stat->recent_scanned[0] + 1);
+   ap = anon_prio * (reclaim_stat->recent_scanned[0] + 1);
   ap /= reclaim_stat->recent_rotated[0] + 1;
-   fp = (file_prio + 1) * (reclaim_stat->recent_scanned[1] + 1);
+   fp = file_prio * (reclaim_stat->recent_scanned[1] + 1);
   fp /= reclaim_stat->recent_rotated[1] + 1;
   spin_unlock_irq(&mz->zone->lru_lock);
@@ -1777,7 +1777,7 @@ out:
      unsigned long scan;
      scan = zone_nr_lru_pages(mz, lru);
-      if (priority || noswap) {
+      if (priority || noswap || !vmscan_swappiness(mz, sc)) {
          scan >>= priority;
          if (!scan && force_scan)
             scan = SWAP_CLUSTER_MAX;
--
1.7.5

 

就像Satoru Moriya所说的那样,在之前的版本中,就算我们设置了swappiness=0并且RAM中还有pagecache,内核也可能会交换出部分匿名内存页。而为了“满足用户的需求”,这个patch修改了swappiness=0的行为,如果你设置swappiness=0,那么只有在(nr_free + nr_filebacked < high watermark)才会交换内存,也就是说空闲内存和文件缓存基本没有了才会触发内存swap。这样的话,副作用在于:内存如果不够了,Linux有可能触发OOM,从而kill掉耗费内存最多的MySQL进程。

 

在2.6.32-303.el6 RHEL/CentOS及更新版本的内核中,该patch就已经被合并进来:

 

* Mon Aug 27 2012 Jarod Wilson <jarod@redhat.com> [2.6.32-303.el6]
...
- [mm] avoid swapping out with swappiness==0 (Satoru Moriya) [787885]

 

其他分发版本的Linux(比如Debian,Ubuntu)的版本中,请各位自己查阅一下,看看时候已经合并该patch。

 

RHEL/CentOS 6.3的内核版本是2.6.32-279,而RHEL/CentOS 6.4的内核为2.6.32-358,从这个版本开始,swappiness的行为就已经修改了,使用这个版本及之后版本的同志们需要特别注意一下。

 

解决的办法其实也很简单,

1、尽量保证Linux操作系统还有足够的内存

2、最新的内核,建议把vm.swappiness设置1

3、考虑设置 /proc/(pidof -s mysqld)/oom_adj为较小的值来尽量避免MySQL由于内存不足而被关闭。

 

参考:

https://github.com/torvalds/linux/blob/master/mm/vmscan.c

http://gitorious.ti.com/ti-linux-kernel/ti-linux-kernel/commit/fe35004fbf9eaf67482b074a2e032abb9c89b1dd?format=patch

http://www.mysqlperformanceblog.com/2014/04/28/oom-relation-vm-swappiness0-new-kernel/