apache+mysql+phpでDB接続エラーになる場合の対応方法

今月に入ってお客さんのサイトでapacheがハングアップして応答が無くなり、mysql,apacheの再起動を余儀なくされる状況が1日おき位に続いていた。対応が結構しんどかったので、何とか原因を見つけようとした作業記録です。

1. サーバ環境

webサーバ:1台:Redhat Linux 2.4.21-40(かなり古いです) mem=1G, cpu=1

DBサーバ:1台:Redhat Linux 2.6.9-42 mem=8G, cpu=4

2.LAMP構成

webサーバ:apache 2.2.6, php4.3.11

DBサーバ:mysql 4.1.22

3.現象

稼動しているとあるタイミング(主にアクセスが多い時間帯)にhttpdが無反応になる。apacheをリスタートしても状況が変わらず、apache停止→mysqlリスタート→apacheスタートの順で回復する。

4.やったこと

(1) mrtgでのプロセス監視

原因が良くわからないので、mrtgで各プロセスを監視することにした。測定内容はapacheのプロセス数、mysqlの接続数、各サーバの負荷など。

(2) apacheの設定変更

MaxClients 20

MaxKeepAliveRequests 5

KeepAlive On

KeepAliveTimeout 1

Timeout 30

とかなり控えめなプロセス数を設定。→ハングアップ周期は長くなるものの、改善に至らず。

(3) mysqlの設定変更

long_query_time=3
log-slow-queries=/var/log/slow.log

my-medium.cnfとほぼ同じ値を設定。(これもマシンスペックに対してはかなり控えめ)→ハングアップ周期は長くなるものの、改善に至らず。上記設定を追加して、実行速度の遅いクエリーを追跡することにした。

(4) php.iniの設定変更

mysql.max_links = 7

mysql.connect_timeout = 8

mysql.trace_mode = On

と接続控えめなプロセス数を設定。→ハングアップ周期は長くなるものの、改善に至らず。

trace_modeをOnにして、mysql接続時のphpエラーを監視することにした。

※追記

mysql.allow_persistent =Off

を追加して様子を見ています。持続的接続関数のmysql_pconnectを使ってないので、関係無いと思うけど設定してみました。

5. 解決策

まず、mysqlの実行速度の遅いクエリーをつぶすことにした。(上記(3)の設定で)

LEFT JOINを使うSQLが特に問題となっていたので、プログラム内でsqlを2つに分けたりしてLEFT JOINを使わない形に変更した。これでハングアップ周期は伸びたが、まだ完全には解決しなかった。

そこでサーバがハングアップした際、web,dbサーバのそれぞれでnetstatコマンドを実行してみると、3306ポートで大量のTIME_WAITが滞留していることに気づいた。このTIME_WAIT状態でネットワークリソースが枯渇すると考え、この状況を改善する方法を調査したところ、sysctlでカーネルの設定値が変更することが分かった。各サーバで下記コマンドを実行。

# /sbin/sysctl -w net.ipv4.tcp_tw_recycle=1

/etc/sysctl.confにも

net.ipv4.tcp_tw_recycle=1
net.ipv4.tcp_fin_timeout=10
net.ipv4.ip_no_pmtu_disc=1

を追加。

この設定により上記状況は改善された。(たぶん。。)