La connexion à MySQL depuis PHP est extrêmement lente

19

Je viens de faire une nouvelle installation de XAMPP. Lors de la première ouverture de PHPMyAdmin, j'ai remarqué que c'était extrêmement lent. Cela n'avait aucun sens que sur localhost, l'ouverture de chaque page devrait prendre près de 5 secondes. J'ai fait un petit test pour décourager 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);

Le script ci-dessus prend environ 3 secondes à exécuter (bien qu'il ait fallu près de 8 secondes pour se charger la première fois que je l'ai exécuté.)

Ensuite, pour vérifier si c'était la faute de PDO, j'ai essayé d'utiliser à la mysql_connectplace:

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

Prend exactement autant de temps pour terminer.

Je pensais que c'était la faute de PHP au début, mais le code PHP et les fichiers statiques sont plus rapides que je ne peux cliquer sur Actualiser. J'ai testé PHP en exécutant ce petit script:

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

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

5000 sha1calculs et la page est toujours plus vivante que je ne peux rafraîchir ma fenêtre.

Ensuite, j'ai pensé que c'était la faute de MySQL. Mais encore une fois, il n'a pas fallu beaucoup de tests pour comprendre que MySQL fonctionne plus rapidement que je n'en ai besoin. En utilisant le client CLI MySQL, la requête de sélection de l'utilisateur ne prend même pas de temps mesurable - c'est fait avant même d'avoir laissé la touche de retour enfoncée.

Le problème doit être la connexion de PHP à MySQL - c'est pour autant que j'ai pu raisonner. Je peux trouver des tonnes de choses sur PHP étant lent ou MySQL lent, mais rien sur PHP + MySQL étant extrêmement lent.

Merci à tous ceux qui peuvent m'aider à résoudre ce problème!


J'utilise XAMPP 1.8.0 pour win32 ( Lien de téléchargement )
Version PHP: 5.4.4
Version MySQL: 14.14


EDIT: Après le chronométrage, il s'avère que c'est la fonction de connexion qui prend si longtemps:

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

Production:

Temps de connexion: 1,006148
Temps de requête: 0,000247

Qu'est-ce qui peut faire que PHP passe beaucoup de temps à se connecter à la base de données? Le client CLI, HeidiSQL et MySQL Workbench se connectent instantanément

Hubro
la source
php -m output please
thinice
@thinice: pastebin.com/4dXe7ZDa
Hubro

Réponses:

17

Se peut-il que votre mysql essaie d'exécuter la requête rev-dns chaque fois que vous vous connectez? essayez d'ajouter à my.cnf, section mysqld: skip-nom-résoudre .

pQd
la source
Curieusement, PHPMyAdmin et le client CLI MySQL me donnent maintenant "L'hôte '127.0.0.1' n'est pas autorisé à se connecter à ce serveur MySQL". Pour une raison quelconque, le script PHP fonctionne toujours, mais tout aussi lentement qu'auparavant
Hubro
les «grosses applications» pour gérer mysql - comme mysql workbench - sont-elles aussi lentes?
pQd
L'exécution de la même requête à partir de MySQL Workbench est tout aussi rapide que le client CLI, tout comme HeidiSQL
Hubro
ajoutez du timing dans php et vérifiez s'il se connecte ou exécute la requête qui prend beaucoup de temps.
pQd
Merci pour ce commentaire, j'ai mis à jour ma question. La connexion et la requête sont super rapides
Hubro
30

Ceci est pris presque mot pour mot de ma réponse ici , mais je sais que nous fronçons les sourcils sur les réponses uniquement sur SO donc j'imagine que vous le faites aussi :-)

Si vous rencontrez ce problème et utilisez une version de Windows antérieure à Windows 7, ce n'est probablement pas la réponse à votre problème.

Pourquoi cela arrive-t-il?

La cause de ce problème est IPv4 vs IPv6.

Lorsque vous utilisez un nom d'hôte au lieu d'une adresse IP, le client MySQL exécute d'abord une AAAArecherche d'hôte (IPv6) pour le nom et essaie d'abord cette adresse s'il réussit à résoudre le nom en adresse IPv6. Si l'une des étapes échoue (résolution de nom ou connexion), elle reviendra à IPv4, exécutant une Arecherche et essayant cet hôte à la place.

En pratique, cela signifie que si la localhostrecherche IPv6 réussit mais que MySQL n'est pas lié au bouclage IPv6, vous devrez attendre un cycle d'expiration de connexion avant que le repli IPv4 ne se produise et que la connexion réussisse.

Ce n'était pas un problème avant Windows 7, car la localhostrésolution était effectuée via le fichier hosts, et il était préconfiguré uniquement 127.0.0.1- il n'était pas fourni avec son homologue IPv6 ::1.

Depuis Windows 7, cependant, la localhostrésolution est intégrée au résolveur DNS, pour les raisons décrites ici . Cela signifie que la recherche IPv6 va maintenant réussir - mais MySQL n'est pas lié à cette adresse IPv6, donc la connexion échouera et vous verrez le délai décrit dans cette question.

C'est zonte. Dites-moi simplement comment y remédier déjà!

Vous avez quelques options. En regardant autour d'Internet, la «solution» générale semble être d'utiliser explicitement l'adresse IP au lieu du nom, mais il y a quelques raisons de ne pas le faire, toutes deux liées à la portabilité, sans doute sans importance:

  • Si vous déplacez votre script vers une autre machine qui ne prend en charge que IPv6, votre script ne fonctionnera plus.

  • Si vous déplacez votre script vers un environnement d'hébergement basé sur * nix, la chaîne magique localhostsignifierait que le client MySQL préférerait utiliser une socket Unix si une est configurée, c'est plus efficace que la connectivité basée sur le bouclage IP

Ils semblent assez importants cependant?

Ils ne le sont pas. Vous devez concevoir votre application pour que ce genre de chose soit défini dans un fichier de configuration. Si vous déplacez votre script vers un autre environnement, il est probable que d'autres choses devront également être configurées.

En résumé, l'utilisation de l'adresse IP n'est pas la meilleure solution, mais elle est probablement acceptable.

Quelle est donc la meilleure solution?

La meilleure façon serait de changer l'adresse de liaison utilisée par le serveur MySQL. Cependant, ce n'est pas aussi simple qu'on pourrait le souhaiter. Contrairement à Apache, Nginx et à presque toutes les autres applications de service réseau sensées jamais créées, MySQL ne prend en charge qu'une seule adresse de liaison, il ne s'agit donc pas simplement d'en ajouter une autre. Heureusement, les systèmes d'exploitation prennent en charge un peu de magie ici, nous pouvons donc permettre à MySQL d'utiliser simultanément IPv4 et IPv6.

Vous devez exécuter MySQL 5.5.3 ou une version ultérieure, et vous devez démarrer MySQL avec l' --bind-address=argument de ligne de commande. Vous avez 4 documents d' options , selon ce que vous voulez faire:

  • Celui que vous connaissez probablement, et celui que vous êtes le plus susceptible (efficace) en utilisant 0.0.0.0. Cela se lie à toutes les adresses IPv4 disponibles sur la machine. En fait, ce n'est probablement pas la meilleure chose à faire même si vous ne vous souciez pas d'IPv6, car il souffre des mêmes risques de sécurité que ::.

  • Une adresse IPv4 ou IPv6 explicite (par exemple 127.0.0.1ou ::1pour le bouclage). Cela lie le serveur à cette adresse et uniquement à cette adresse.

  • La chaîne magique ::. Cela liera MySQL à chaque adresse de la machine, à la fois les adresses de bouclage et les interfaces physiques, en mode IPv4 et IPv6. C'est potentiellement un risque pour la sécurité, ne le faites que si vous avez besoin de MySQL pour accepter les connexions des hôtes distants.

  • Utilisez une adresse IPv6 mappée IPv4 . Il s'agit d'un mécanisme spécial intégré à IPv6 pour une compatibilité descendante pendant la transition 4 -> 6, et il vous permet de vous lier à une adresse IPv4 spécifique et à son équivalent IPv6. Il est peu probable que cela vous soit utile pour autre chose que l'adresse de "double bouclage" ::ffff:127.0.0.1. Il s'agit probablement de la meilleure solution pour la plupart des gens, ne se liant qu'au bouclage mais autorisant les connexions IPv4 et IPv6.

Dois-je modifier le fichier hosts?

Non . Ne modifiez pas le fichier hosts. Le résolveur DNS sait quoi faire localhost, le redéfinir n'aura au mieux aucun effet, et au pire confondra l'enfer du résolveur.

Et alors --skip-name-resolve?

Cela peut également résoudre le problème, pour une raison connexe mais légèrement différente.

Sans cette option de configuration, MySQL tentera de résoudre toutes les adresses IP de connexion client en un nom d'hôte via une PTRrequête DNS. Si votre serveur MySQL est déjà activé pour utiliser IPv6 mais que les connexions prennent encore du temps, cela peut être dû au fait que l'enregistrement DNS inversé ( PTR) n'est pas correctement configuré.

La désactivation de la résolution de noms résoudra ce problème, mais elle a d'autres ramifications, notamment que toutes les autorisations d'accès configurées pour utiliser un nom DNS dans la Hostcondition échouent désormais.

Si vous comptez le faire, vous devrez configurer toutes vos autorisations pour utiliser des adresses IP au lieu de noms.

DaveRandom
la source
2
Finalement! Merci merci! J'ai enfin trouvé une explication correcte, complète et claire de toutes les causes, des solutions possibles et de leurs contre-indications. Vous devriez écrire un livre à ce sujet!
tobia.zanarella
4
J'avais un délai d'une seconde pour me connecter à MySQL sur localhost jusqu'à ce que je change l'adresse de liaison en ::1. Malheureusement, a ::ffff:127.0.0.1continué de me donner un délai d'une seconde (indépendamment de l'utilisation skip-name-resolveou non), des idées pourquoi? (sous Windows 8.1)
Simon East
1
@Simon Pas un indice sans regarder, mais la première étape du débogage serait d'essayer de se connecter à la fois au bouclage IPv6 et au bouclage IPv4 directement en utilisant les adresses explicites pour vérifier que MySQL est réellement à l'écoute et connectable sur les deux piles, et débogage à partir de là .
DaveRandom
1
oui, :: ffff: 127.0.0.1 ne fonctionne pas ...
Raheel Hasan
Si je le lie avec :: 1, Sqlyog ne fonctionne pas ... que faire ??
Raheel Hasan
13

Habituellement, lorsque IPv6 est activé dans les connexions serveur à MySQL, l'utilisation localhostest extrêmement lente.

Changer l'adresse du serveur mysql dans le script pour 127.0.0.1résoudre le problème.

Doug
la source
+1, c'était la bonne réponse pour moi. Je suppose que dans Windows 8, ils ont déplacé la résolution du localhostrésolveur DNS pour la raison que @DaveRandom lié à: serverfault.com/questions/4689/…
wwarren
Cela a fonctionné pour moi: D
FosAvance
Cela peut arriver pour d'autres serveurs que localhost. J'ai eu le même problème de délai pour une adresse de serveur sur le formulaire xx.xxxx.xxxxx.xxxxx.com. Une fois que j'ai changé le nom du serveur en son adresse IP, le problème a disparu.
Gruber
1
mysql_connect("localhost", "root", "");

Eh bien, la raison en est assez évidente. PHP est vraiment bon à certains égards, mais pas à traduire directement «localhost» en «127.0.0.1». Vous devez essayer cela, cela réduira vraiment le temps de chargement global de votre page Web, car il empêche PHP de vérifier votre fichier HOSTS et ce qu'il ne fait pas pour obtenir la véritable adresse IP derrière «localhost»

Xesau
la source
Je ne peux pas comprendre ce que vous essayez de suggérer.
kasperd
@kasperd Résolution DNS de 'localhost'
Xesau
note de '17 - les fonctions mysql_ * sont obsolètes maintenant et supprimées dans php7
treyBake
0

Vous pouvez également éliminer le ralentissement des requêtes en apportant un petit ajustement à votre variable de connexion db (qui, espérons-le, se trouve dans un fichier distinct de vos scripts pour la portabilité). Modifiez la valeur d'hôte en "127.0.0.1" au lieu de "localhost". Cela contourne la longue recherche DNS pour localhost.

J'espère que cela t'aides!

Billman64
la source