レイズソフトウェア

XAMPPでMySQL接続がやたらと遅い件

はてなブックマーク
2012/09/26 16:06:06

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 を有効にするという方法もあるようです。

参考サイト