Memory Overcommit

redis を起動すると、以下の2行目のように「RAMが少ない場合は vm.overcommit_memory = 1 にするように!」という警告メッセージが表示される。

$ ./redis-server
[9703] 19 Feb 01:02:07 * Server started, Redis version 2.4.7
[9703] 19 Feb 01:02:07 # WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect.

ピンと来なかったので overcommit_memory について調べた。

overcommit_memory

Linux ではプロセスがメモリを確保(malloc)した際に

  • プロセスはすぐにはメモリを使わない
  • プロセスは確保したメモリを使いきらない

という前提のもと、空きメモリより多くのメモリを確保する仕組みがある。
これを オーバーコミットという。

楽観的にメモリをガツガツ確保し、本当にメモリを使いきってしまうと、カーネルは計算にもどづいて特定のプロセスを kill してメモリを確保する(いわゆる OOM-killer/out of memory killer)。

Kernel 2.6 系では次の3モードでオーバーコミットを制御できる

0[OVERCOMMIT_GUESS]: heuristic に基づき、overcommit できたり、できなかったりする(デフォルト)
1[OVERCOMMIT_ALWAYS]: 常に overcommit
2[OVERCOMMIT_NEVER]: overcommit しない。 [swap size] + [RAM size] * overcommit_ratio / 100 の計算式の範囲でしかメモリ確保できない。なお overcommit_ratio のデフォルト値は 50。

http://www.kernel.org/doc/Documentation/vm/overcommit-accounting

なお RHEL 6 のマニュアルでは次のようにアドバイスされている。

This setting is only recommended for systems with swap areas larger than physical memory.

overcommit_memory の設定変更方法

コマンド経由で設定

# sysctl -w vm.overcommit_memory=2

直接書き込む場合

# echo 2 > /proc/sys/vm/overcommit_memory

メモリーアロケートの実験

overcommit モードごとに メモリーアロケートを3パターンで試すサンプルプログラムがあったので実験してみた。
http://www.win.tue.nl/~aeb/linux/lk/lk-9.html

  1. サイズを増やしながらひたすらmalloc
  2. サイズを増やしながらmallocして利用を繰り返す
  3. サイズを増やしながら上限までmallocし、確保した領域を順次利用

Demo program 1: allocate memory without using it.

#include <stdio.h>
#include <stdlib.h>

int main (void) {
        int n = 0;

        while (1) {
                if (malloc(1<<20) == NULL) {
                        printf("malloc failure after %d MiB\n", n);
                        return 0;
                }
                printf ("got %d MiB\n", ++n);
        }
}

Demo program 2: allocate memory and actually touch it all.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main (void) {
        int n = 0;
        char *p;

        while (1) {
                if ((p = malloc(1<<20)) == NULL) {
                        printf("malloc failure after %d MiB\n", n);
                        return 0;
                }
                memset (p, 0, (1<<20));
                printf ("got %d MiB\n", ++n);
        }
}

Demo program 3: first allocate, and use later.

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define N       10000

int main (void) {
        int i, n = 0;
        char *pp[N];

        for (n = 0; n < N; n++) {
                pp[n] = malloc(1<<20);
                if (pp[n] == NULL)
                        break;
        }
        printf("malloc failure after %d MiB\n", n);

        for (i = 0; i < n; i++) {
                memset (pp[i], 0, (1<<20));
                printf("%d\n", i+1);
        }

        return 0;
}

Linux-2.6.32、RAM 256MB、スワップなし(意図的に # swapoff -a)の環境で 各 overcommit モードに対してどこまで malloc が成功するのか調べたところ

  • モード 0, 1 では デモ1&2ともに OOM-killer 発動までひたすら malloc、デモ3は10G分のmallocは正常に終わり、利用のタイミングでOOM発動。
  • モード 2 では計算式にもどづいた領域まで malloc が成功し、正常終了

という結果になった。
モード 0 でも OOM の前に malloc が NULL をかえすのではないかと思ったが、そうはならなかった。

Copy-On-Write(COW)
Linux ではフォーク時に親子で全てがコピーされるわけではなく、メモリ空間は共有される。そのため10GBのプロセスがフォークされても 親子で10GB x2 = 20GB も消費せずにすむ。親子のどちからから書き込み要求があったタイミングで始めてメモリーデータもコピーされる(Copy on Write)。(この機能がないと fork してちょろっと exec するだけの処理が非常に大掛かりになる)。

Redis では BGSAVE コマンドで データベースをダンプする際、実処理は子プロセスに任せている(rdb.c)。
ダンプ時にメモリを大量を確保する際に、チェックが厳しいとこけてしまうので、 overcommit_memory=1 にしろ、というのが起動時の警告メッセージの意図みたい。

BSD の vfork
Linux Kernel Development(3rd ed.)“(Robert Love, Addison-Wesley Professional) の§3 Process Management – Process Creation によると、Kernel 2.6 では fork 時に

  • Copy-On-Write
  • child-run-first

する仕組みがある。この仕組がない Unix 系OSでは(古の 3BSD とか)、親プロセスのアドレス空間で実行され(read only)、子プロセスが exec するか exit するまで親プロセスがブロックされる vfork  が活用されていたらしい。

NetBSD Documentation: Why implement traditional vfork()
http://www.netbsd.org/docs/kernel/vfork.html

 redis : bgsave 失敗時のログ

話を最初に戻し、 redis が bgsave 時のメモリ確保に失敗すると以下の様になる。
クライアントのコマンド実行

$ ./redis-cli bgsave
(error) ERR

サーバーサイドのログ

[4574] 19 Feb 11:09:05 # Can't save in background: fork: Cannot allocate memory

Redis のメモリ使用量の確認方法

info コマンドの memory 系出力を見ればOK。

$ ./redis-cli info | grep mem
used_memory:42509968
used_memory_human:40.54M
used_memory_rss:51511296
used_memory_peak:44598520
used_memory_peak_human:42.53M
mem_fragmentation_ratio:1.21
mem_allocator:jemalloc-2.2.5

各パラメータの意味は INFO コマンドのページにある。

  • used_memory is the total number of bytes allocated by Redis using its allocator (either standard libc malloc, or an alternative allocator such as tcmalloc
  • used_memory_rss is the number of bytes that Redis allocated as seen by the operating system. Optimally, this number is close to used_memory and there is little memory fragmentation. This is the number reported by tools such as top and ps. A large difference between these numbers means there is memory fragmentation. Because Redis does not have control over how its allocations are mapped to memory pages, used_memory_rss is often the result of a spike in memory usage. The ratio between used_memory_rss and used_memory is given as mem_fragmentation_ratio.

used_memory は スタティック変数 used_memory で管理されている。

rssResident Set Size の略。実装(zmalloc.c : zmalloc_get_rss)を見ると /proc/[pid]/stat の 24 番目の rss フィールドを読んでいるだけ。stat(5) の man によると

number of pages the process has in real memory. This is just the pages which count toward text, data, or stack space. This does not include pages which have not been demand-loaded in, or which are swapped out.

used_memory_peak は Redis 2.4 から導入。Redis 作者 antirez “Everything about Redis 2.4” の記述を引用すると

Your RSS and your fragmentation rate are usually related to the peak memory usage. Now Redis is able to hold this information, and this is very useful for memory related troubleshooting.

*_human 系は表示を human readable にしているだけ。

References

Tagged with: , , , ,
Posted in database, linux

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Archives
  • RT @__apf__: How to write a research paper: a guide for software engineers & practitioners. docs.google.com/presentation/d… /cc @inwyrd 1 week ago
  • RT @HayatoChiba: 昔、自然と対話しながら数学に打ち込んだら何かを悟れるのではと思いたち、専門書1つだけ持ってパワースポットで名高い奈良の山奥に1週間籠ったことがある。しかし泊まった民宿にドカベンが全巻揃っていたため、水島新司と対話しただけで1週間過ぎた。 それ… 3 weeks ago
  • RT @googlecloud: Ever wonder what underwater fiber optic internet cables look like? Look no further than this deep dive w/ @NatAndLo: https… 3 weeks ago
  • @ijin UTC+01:00 な時間帯で生活しています、、、 6 months ago
  • RT @mattcutts: Google's world-class Site Reliability Engineering team wrote a new book: amazon.com/Site-Reliabili… It's about managing produc… 9 months ago
%d bloggers like this: