PHPからMySQLへの接続が非常に遅い


19

XAMPPを新規インストールしました。PHPMyAdminを最初に開いたとき、非常に遅いことがわかりました。ローカルホストでは、すべてのページを開くのに約5秒かかるとは意味がありませんでした。PHPMyAdminのせいにするために、小さなテストケースを作成しました。

$con = new PDO("mysql:host=localhost;dbname=mysql", "root", "");
$statement = $con->query('SELECT host,user,password FROM user;');
$users = $statement->fetchAll(PDO::FETCH_ASSOC);

上記のスクリプトは、実行に約3秒かかります(ただし、最初に実行したときに読み込むのに8秒近くかかりました)。

次に、PDOの障害かどうかを確認するために、mysql_connect代わりに使用してみました。

$con = mysql_connect("localhost", "root", "");
mysql_select_db("mysql", $con);
$result = mysql_query('SELECT host,user,password FROM user;');

終了するのに丁度時間がかかります。

最初はPHPのせいだと思っていましたが、PHPコードと静的ファイルは、更新をクリックするよりも早く提供されます。この小さなスクリプトを実行してPHPをテストしました。

header("Content-Type: text/plain");

for($i = 0; $i < 5000; $i++)
{
    echo sha1(rand()) . "\n";
}

5000のsha1計算とページは、ウィンドウを更新できるよりもずっと速く表示されます。

それから、MySQLのせいだと思った。しかし、繰り返しますが、MySQLが必要以上に高速に動作していることを理解するために多くのテストをしませんでした。MySQL CLIクライアントを使用すると、ユーザー選択クエリは測定可能な時間もかかりません。リターンキーを離す前に完了します。

問題はPHPのMySQLへの接続であるに違いありません-それは私が推論できる限りです。PHPが遅い、またはMySQLが遅いということはたくさんありますが、PHP + MySQLが極端に遅いということはありません。

これを解決できる人に感謝します!


win32にXAMPP 1.8.0を使用しています(ダウンロードリンク
PHPバージョン:5.4.4
MySQLバージョン:14.14


編集:タイミングをとると、接続機能が非常に時間がかかっていることがわかります:

$time = microtime(true);

$con = mysql_connect("localhost", "root", "");
mysql_select_db("mysql", $con);

$con_time = microtime(true);

$result = mysql_query('SELECT host,user,password FROM user;');

$sel_time = microtime(true);

printf("Connect time: %f\nQuery time: %f\n",
       $con_time-$time,
       $sel_time-$con_time);

出力:

接続時間:1.006148
クエリ時間:0.000247

PHPがデータベースへの接続に多くの時間を費やす原因は何ですか?CLIクライアント、HeidiSQLおよびMySQLワークベンチは即座に接続します


php -m出力してください
thinice

回答:


17

mysqlが接続するたびにrev-dnsクエリを実行しようとすることはありますか?my.cnfのセクションmysqld:skip-name-resolveに追加してみてください。


奇妙なことに、PHPMyAdminとMySQL CLIクライアントの両方が「ホスト '127.0.0.1'はこのMySQLサーバーへの接続を許可されていません」と表示します。PHPスクリプトはまだ動作しますが、同じように遅い以前のように、いくつかの理由
Hubro

mysqlワークベンチのようなmysqlを管理するための「脂肪アプリケーション」は遅いですか?
pQd

MySQL Workbenchから同じクエリを実行すると、CLIクライアントと同じくらい高速になります
。HeidiSQL– Hubro

PHPでタイミングを追加し、接続に時間がかかっているか、クエリを実行しているかどうかを確認します。
pQd

そのコメントをありがとう、質問を更新しました。接続とクエリの両方が非常に高速です
-Hubro

30

これは、私の答えから、ほぼ丸写ししたものですここで、私が想像するように、私はあなたたちが同様んので、上のリンクのみの回答に私たちを知っている顔をしかめ:-)

この問題が発生していて、Windows 7より前のバージョンのWindowsを使用している場合、これはおそらく問題の答えではありません。

なぜこうなった?

この問題の原因は、IPv4とIPv6です。

IPアドレスの代わりにホスト名を使用する場合、MySQLクライアントは最初にAAAA名前の(IPv6)ホストルックアップを実行し、名前がIPv6アドレスに正常に解決される場合、最初にこのアドレスを試行します。いずれかのステップ(名前解決または接続)が失敗すると、IPv4にフォールバックし、Aルックアップを実行して代わりにこのホストを試行します。

これが実際に意味することは、IPv6 localhostルックアップは成功したが、MySQLがIPv6ループバックにバインドされていない場合、IPv4フォールバックが発生して接続が成功する前に1つの接続タイムアウトサイクルを待つ必要があることです。

これはWindows 7より前の問題ではありませんでした。これはlocalhost、ホストファイルを介して解決が行われ、127.0.0.1IPv6に対応するものではなく、事前に構成されていたため::1です。

ただし、Windows 7以降、ここでlocalhost概説した理由により、DNSリゾルバーに解決が組み込まれています。これは、IPv6ルックアップが成功することを意味しますが、MySQLはそのIPv6アドレスにバインドされていないため、接続は失敗し、この質問で説明されている遅延が表示されます。

それはすばらしい。すでに修正する方法を教えてください!

いくつかのオプションがあります。インターネットを見回すと、一般的な「解決策」は名前の代わりにIPアドレスを明示的に使用するように思われますが、これを行わない理由がいくつかあります。

  • IPv6 のみをサポートする別のマシンにスクリプトを移動すると、スクリプトは機能しなくなります。

  • スクリプトを* nixベースのホスティング環境に移動する場合、マジックストリングlocalhostは、MySQLクライアントが設定されている場合、Unixソケットを使用することを意味します。これは、IPループバックベースの接続よりも効率的です。

彼らはかなり重要に聞こえますか?

そうではありません。この種のものが構成ファイルで定義されるように、アプリケーションを設計する必要があります。スクリプトを別の環境に移動する場合、他にも設定が必要になる可能性があります。

要約すると、IPアドレスを使用することは最善の解決策ではありませんが、ほとんどの場合受け入れられるものです。

それでは、最善の解決策は何ですか?

最良の方法は、MySQLサーバーが使用するバインドアドレスを変更することです。ただし、これは簡単なことではありません。Apache、Nginx、およびこれまでに作成された他のほぼすべての健全なネットワークサービスアプリケーションとは異なり、MySQLは単一のバインドアドレスのみをサポートするため、別のアドレスを追加するだけではありません。幸いなことに、オペレーティングシステムはここで少し魔法をサポートしているので、MySQLがIPv4とIPv6の両方を同時に使用できるようにすることができます。

MySQL 5.5.3以降を実行する必要があり、--bind-address=コマンドライン引数を使用してMySQLを起動する必要があります。やりたいことに応じて、4つのオプションdocsがあります。

  • おそらく使い慣れたものと、おそらく(効果的に)使用している可能性の高いものです0.0.0.0。これは、マシンで使用可能なすべてのIPv4アドレスにバインドします。IPv6を気にしなくても、実際にはおそらくこれは最善の方法ではありません。IPv6と同じセキュリティリスクがあるため::です。

  • 明示的なIPv4またはIPv6アドレス(たとえば、127.0.0.1または::1ループバック用)。これにより、サーバーがそのアドレスにバインドされ、そのアドレスのみがバインドされます。

  • マジックストリング::。これにより、MySQLはIPv4およびIPv6モードで、マシン上のすべてのアドレス(ループバックアドレスと物理インターフェースアドレスの両方)にバインドされます。これは潜在的にセキュリティリスクです。これは、リモートホストからの接続を受け入れるためにMySQLが必要な場合にのみ行ってください。

  • IPv4-mapped IPv6 addressを使用します。これは、4-> 6の移行中の下位互換性のためにIPv6に組み込まれた特別なメカニズムであり、特定のIPv4アドレスにバインドすることができ、IPv6と同等です。これは、「デュアルループバック」アドレス以外の場合に役立つとは考えにくい::ffff:127.0.0.1。これはほとんどの場合、ループバックにバインドするだけでIPv4接続とIPv6接続の両方を許可するほとんどの人にとって最適なソリューションです。

hostsファイルを変更する必要がありますか?

いや。hostsファイルを変更しないでください。DNSリゾルバは何をすべきかを知っておりlocalhost、再定義してもせいぜい効果がなく、最悪の場合リゾルバの混乱を招くでしょう。

どう? --skip-name-resolve

これは、関連するがわずかに異なる理由で、問題を解決する場合もあります。

この構成オプションがないと、MySQLはすべてのクライアント接続IPアドレスをPTRDNSクエリを介してホスト名に解決しようとします。MySQLサーバーでIPv6の使用が既に有効になっているが、接続にまだ時間がかかる場合は、逆DNS(PTR)レコードが正しく構成されていない可能性があります。

名前解決を無効にするとこの問題は解決しますが、他の影響もあります。特に、Host条件でDNS名を使用するように構成されたアクセス許可はすべて失敗します。

これを行う場合は、名前の代わりにIPアドレスを使用するようにすべての許可を構成する必要があります。


2
最後に!ありがとうありがとう!最後に、私はすべての原因、考えられる解決策、およびそれらの反対徴候の適切で完全かつ明確な説明を見つけました。あなたはそれについて本を書くべきです!
tobia.zanarella 14年

4
bind-addressをに変更するまで、localhost上のMySQLへの接続に1秒の遅延がありました::1。残念ながら::ffff:127.0.0.1、(使用してskip-name-resolveいるかどうかに関係なく)1秒の遅延が発生し続けましたが、その理由は何ですか?(Windows 8.1)
サイモンイースト14

1
@Simon見ないで手掛かりではありませんが、デバッグの最初のステップは、明示的なアドレスを使用してIPv6ループバックとIPv4ループバックの両方に直接接続し、MySQLが実際に両方のスタックでリッスンして接続可能であることを確認し、そこからデバッグすることです。
DaveRandom

1
いや、:: FFFF:127.0.0.1 doesntの仕事...
Raheelハサン

:: 1でバインドすると、Sqlyogは機能しません...どうすればいいですか?
ラヒールハサン14

13

通常、MySQLへのサーバー接続でIPv6が有効になっている場合 localhostは非常に遅くなります。

127.0.0.1この問題を解決するために、スクリプト内のmysqlサーバーアドレスを変更します。


+1これは私にとって正解でした。私は、Windows 8で、彼らはの解像度に移動推測localhost:@DaveRandomがにリンクされていることの理由のためのDNSリゾルバへserverfault.com/questions/4689/...
wwarren

それは私のために働いた:D
FosAvance 14年

これは以外のサーバーで発生する可能性がありますlocalhost。フォームのサーバーアドレスにも同じ遅延の問題がありましたxx.xxxx.xxxxx.xxxxx.com。サーバー名をIPアドレスに変更すると、問題はなくなりました。
グルーバー14年

1
mysql_connect("localhost", "root", "");

理由は何なのかは明らかです。PHPはいくつかの点で非常に優れていますが、「localhost」を「127.0.0.1」に直接変換することはできません。あなたはそれを試してください、それは本当にあなたの全体のウェブサイトページのロード時間を短縮します


あなたが問題を示唆しようとしているものを理解することはできません。
カスペルド14

@kasperd 'localhost'のDNS解決
Xesau 14

'17からのノート- mysql_ *の機能は廃止とPHP7で除去される
treyBake


0

また、db接続変数を少し調整することで、クエリのスローダウンをなくすこともできます(移植性のために、スクリプトとは別のファイルに格納することが望ましい)。ホスト値を「localhost」ではなく「127.0.0.1」に変更します。これにより、localhostの長いDNSルックアップがバイパスされます。

お役に立てれば!

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.