Die Verbindung zu MySQL über PHP ist extrem langsam

19

Ich habe gerade eine Neuinstallation von XAMPP durchgeführt. Beim ersten Öffnen von PHPMyAdmin ist mir aufgefallen, dass es extrem langsam ist. Es machte keinen Sinn, dass es auf localhost fast 5 Sekunden dauern sollte, bis sich jede Seite öffnet. Ich habe einen kleinen Testfall gemacht, um die Schuld von PHPMyAdmin abzuwenden:

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

Die Ausführung des obigen Skripts dauert nur ca. 3 Sekunden (obwohl das Laden beim ersten Ausführen knapp 8 Sekunden dauerte.)

Um dann zu überprüfen, ob es der Fehler von PDO war, habe ich mysql_connectstattdessen versucht, Folgendes zu verwenden:

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

Es dauert genau so lange, bis es fertig ist.

Anfangs dachte ich, es sei PHPs Fehler, aber PHP-Code und statische Dateien werden schneller geliefert, als ich auf Aktualisieren klicken kann. Ich habe PHP getestet, indem ich dieses kleine Skript ausgeführt habe:

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

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

5000 sha1Berechnungen und die Seite wird immer noch schneller angezeigt, als ich mein Fenster aktualisieren kann.

Dann dachte ich, es sei MySQLs Schuld. Aber ich habe auch nicht viel getestet, um herauszufinden, dass MySQL schneller funktioniert, als ich es brauche. Mit dem MySQL CLI-Client nimmt die Benutzerauswahlabfrage nicht einmal messbare Zeit in Anspruch - sie wird durchgeführt, bevor ich die Return-Taste überhaupt losgelassen habe.

Das Problem muss die PHP-Verbindung zu MySQL sein - soweit ich es begründen konnte. Ich kann Unmengen von Dingen finden, bei denen PHP langsam oder MySQL langsam ist, aber nichts, bei denen PHP + MySQL extrem langsam ist.

Vielen Dank an alle, die mir bei der Lösung helfen können!


Ich benutze XAMPP 1.8.0 für Win32 ( Download-Link )
PHP-Version: 5.4.4
MySQL-Version: 14.14


BEARBEITEN: Nach dem Timing stellt sich heraus, dass es die Verbindungsfunktion ist, die so lange dauert:

$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);

Ausgabe:

Verbindungszeit: 1.006148
Abfragezeit: 0.000247

Was kann dazu führen, dass PHP viel Zeit mit dem Herstellen einer Verbindung zur Datenbank verbringt? Der CLI-Client, HeidiSQL und MySQL Workbench stellen sofort eine Verbindung her

Hubro
quelle
PHP -m Ausgabe bitte
Thinice
@thinice: pastebin.com/4dXe7ZDa
Hubro

Antworten:

17

Kann es sein, dass Ihre MySQL versucht, Rev-DNS-Abfrage auszuführen, wenn Sie eine Verbindung herstellen? Versuchen Sie, my.cnf, Abschnitt mysqld: Skip-Name- Resolution, hinzuzufügen .

pQd
quelle
Seltsamerweise geben mir PHPMyAdmin und der MySQL CLI-Client jetzt "Host '127.0.0.1' darf keine Verbindung zu diesem MySQL-Server herstellen". Aus irgendeinem Grund funktioniert das PHP-Skript immer noch, aber genauso langsam wie zuvor
Hubro
Sind 'Fat Applications' für die Verwaltung von MySQL - wie MySQL Workbench - so langsam?
pQd
Dieselbe Abfrage von MySQL Workbench aus auszuführen ist genauso schnell wie der CLI-Client, und HeidiSQL
Hubro wird
Fügen Sie ein wenig Zeit in PHP hinzu und prüfen Sie, ob die Verbindung oder Ausführung der Abfrage viel Zeit in Anspruch nimmt.
pQd
Vielen Dank für diesen Kommentar, ich habe meine Frage aktualisiert. Die Verbindung und Abfrage ist super schnell
Hubro
30

Dies ist fast wörtlich aus meiner Antwort hier entnommen , aber ich weiß, dass wir bei SO-Antworten nur auf Links die Stirn runzeln, also stelle ich mir vor, dass ihr es auch tut :-)

Wenn Sie dieses Problem haben und eine Windows-Version vor Windows 7 verwenden, ist dies wahrscheinlich nicht die Antwort auf Ihr Problem.

Warum passiert das?

Die Ursache für dieses Problem ist IPv4 vs IPv6.

Wenn Sie anstelle einer IP-Adresse einen Hostnamen verwenden, führt der MySQL-Client zuerst eine AAAAIPv6-Hostsuche nach dem Namen durch und versucht diese Adresse zuerst, wenn der Name erfolgreich in eine IPv6-Adresse aufgelöst wird. Wenn einer der Schritte fehlschlägt (Namensauflösung oder Verbindung), wird auf IPv4 zurückgegriffen, eine ASuche ausgeführt und stattdessen dieser Host getestet.

In der Praxis bedeutet dies, dass Sie, wenn die IPv6- localhostSuche erfolgreich ist, MySQL jedoch nicht an den IPv6-Loopback gebunden ist, einen Verbindungszeitüberschreitungszyklus abwarten müssen, bevor der IPv4-Fallback auftritt und die Verbindung erfolgreich ist.

Dies war vor Windows 7 kein Problem, da die localhostLösung über die Hosts-Datei erfolgte und nur mit vorkonfiguriertem 127.0.0.1IPv6-Gegenstück geliefert wurde ::1.

Seit Windows 7 localhostist die Auflösung jedoch aus den hier beschriebenen Gründen in den DNS-Resolver integriert . Dies bedeutet, dass die IPv6-Suche nun erfolgreich ist. MySQL ist jedoch nicht an diese IPv6-Adresse gebunden, sodass die Verbindung fehlschlägt und die in dieser Frage angegebene Verzögerung angezeigt wird.

Das ist schön. Sagen Sie mir einfach, wie ich das Problem beheben kann!

Sie haben ein paar Möglichkeiten. Wenn Sie sich im Internet umsehen, scheint die allgemeine "Lösung" darin zu bestehen, die IP-Adresse explizit anstelle des Namens zu verwenden. Es gibt jedoch eine Reihe von Gründen, warum Sie dies nicht tun sollten. Beide Gründe hängen mit der Portabilität zusammen, beide sind vermutlich nicht wichtig:

  • Wenn Sie Ihr Skript auf einen anderen Computer verschieben, der nur IPv6 unterstützt, funktioniert Ihr Skript nicht mehr.

  • Wenn Sie Ihr Skript in eine * nix-basierte Hosting-Umgebung verschieben, würde die magische Zeichenfolge localhostbedeuten, dass der MySQL-Client lieber einen Unix-Socket verwenden würde, wenn einer konfiguriert ist. Dies ist effizienter als eine IP-Loopback-basierte Konnektivität

Sie klingen aber ziemlich wichtig?

Sie sind nicht. Sie sollten Ihre Anwendung so gestalten, dass dies in einer Konfigurationsdatei definiert ist. Wenn Sie Ihr Skript in eine andere Umgebung verschieben, müssen möglicherweise auch andere Dinge konfiguriert werden.

Zusammenfassend ist die Verwendung der IP-Adresse nicht die beste Lösung, aber höchstwahrscheinlich eine akzeptable.

Also, was ist die beste Lösung?

Am besten ändern Sie die Bindungsadresse, die der MySQL-Server verwendet. Dies ist jedoch nicht so einfach, wie man möchte. Im Gegensatz zu Apache, Nginx und fast jeder anderen vernünftigen Netzwerkdienstanwendung, die jemals erstellt wurde, unterstützt MySQL nur eine einzige Bindungsadresse, sodass nicht nur eine weitere hinzugefügt werden muss. Glücklicherweise unterstützen Betriebssysteme hier ein bisschen Magie, sodass MySQL IPv4 und IPv6 gleichzeitig verwenden kann.

Sie müssen MySQL 5.5.3 oder höher ausführen und MySQL mit dem --bind-address=Befehlszeilenargument starten . Sie haben 4 Optionen docs , je nachdem , was Sie tun möchten:

  • Die Sie wahrscheinlich vertraut sind, und die, die Sie am ehesten (effektiv) Verwendung 0.0.0.0. Dies bindet an alle verfügbaren IPv4-Adressen auf dem Computer. Dies ist wahrscheinlich auch dann nicht das Beste, wenn Sie sich nicht für IPv6 interessieren, da es die gleichen Sicherheitsrisiken wie IPv6 aufweist ::.

  • Eine explizite IPv4- oder IPv6-Adresse (zum Beispiel 127.0.0.1oder ::1für Loopback). Dadurch wird der Server an diese Adresse und nur an diese Adresse gebunden.

  • Die magische Schnur ::. Dadurch wird MySQL im IPv4- und IPv6-Modus an alle Adressen auf dem Computer gebunden, sowohl Loopback- als auch physikalische Schnittstellenadressen. Dies ist möglicherweise ein Sicherheitsrisiko. Tun Sie dies nur, wenn Sie MySQL benötigen, um Verbindungen von Remote-Hosts zu akzeptieren.

  • Verwenden Sie eine IPv4-zugeordnete IPv6-Adresse . Dies ist ein spezieller Mechanismus, der in IPv6 integriert ist, um die Abwärtskompatibilität während des 4 -> 6-Übergangs zu gewährleisten. Er ermöglicht das Binden an eine bestimmte IPv4-Adresse und deren IPv6-Äquivalent. Es ist ziemlich unwahrscheinlich, dass dies für Sie für etwas anderes als die "Dual Loopback" -Adresse nützlich ist ::ffff:127.0.0.1. Dies ist höchstwahrscheinlich die beste Lösung für die meisten Benutzer, da sie nur an das Loopback gebunden sind, aber sowohl IPv4- als auch IPv6-Verbindungen zulassen.

Muss ich die hosts-Datei ändern?

NO . Ändern Sie nicht die Hosts-Datei. Der DNS-Resolver weiß, was zu tun localhostist. Eine Neudefinition hat bestenfalls keine Auswirkungen und verwirrt im schlimmsten Fall den Resolver.

Was ist --skip-name-resolve?

Dies kann das Problem auch aus einem verwandten, aber etwas anderen Grund beheben.

Ohne diese Konfigurationsoption versucht MySQL, alle IP-Adressen der Client-Verbindung über eine PTRDNS-Abfrage in einen Hostnamen aufzulösen . Wenn Ihr MySQL-Server bereits für die Verwendung von IPv6 aktiviert ist, die Verbindungen jedoch noch lange dauern, liegt dies möglicherweise daran, dass der Reverse-DNS ( PTR) -Datensatz nicht richtig konfiguriert ist.

Das Deaktivieren der Namensauflösung behebt dieses Problem, hat jedoch andere Konsequenzen, insbesondere, dass alle Zugriffsberechtigungen, die für die Verwendung eines DNS-Namens in der HostBedingung konfiguriert sind, jetzt fehlschlagen.

In diesem Fall müssen Sie alle Ihre Berechtigungen so konfigurieren, dass IP-Adressen anstelle von Namen verwendet werden.

DaveRandom
quelle
2
Endlich! Danke Danke! Endlich fand ich eine richtige, vollständige und klare Erklärung aller Ursachen, Lösungsmöglichkeiten und deren Gegenanzeigen. Du solltest ein Buch darüber schreiben!
tobia.zanarella
4
Ich hatte eine Verzögerung von 1 Sekunde, um eine Verbindung zu MySQL auf localhost herzustellen, bis ich die Bindungsadresse in änderte ::1. Leider ::ffff:127.0.0.1gab es mir weiterhin eine Verzögerung von 1 Sekunde (unabhängig von der Verwendung skip-name-resolveoder nicht), irgendwelche Ideen warum? (unter Windows 8.1)
Simon East
1
@Simon Keine Ahnung, aber der erste Schritt zum Debuggen besteht darin, zu versuchen, eine direkte Verbindung zum IPv6-Loopback und zum IPv4-Loopback herzustellen, indem die expliziten Adressen verwendet werden, um zu überprüfen, ob MySQL tatsächlich auf beiden Stacks empfangsbereit und verbindbar ist, und von dort aus zu debuggen .
DaveRandom
1
Ja, :: ffff: 127.0.0.1 funktioniert nicht ...
Raheel Hasan
Wenn ich es mit :: 1 verbinde, funktioniert Sqlyog nicht ... was tun?
Raheel Hasan
13

Normalerweise sind die Verbindungen zu MySQL, wenn IPv6 auf dem Server aktiviert localhostist, extrem langsam.

Ändern Sie die MySQL-Serveradresse im Skript, um 127.0.0.1das Problem zu beheben.

Doug
quelle
+1 Das war die richtige Antwort für mich. Ich vermute, in Windows 8 haben sie die Auflösung localhostauf den DNS-Resolver verschoben, aus dem Grund, dass @DaveRandom mit folgendem Link verbunden ist: serverfault.com/questions/4689/…
wwarren
Es hat bei mir funktioniert: D
FosAvance
Dies kann für andere Server als passieren localhost. Ich hatte das gleiche Verzögerungsproblem für eine Serveradresse auf dem Formular xx.xxxx.xxxxx.xxxxx.com. Nachdem ich den Servernamen in seine IP-Adresse geändert hatte, war das Problem behoben.
Gruber
1
mysql_connect("localhost", "root", "");

Nun, es ist ziemlich offensichtlich, woran das liegt. PHP ist in einigen Dingen wirklich gut, kann aber 'localhost' nicht direkt in '127.0.0.1' übersetzen. Sie müssen dies versuchen, da dies die Ladezeit Ihrer gesamten Website-Seite erheblich verkürzt, da PHP daran gehindert wird, Ihre HOSTS-Datei zu überprüfen und die tatsächliche IP-Adresse hinter "localhost" zu ermitteln.

Xesau
quelle
Ich kann nicht herausfinden, was Sie vorschlagen, das Problem ist.
Kasperd
@kasperd DNS Auflösung von 'localhost'
Xesau
note from '17 - mysql_ * funktionen sind jetzt veraltet und in php7
treyBake entfernt.
0

Sie können die Verlangsamung der Abfrage auch beseitigen, indem Sie eine kleine Änderung an Ihrer Datenbankverbindungsvariablen vornehmen (die sich aus Gründen der Portabilität hoffentlich in einer von Ihren Skripten getrennten Datei befindet). Ändern Sie den Host-Wert in "127.0.0.1" anstelle von "localhost". Dadurch wird die lange DNS-Suche nach localhost umgangen.

Hoffe das hilft!

Billman64
quelle