DBサーバの場合のLinuxチューニング

今回はLinux上でPostgreSQLMySQLなどのDBMSを使う場合のLinuxカーネルチューニングについて。共有メモリ(shmallやshmmax)については設計時にもれなく設定の確認などをしている場合がほとんどだと思うが、それ以外のTipsとして、今回はLinuxのメモリオーバーコミットに関する記事。DBMS向けと書いたが、Linux上で動作させるソフトウェアであればDBMS以外でも考慮に入れて設計すべきポイントである。

Linuxのメモリ管理

Linuxのメモリ管理サブシステムには「メモリオーバーコミット」と呼ばれる機構があり、マシンに物理的に搭載されているメモリ以上の領域を確保できてしまう。この動作については、メモリ確保の際(Cの場合)実際に使われるmallocを使ったサンプルアプリなどで実際に挙動を確認することができる。参考までに、あるサイトの方は以下のようなサンプル(alloctest.cとする)を使っている。


#include
#include

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状態にし、このサンプルを使用(gcc -o 実行ファイル名 alloctest.c等としてコンパイルして実行)すると、確かに搭載メモリ以上のメモリ領域を確保できてしまうことがわかる。これがLinuxではデフォルトで有効化されているメモリオーバーコミットの動作である。

Linux仮想メモリシステムでは、プロセスがメモリを確保する際には大容量のメモリがあるように見せかけておいて、実際にメモリアクセスが発生したタイミングで物理メモリを割り当てる。これは、「大きなメモリ領域をリザーブしてはいるが、実際にはリザーブした領域全てを使用しないようなプロセス」が複数動いている場合などに、ハードウェアリソースを有効活用するための仕組みであり、元来非常に便利なものである。

では、実際にメモリ不足が起きたらどうなってしまうか?

OOM(Out Of memory) Killer

ここで登場するのが昔から有名な「OOM Killer」という仕組み。Linux が実際にメモリ不足の状態に陥ると、特定のアルゴリズムでいくつかのプロセスを選択し、それらを勝手に killしてしまう。killされるプロセスは、どういう原因で落とされるのかを知る術がない。
実際にOOM Killerがメモリ不足状態であるプロセスをkillすると、以下のようになる。


Jul 25 03:23:27 host1 kernel: Out of Memory: Killed process 8099 (java).
Jul 25 03:23:28 host1 kernel: Fixed up OOM kill of mm-less task
上記の例は、メモリ不足に陥ったLinuxJavaプロセス(この実体はTomcatプロセス)をkillした場合のsyslogの出力結果。(以前本ブログ OOM kill of mm-less task でも少し触れている)。前述のように、この場合Tomcatは何故自分が停止したのかを知らないままプロセスが停止されてしまった状態。この場合のシューティングでは、Tomcatのログを追ってもダウンした理由は吐かれていない。

つまり、malloc の結果を見てメモリ不足のエラー処理をしていたとしても、実際にその処理が走る事はない、ということになる。Linux 上で動いている全てのプロセスは、システムがメモリ不足になっていても気にせずに動作し続けてしまい、その裏では OOM Killer がせっせとプロセスを停止している状態なわけである。

場合によってはパフォーマンス面において OOM Killer が有効な場合もあるので、必ずしもこの仕組みが悪いとは言えない。が、MySQLPostgreSQL などの DBサーバでこの機構が動いてくれるのは嬉しいことではない。メモリ不足が原因で強制的にDBMSのプロセスが落とされるくらいなら、DBサーバ自身でメモリ不足を検出してエラー処理をしたほうが安全なはず。

ここでようやく本題。Linuxカーネル2.6では、メモリオーバーコミットの動作を制御することができるようになっている。

Linuxカーネル設定

メモリオーバーコミットの動作を制御する際に、変更すべきパラメータは以下の2つ。
(ソース:/proc/sys/vm/* に関する文書より)。

パラメータ 説明
/proc/sys/vm/overcommit_memory このファイルには、メモリ・オーバーコミットを有効にするフラグが含まれています。

このフラグが 0 の場合、カーネルは、ユーザ空間からメモリ要求があったときに、残っているフリーメモリの量を評価しようとします。
このフラグが 1 の場合、カーネルは、実際にメモリを使い切るまでは常に十分なメモリがあるかのように振舞います。
このフラグが 2 の場合、カーネルは、どのようなメモリ・オーバーコミットも許さないように試みる「厳格なオーバーコミット」ポリシーを使用します。「もしものときのために」大量のメモリを malloc() するけれども、それをほとんど使用しないというプログラムがたくさんあるので、メモリ・オーバーコミットの機能は非常に役に立ちます。

デフォルトの値は 0 です。
/proc/sys/vm/overcommit_ratio overcommit_memory が 2 にセットされているとき、スワップと、物理
RAM にこのパーセンテージを掛けたものとを足した値を、コミットされ
アドレス空間が越えることは許されません。

DBサーバでは、この2つのパラメータを以下のように設定するとよい。


/proc/sys/vm/overcommit_memory :2を指定。メモリオーバーコミットを無効化
/proc/sys/vm/overcommit_ratio :99を指定。確保できる物理メモリの割合を99%に設定(デフォルトは50)
設定方法は/proc/下のファイルにechoで値を書き込む方法でも、sysctl -wコマンドを用いても問題ないが、設定をサーバ再起動後も維持できた方が都合がよいので、/etc/sysctl.confに設定を書き込み、「sysctl -p」で設定を反映させる。

この状態で先ほどの サンプル(alloctest)を実行してみると、マシンに搭載されている物理メモリのうち、実際の空き容量分しかメモリを確保できなくなることを確認できる。
MySQLPostgreSQLのパフォーマンスチューニングなどをする際には、overcommit_memory=2 にして確保できるメモリサイズを制限しておく。メモリが足りなくなったら Out Of Memory で DBMSが自分で落ちるようになるため、実際にどれだけのメモリを必要としているかがわかるようになる。この設定は、キャッシュやバッファサイズなどのパラメータ調整を大いに助けてくれるはず。