XAMPP で MySQL 接続がやたらと遅い件
XAMPP 環境にて作業を行っていたところ、MySQL 接続がやたらと遅く辟易したため、原因を調査してみた。 少々ハマってしまったので、ここに書いておきます。誰かの役に立てば幸いだ。
ことの発端
現象
現象としては、PHP から MySQL への接続に1秒以上かかるというもの。 ソースコードは下記のたった1行のみ。
mysqli_connect('localhost', 'user', 'password', 'dbname');
ごらんの通り、ただ MySQL に接続するだけのコードである。 たったこれだけで1秒以上かかってしまう。どう考えても遅すぎる。 ちなみに正常な場合は、遅くても 0.01 秒ほど。 空いた口が塞がらなくて顎が外れそうだ。
環境
調査して判明したことだが、この問題はかなり特殊な環境依存問題なので、ここにバージョンを記しておく。 結論からいえば、この問題は Windows Vista/7 + PHP + MySQL という組み合わせ、 つまりは Windows Vista/7 上の XAMPP 環境で発生する。
OS | Windows 7 64bit |
---|---|
XAMPP | 1.8.0 |
PHP | 5.4.4 |
MySQL | 5.5.25a |
原因
判明した原因は下記の通り。
Windows Vista/7 では、 localhost の名前解決に DNS が使われるようになった
hosts ファイルにはこう書いてあり、localhost の記述はコメントアウトされている。
# localhost name resolution is handled within DNS itself. # 127.0.0.1 localhost # ::1 localhost
Windows Vista/7 では、localhost の名前解決は DNS によって行われるようになっているよう(デフォルトの hosts ファイルでは)。 これはどうも Windows Vista/7 時代からのようで、XP 時代では無かったことのようです。 IPv4 か IPv6 のどちらかのコメントアウトを外すことで、外した方を使用することが可能になりますが、 両方ともコメントアウトされていると、IPv6 の方が優先され、ダメなら IPv4 でというような動作となっているようです。
理由は、現在は IPv4 と IPv6 の過渡期なので同時稼働ということになってはいますが、 将来的には IPv4 は無効化されるということが強く想定されているためとのこと。 IPv4 が無効な状態のときに、IPv4 のアドレスに解決されてしまうと困るので、 DNS で解決するようにして切り替えられるようにということらしい。
IPv4 アドレス | IPv6 アドレス | 動作 |
---|---|---|
両方ともコメントアウト(もしくは両方とも有効?) | DNS によって IPv6 と IPv4 を動的に解決 | |
有効 | 無効(コメントアウト) | hosts ファイルに従って IPv4 アドレスを使用 |
無効(コメントアウト) | 有効 | hosts ファイルに従って IPv6 アドレスを使用 |
ここで注目なのは、両方ともコメントアウト状態だと、DNS が使われるということです。
DBサーバーへの接続にも名前解決が必要
IP 上で動作している以上は当たり前なのかもですが、 Web サーバーから DB サーバーへの接続の際に、接続先の DB サーバーをホスト名で指定した場合、 そのホスト名を名前解決する必要があります。
そして今回の原因はまさにその接続先のホスト名に "localhost" と指定することによって、 DNS によって名前解決される必要性が発生し、その結果、処理の遅延が発生したと考えられます。
試しに、接続先のホスト名に "127.0.0.1" と指定した場合には、名前解決をする必要が無いため処理の遅延は発生しません。 また、接続先のホスト名を "localhost" と指定したままの状態で、hosts ファイルの IPv4 のコメントアウトを外した場合も、 DNS が使用されないため、処理の遅延は発生しません。
まとめ
再現方法
- 1. OS が Windows Vista/7 である。
- 2. hosts ファイルの "localhost" の指定が IPv4、IPv6 共にコメントアウトされている。
- 3. PHP から MySQL への接続先のホスト名に "127.0.0.1" ではなく "localhost" と指定している。
上記の条件が全て満たされた場合に、この現象は再現します。
解決方法
ということは、上記のいずれかの条件を満たさないようにすれば良いわけです。 1 は OS を変えなければならないのでそう簡単ではなく、 2 は IPv4 のコメントアウトを外せば解決しますが、IPv6 を外すと今度は繋がらなくなります。 問題の局所性を考えればあまりお勧めしません(この変更はシステム全体に影響するので)。 ということで 3 をなんとかするのが一番簡単な解決方法かと。
つまり...
// これを mysqli_connect('localhost', 'user', 'password', 'dbname'); // こう変更すれば良い... mysqli_connect('127.0.0.1', 'user', 'password', 'dbname');
ナ ナンダッテー!!
Ω ΩΩ
補足
my.cnf で skip-name-resolve を有効にするという方法もあるようです。