web-dev-qa-db-fra.com

Connexion à MySQL depuis PHP est extrêmement lent

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 plus de 8 secondes pour le 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 mysql_connect au lieu:

$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 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 sha1 calculs et la page est toujours plus vivante que je 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 - elle est effectuée avant même d'avoir laissé la touche 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 étant 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

20
Hubro

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-name-resolution .

17
pQd

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 de lien 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 se produit-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 recherche d'hôte AAAA (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 recherche A et essayant cet hôte à la place.

En pratique, cela signifie que si la recherche IPv6 localhost 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 résolution localhost a été effectuée via le fichier hosts, et elle était préconfigurée avec uniquement 127.0.0.1 - il n'est pas venu avec son homologue IPv6 ::1.

Depuis Windows 7, cependant, la résolution localhost 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 sympa. 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 l'adresse IP explicitement 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 seulement prend en charge 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 localhost signifierait 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?

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 solution meilleure, mais elle est très probablement acceptable.

Alors quelle est 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 le --bind-address= argument de ligne de commande. Vous avez 4 optionsdocuments, selon ce que vous voulez faire:

  • Celui que vous connaissez probablement et celui que vous utilisez le plus (efficacement), 0.0.0.0. Cela se lie à toutes les adresses IPv4 disponibles sur la machine. 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.1 ou ::1 pour le bouclage). Cela lie le serveur à cette adresse et seulement à 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. Ceci 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 avec localhost, le redéfinir n'aura au mieux aucun effet, et au pire confondra l'enfer du résolveur.

Qu'en est-il de --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 requête DNS PTR. 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 condition Host échoueront désormais.

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

30
DaveRandom

Habituellement, lorsque IPv6 est activé dans les connexions serveur à MySQL en utilisant localhost sont extrêmement lentes.

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

13
Doug

L'ajout de cette ligne à votre fichier d'hôtes a résolu le problème pour moi

127.0.0.1 localhost

La réponse détaillée peut être trouvée dans ce fil: https://stackoverflow.com/questions/13584360/php-with-mysql-is-slow

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

Eh bien, la raison en est assez évidente. PHP vraiment bon dans certaines choses, mais pas pour traduire directement 'localhost' en '127.0.0.1'. Vous devez essayer cela, cela réduira vraiment le temps de chargement de la page de votre site Web, car il contient PHP retour de la vérification de votre fichier HOSTS et de ce qu'il ne fait pas pour obtenir la véritable adresse IP derrière 'localhost'

1
Xesau

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

J'espère que cela t'aides!

0
Billman64