2006年09月14日
DBサーバ向けLinuxチューニングを考える 〜 メモリオーバーコミット編
Cでプログラムを書いていて大量のメモリを確保したくなったとき、大抵は mallocを使うと思いますが、その際には戻り値がNULLかどうかを判断してエラー処理に飛ばすと思います。しかし、Linux のメモリ管理サブシステムには「メモリ・オーバーコミット」という機構があり、実装されているメモリ以上の領域を確保できてしまいます。
試しに以下のような簡単なコード (alloctest.c) を書いてみます。
swapoff したメモリ 1G のマシンでこれを実行するとこんな感じになります。
ちなみに free の結果はこんな感じです。
なんと 1G のマシンなのに 3G もの領域を確保することができてしまいました。
Linux の仮想メモリシステムでは、プロセスがメモリを確保する際にはたくさんあるように見せかけておいて、実際にアクセスが発生した時に物理メモリを割り当てています。これは、「念のためいっぱいメモリを確保しておくけど実際はそんなに使わないプロセス」が複数動いているときなどに、ハードウエアリソースを有効活用するための優れた仕組みといえます。
それでは、実際にメモリ不足が起きたらどうなってしまうのでしょうか。
ここで登場するのが有名な「OOM Killer」という仕組みです。
Linux が実際にメモリ不足に陥ると、とあるアルゴリズムでいくつかのプロセスを選択し、それを勝手に kill してしまいます。kill されるプロセスは、自分が何故落とされるのかを知る由がありません。
つまり、malloc の結果を見てメモリ不足のエラー処理をしていたとしても、実際にその処理が走る事はないのです。Linux 上で動いている全てのプロセスは、システムがメモリ不足になっていても気にせずに動作しつづけてしまい、その裏では OOM Killer がせっせとプロセスを停止しているわけです。
時と場合によってはパフォーマンス面において OOM Killer が有効な事もありますので、必ずしもこの仕組みが悪いとはいいませんが、ただ、MySQL や PostgreSQL などの DBサーバでこの機構が動いてくれるのは嬉しいことではありません。メモリ不足が原因で強制的にプロセスが落とされるくらいなら、 DBサーバ自身でメモリ不足を検出してエラー処理をしたほうが安全ではないでしょうか。
カーネル2.6では、以下のコマンドでこの動作を制御することができるようになっています。
/proc/sys/vm/overcommit_ratio で確保できる物理メモリの割合を指定します。
/proc/sys/vm/overcommit_memory を 2にするとメモリオーバーコミットを許さなくなります。
※overcommit_ratio のデフォルト値は 50 です。指定しないままだと搭載メモリの半分までしか確保できなくなってしまいますのでご注意下さい。でないとあちらこちらでメモリ不足がではじめます。
この状態で先ほどの alloctest を実行してみます。
実際の空き容量しか確保できなくなったようです。
MySQLのパフォーマンスチューニングなどをする際には、overcommit_memory=2 にして確保できるメモリサイズを制限しておきます。メモリが足りなくなったら Out Of Memory で MySQL が自分で落ちるようになりますので、実際にどれだけのメモリを必要としているかがわかるようになります。
これでバッファサイズなどのパラメータ調整が容易になるのではないでしょうか。
参考
/proc/sys/vm/* に関する文書
#include <stdio.h>
#include <stdlib.h>
int main()
{
int i;
char *p;
for(i=0;i<65536;i++){
p = (char *)malloc(65536);
if(0 == (long)p){
break;
}
}
printf("SIZE=%dMB\n",i*65536/1024/1024);
return(0);
}
swapoff したメモリ 1G のマシンでこれを実行するとこんな感じになります。
$ gcc -o alloctest alloctest.c
$ ./alloctest
SIZE=3052MB
ちなみに free の結果はこんな感じです。
$ free -m
total used free shared buffers cached
Mem: 938 728 209 0 191 118
-/+ buffers/cache: 418 519
Swap: 0 0 0
なんと 1G のマシンなのに 3G もの領域を確保することができてしまいました。
Linux の仮想メモリシステムでは、プロセスがメモリを確保する際にはたくさんあるように見せかけておいて、実際にアクセスが発生した時に物理メモリを割り当てています。これは、「念のためいっぱいメモリを確保しておくけど実際はそんなに使わないプロセス」が複数動いているときなどに、ハードウエアリソースを有効活用するための優れた仕組みといえます。
それでは、実際にメモリ不足が起きたらどうなってしまうのでしょうか。
ここで登場するのが有名な「OOM Killer」という仕組みです。
Linux が実際にメモリ不足に陥ると、とあるアルゴリズムでいくつかのプロセスを選択し、それを勝手に kill してしまいます。kill されるプロセスは、自分が何故落とされるのかを知る由がありません。
つまり、malloc の結果を見てメモリ不足のエラー処理をしていたとしても、実際にその処理が走る事はないのです。Linux 上で動いている全てのプロセスは、システムがメモリ不足になっていても気にせずに動作しつづけてしまい、その裏では OOM Killer がせっせとプロセスを停止しているわけです。
時と場合によってはパフォーマンス面において OOM Killer が有効な事もありますので、必ずしもこの仕組みが悪いとはいいませんが、ただ、MySQL や PostgreSQL などの DBサーバでこの機構が動いてくれるのは嬉しいことではありません。メモリ不足が原因で強制的にプロセスが落とされるくらいなら、 DBサーバ自身でメモリ不足を検出してエラー処理をしたほうが安全ではないでしょうか。
カーネル2.6では、以下のコマンドでこの動作を制御することができるようになっています。
# sysctl -w vm.overcommit_ratio=99
# sysctl -w vm.overcommit_memory=2
/proc/sys/vm/overcommit_ratio で確保できる物理メモリの割合を指定します。
/proc/sys/vm/overcommit_memory を 2にするとメモリオーバーコミットを許さなくなります。
※overcommit_ratio のデフォルト値は 50 です。指定しないままだと搭載メモリの半分までしか確保できなくなってしまいますのでご注意下さい。でないとあちらこちらでメモリ不足がではじめます。
この状態で先ほどの alloctest を実行してみます。
$ ./alloctest
SIZE=428MB
実際の空き容量しか確保できなくなったようです。
MySQLのパフォーマンスチューニングなどをする際には、overcommit_memory=2 にして確保できるメモリサイズを制限しておきます。メモリが足りなくなったら Out Of Memory で MySQL が自分で落ちるようになりますので、実際にどれだけのメモリを必要としているかがわかるようになります。
これでバッファサイズなどのパラメータ調整が容易になるのではないでしょうか。
参考
/proc/sys/vm/* に関する文書
トラックバックURL
この記事へのコメント
2. Posted by imsut 2006年12月06日 20:41
こんにちは。OOM Killerのことは始めて知りました。勉強になります。
> MySQLのパフォーマンスチューニングなどをする際には、overcommit_ratio=2 にして
「overcommit_memory=2にして」ではないでしょうか? _ratio=2だと、恐ろしいことになりそうです。
> MySQLのパフォーマンスチューニングなどをする際には、overcommit_ratio=2 にして
「overcommit_memory=2にして」ではないでしょうか? _ratio=2だと、恐ろしいことになりそうです。
3. Posted by Yasui 2006年12月06日 21:18
おおおおぉ・・
imsutさん、ご指摘ありがとうございます。
本文を修正しました。
たしかに ratio=2 にすると恐ろしいことになりますね(^^;
imsutさん、ご指摘ありがとうございます。
本文を修正しました。
たしかに ratio=2 にすると恐ろしいことになりますね(^^;