Jeremy Zawodny 氏のブログに MySQL, Linux, and Thread Caching という興味深い記事があったので、理解するために自分翻訳してみた。この記事は、今はないけれども rember.yahoo.com で MySQL を使ったときの話のようです。ちなみに MySQL のバージョンは 4.0.4。
わぉ、とても忙しい一週間だった。私は、remeber.yahoo.com の MySQL サーバや関連するものごとに実に何日かを費した。そして、私は1日か2日を休息に使った(睡眠やシャワーなど)
ところで、私はいくつか興味深い発見をした。もっとも驚いたことは MySQL サーバがとても忙しくなったときの Linux 上でのスレッドキャッシングの挙動だ。ポイントは、忙しいときのみということだ。忘れずに。
あなたも分かっていると思うが、われわれがすべてのウェブサーバは一つのマスター DB サーバを持っていて PHP を使って接続しているということだ(これはいつか変えたいと思っているが)。このことは一つのタイルを作ることを含んでいて、一つのタイルがクールになるということだ。加えてマスター DB サーバはとても忙しくなるということだ。
なぜならこのとき 20 台から 45 台のフロントエンドのweb サーバがあって、それぞれのサーバの Apache プロセス数が 70 以上になって接続が必要になったときある問題が発生した。つまりマスター DB サーバは最悪のケースで 45 x 70 = 3,150 の接続をハンドリングすることが必要だった。ほとんどの PHP コードは mysql_pconnect 関数を使って持続的な接続 (persistent connections) を保っていた。
ただ心配するまでもなく、私は wait_timeout をとても低い値である 15 秒に設定していた。これは MySQL は 15 秒異常になったアイドル状態の接続を閉じるということだ。しかし、私はウェブサーバからマスター DB サーバへの接続が拒否されるというレポートを受け取るまで問題に気がつかなかった。なぜか?なぜなら、私はマスター DB サーバの my.cnf で意味のある最大接続数を設定していたからだ。
set-variable = max_conenctions=180
set-variable = max_user_connections=140
このとき、wait_timeout は 600 秒(10分)に設定していた。問題は明かになった。それはたくさんのアイドルなクライアント接続があると、新しいクライアントからの接続はブロックされてしまうことだった。
何をしたのか?
われわれは mysql_pconnect 関数を使うことを止めたが、あなたにも分かるとおり遅延する問題を解決することができなかった。これは本当にくだらなかった。そして私は MySQL 4.0.4 を動作させていたことを分かっていた。私は再起動することなしでサーバの設定をほとんど変更点できるという新しい機能をもっていた。このオンラインマニュアルを読んでみよう。
すばらしい!
次のコマンドを実行することが必要だった。
SET GLOBAL wait_timeout=60;
60 の部分を異なる値に置き換えればよい。
私は15秒のタイムアウトに設定した。
しかし、とても興味深く予期せぬ現象に遭遇した。それは Linux サーバはかなりの高負荷になったとき新しいスレッドを生成しているということだ。これは CPU 時間の量からも明かだった。
CPU 時間はどのくらいか?そのときの SHOW STATUS の出力を見てみると、次の状態だった。
| Threads_cached | 0 |
| Threads_created | 270194 |
| Threads_connected | 46 |
| Threads_running | 28 |
とても悪い現象が起こった。マシンはとても少ないアイドルの CPU 時間を持っていたほとんどの場合、約 5-10%。しかし、本当はそれほど仕事をしていなかった、たぶん 1 秒あたり 40 クエリーほど。私は、すこし戸惑った。しかし、Thread_cached の数字が飛びぬけているのが分かった。それはとても高く増え続けていた。
幸運なことに私は thread_cache の設定を覚えていた。だから、私はさらに詳しく調べてみることにした。
mysql> SELECT @@global.thread_cache_size;
@@thread_cache_size が 0 になっている。
オー、これはいかん。私は my.cnf にスレッドキャッシュの設定をしていなかったのでデフォルト値であった。これは悪かった。それは Apache 1.3 の fork 前の許容量を削除しているようなものだったので忙しいウェブサーバになってしまった。新しいリクエストごとに新しいプロセスをフォークするということは、とても高くついてしまった。
Ugh!
すぐにスレッドキャッシュの設定を次のようにした。
SET GLOBAL thread_cache_size=40;
これは 40 スレッドをキャッシュするという意味になって、われわれはたくさんの仕事をしなくて済むことができた。
別のウィンドウでは私は vmstat 1 コマンドを実行していて動的な変化に注意していた。マシン上のアイドル CPU はすぐに 35-40% から 5-10% になった。
もしたった一つだけ私はすぐに考えることができた。(?)
だから、物語りのモラルはこれだ「もしあなたはたくあんの素早い接続をしていくる忙しいサーバを持っていたらのなら、SHOW STATUS の Threads_created の値が増え続けるのを止めるために十分に高いスレッドキャッシュの設定をしなさい。あなたの CPU にありがとうを言おう。
私は悪く考えたとは感じなかった。われわれはコードを最適しようとしていたが、サーバが動き続けていてとても少ないスリープをしていた。スレッドキャッシングは、本当に我々問題の中で最悪なものではなかった。しかし、それは大きくなる前に解決できた問題になった。
それはとても経験から学べることが多かったことであった。
ということで、MySQL のスレッドキャッシングの設定を見直してみたいと思います。
まず、現在の wait_timeout を見るには、次のコマンドを実行する。
$ mysql -u root -e ‘show variables like “wait_timeout”‘
+—————+——-+
| Variable_name | Value |
+—————+——-+
| wait_timeout | 28800 |
+—————+——-+
次に、thread_cache_size の値をみてみる。
$ mysql -u root -e ‘select @@thread_cache_size’
+———————+
| @@thread_cache_size |
+———————+
| 250 |
+———————+
そして、Thread_created の値をみてみる。
$ mysql -u root -e ‘show status’ | grep Threads_created
Threads_created 251
wait_timeout を変更するときには、次のコマンドを実行する。変更したあとは、/etc/my.cnf の mysqld セクションに wait_timeout = 60 の追記を忘れずに。my.cnf に追記しないと再起動したときに戻ってしまうので要注意。
$ mysql -u root -e ‘SET GLOBAL wait_timeout=60′
もし、MySQL に接続して比較的長いバッチ処理をするのなら、あまり短い時間を設定してしまうと接続が切れてしまうので注意する。
次に thread_cache_size を変更するときには、次のコマンドを実行する。thread_cache_size はキャッシュするスレッドサイズの数を指定する。この値は、MySQL のスレッドが使用するメモリ容量を考えて設定する必要がある。MySQL のスレッドスタックサイズは通常 2MB となっている(show variables の thread_stack の値がそれ)。そして、SHOW STATUS コマンドの Threads_connected(現在開いている接続の数)、Threads_created(接続を処理するために生成されたスレッド数)、Threads_running(スリープ状態になっていないスレッド数)、の値を見ながら調整する調整する必要がある。my.cnf に追記する場合は、thread_cache = 10 となるので要注意。
$ mysql -u root -e ‘SET GLOBAL thread_cache_size=10′
あわせて、max_connections を変更するときには、次のコマンドを実行する。max_connections はただ単に増やしてしまうと MySQL がメモリ不足になってしまうので要注意。サーバのリリースをみながら変更していくのが吉。max_connections = thread_cache_size / 3 で設定している例が多いようです。
$ mysql -u root -e ‘SET GLOBAL max_connections=100′
MySQL の show status コマンドで、Threads_created が増えている場合はスレッドキャッシュを設定するといいかもしれません。
Tags: mysql