Conectarse a MySQL desde PHP es extremadamente lento

19

Acabo de hacer una nueva instalación de XAMPP. Cuando abrí PHPMyAdmin por primera vez, noté que era extremadamente lento. No tenía sentido que en localhost debería tomar casi 5 segundos para que cada página se abra. Hice un pequeño caso de prueba para quitarle la culpa a 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);

El script anterior tarda aproximadamente 3 segundos en ejecutarse (aunque tardó más de 8 segundos en cargarse la primera vez que lo ejecuté).

Luego, para verificar si fue culpa de PDO, intenté usar en su mysql_connectlugar:

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

Toma exactamente el mismo tiempo para terminar.

Al principio pensé que era culpa de PHP, pero el código PHP y los archivos estáticos se sirven más rápido de lo que puedo hacer clic en actualizar. Probé PHP ejecutando este pequeño script:

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

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

5000 sha1cálculos y la página todavía se muestra más ágil de lo que puedo actualizar mi ventana.

Entonces pensé que era culpa de MySQL. Pero, de nuevo, no tomó muchas pruebas para darse cuenta de que MySQL funciona más rápido de lo que lo necesito. Al usar el cliente CLI de MySQL, la consulta de selección del usuario ni siquiera toma un tiempo medible, se hace antes de que incluso deje que se active la tecla de retorno.

El problema debe ser la conexión de PHP a MySQL, eso es lo que he podido razonar. Puedo encontrar toneladas de cosas sobre PHP lento o MySQL lento, pero nada sobre PHP + MySQL extremadamente lento.

¡Gracias a cualquiera que pueda ayudarme a resolver esto!


Estoy usando XAMPP 1.8.0 para win32 ( enlace de descarga )
Versión de PHP: 5.4.4
Versión de MySQL: 14.14


EDITAR: después del tiempo, resulta que es la función de conexión que está tardando tanto:

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

Salida:

Tiempo de conexión: 1.006148
Tiempo de consulta: 0.000247

¿Qué puede hacer que PHP pase mucho tiempo conectándose a la base de datos? El cliente CLI, el banco de trabajo HeidiSQL y MySQL se conectan instantáneamente

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

Respuestas:

17

¿Puede ser que su mysql intente ejecutar una consulta rev-dns cada vez que se conecta? intente agregar a my.cnf, sección mysqld: skip-name-resolve .

pQd
fuente
Por extraño que parezca, tanto PHPMyAdmin como el cliente CLI de MySQL ahora me dan "Host '127.0.0.1' no puede conectarse a este servidor MySQL". Por alguna razón, el script PHP todavía funciona, pero tan lento como antes
Hubro
¿son las 'aplicaciones gordas' para administrar mysql, como mysql workbench, como lentas?
pQd
Ejecutar la misma consulta desde MySQL Workbench es tan rápido como el cliente CLI, y también lo es HeidiSQL
Hubro
agregue un poco de tiempo en php y verifique si está conectando o ejecutando la consulta que lleva mucho tiempo.
pQd
Gracias por ese comentario, actualicé mi pregunta. Tanto la conexión como la consulta son súper rápidas
Hubro
30

Esto se toma casi al pie de la letra de mi respuesta aquí , pero sé que desaprobamos las respuestas de solo enlace en SO, así que imagino que ustedes también lo hacen :-)

Si tiene este problema y usa una versión de Windows anterior a Windows 7, probablemente esta no sea la respuesta a su problema.

¿Por qué está pasando esto?

La causa de este problema es IPv4 vs IPv6.

Cuando usa un nombre de host en lugar de una dirección IP, el cliente MySQL primero ejecuta una AAAAbúsqueda de host (IPv6) para el nombre, e intenta esta dirección primero si resuelve con éxito el nombre en una dirección IPv6. Si alguno de los pasos falla (resolución de nombre o conexión), recurrirá a IPv4, ejecutará una Abúsqueda e intentará este host en su lugar.

Lo que esto significa en la práctica es que si la localhostbúsqueda de IPv6 es exitosa pero MySQL no está vinculada al loopback de IPv6, deberá esperar un ciclo de tiempo de espera de conexión antes de que ocurra el repliegue de IPv4 y la conexión sea exitosa.

Esto no era un problema anterior a Windows 7, porque la localhostresolución se realizó a través del archivo de hosts y solo estaba preconfigurada 127.0.0.1; no venía con su contraparte IPv6 ::1.

Sin embargo, desde Windows 7, la localhostresolución está integrada en el solucionador de DNS, por las razones descritas aquí . Esto significa que la búsqueda de IPv6 ahora tendrá éxito, pero MySQL no está vinculado a esa dirección IPv6, por lo que la conexión fallará y verá el retraso descrito en esta pregunta.

Eso es bueno. ¡Solo dime cómo solucionarlo ya!

Tienes pocas opciones. Al mirar alrededor de Internet, la "solución" general parece ser usar la dirección IP explícitamente en lugar del nombre, pero hay un par de razones para no hacerlo, ambas relacionadas con la portabilidad, ambas posiblemente no importantes:

  • Si mueve su script a otra máquina que solo admite IPv6, su script ya no funcionará.

  • Si mueve su script a un entorno de alojamiento basado en * nix, la cadena mágica localhostsignificaría que el cliente MySQL preferiría usar un socket Unix si está configurado, esto es más eficiente que la conectividad basada en IP loopback

¿Suenan bastante importantes?

No lo son Debería diseñar su aplicación para que este tipo de cosas se defina en un archivo de configuración. Si mueve su script a otro entorno, es probable que también sea necesario configurar otras cosas.

En resumen, el uso de la dirección IP no es la mejor solución, pero lo más probable es que sea una solución aceptable.

Entonces, ¿cuál es la mejor solución?

La mejor manera sería cambiar la dirección de enlace que utiliza el servidor MySQL. Sin embargo, esto no es tan simple como a uno le gustaría. A diferencia de Apache, Nginx y casi cualquier otra aplicación de servicio de red sensata que se haya creado, MySQL solo admite una única dirección de enlace, por lo que no se trata solo de agregar otra. Afortunadamente, los sistemas operativos admiten un poco de magia aquí, por lo que podemos habilitar MySQL para usar tanto IPv4 como IPv6 simultáneamente.

Debe ejecutar MySQL 5.5.3 o posterior, y debe iniciar MySQL con el --bind-address=argumento de la línea de comandos. Tienes 4 opciones de documentos , dependiendo de lo que quieras hacer:

  • La que probablemente está familiarizado con, y el que lo más probable es (efectivamente) usando, 0.0.0.0. Esto se une a todas las direcciones IPv4 disponibles en la máquina. Probablemente, esto no sea lo mejor, incluso si no le importa IPv6, ya que sufre los mismos riesgos de seguridad ::.

  • Una dirección IPv4 o IPv6 explícita (por ejemplo, 127.0.0.1o ::1para loopback). Esto une el servidor a esa dirección y solo a esa dirección.

  • La cuerda mágica ::. Esto vinculará MySQL a cada dirección de la máquina, tanto las direcciones de interfaz física como de bucle invertido, en modo IPv4 e IPv6. Esto es potencialmente un riesgo de seguridad, solo haga esto si necesita que MySQL acepte conexiones de hosts remotos.

  • Use una dirección IPv6 asignada a IPv4 . Este es un mecanismo especial integrado en IPv6 para la compatibilidad con versiones anteriores durante la transición 4 -> 6, y le permite vincularse a una dirección IPv4 específica y es equivalente a IPv6. Es poco probable que esto le sea útil para otra cosa que no sea la dirección de "doble loopback" ::ffff:127.0.0.1. Esta es probablemente la mejor solución para la mayoría de las personas, ya que solo se vincula al loopback pero permite conexiones IPv4 e IPv6.

¿Necesito modificar el archivo de hosts?

NO . No modifique el archivo de hosts. El solucionador de DNS sabe qué hacer localhost, redefinirlo en el mejor de los casos no tendrá ningún efecto y, en el peor de los casos, confundirá al resolutor.

¿Qué hay de --skip-name-resolve?

Esto también puede solucionar el problema, por una razón relacionada pero ligeramente diferente.

Sin esta opción de configuración, MySQL intentará resolver todas las direcciones IP de conexión del cliente a un nombre de host mediante una PTRconsulta DNS. Si su servidor MySQL ya está habilitado para usar IPv6 pero las conexiones aún están tardando mucho, puede ser porque el PTRregistro DNS ( ) inverso no está configurado correctamente.

Deshabilitar la resolución de nombre solucionará este problema, pero tiene otras ramificaciones, en particular que cualquier permiso de acceso configurado para usar un nombre DNS en la Hostcondición ahora fallará.

Si va a hacer esto, deberá configurar todas sus concesiones para usar direcciones IP en lugar de nombres.

DaveRandom
fuente
2
¡Finalmente! ¡Gracias Gracias! Finalmente encontré una explicación adecuada, completa y clara de todas las causas, posibles soluciones y sus contraindicaciones. ¡Deberías escribir un libro sobre eso!
tobia.zanarella
44
Estaba teniendo un retraso de 1 segundo para conectarme a MySQL en localhost hasta que cambié la dirección de enlace a ::1. Lamentablemente ::ffff:127.0.0.1continuó dándome un retraso de 1 segundo (independientemente de si lo usó skip-name-resolveo no), ¿alguna idea de por qué? (en Windows 8.1)
Simon East
1
@Simon No es una pista sin mirar, pero el primer paso para depurar sería intentar conectarse tanto al bucle invertido IPv6 como al bucle invertido IPv4 directamente usando las direcciones explícitas para verificar que MySQL realmente está escuchando y conectando en ambas pilas, y depurando desde allí .
DaveRandom
1
sí, :: ffff: 127.0.0.1 no funciona ...
Raheel Hasan
Si lo ato con :: 1, Sqlyog no funciona ... ¿qué hacer?
Raheel Hasan
13

Por lo general, cuando IPv6 está habilitado en el servidor, las conexiones a MySQL localhostson extremadamente lentas.

Cambiar la dirección del servidor mysql en el script para 127.0.0.1resolver el problema.

Doug
fuente
+1 esta fue la respuesta correcta para mí. Supongo que en Windows 8 trasladaron la resolución localhosta la resolución DNS por la razón que @DaveRandom se vinculó a: serverfault.com/questions/4689/…
wwarren
Funcionó para mí: D
FosAvance
Esto puede suceder para otros servidores que no sean localhost. Tuve el mismo problema de retraso para una dirección de servidor en el formulario xx.xxxx.xxxxx.xxxxx.com. Una vez que cambié el nombre del servidor a su dirección IP, el problema desapareció.
Gruber
1
mysql_connect("localhost", "root", "");

Bueno, es bastante obvio cuál es la razón. PHP realmente bueno en algunas cosas, pero no en la traducción directa de 'localhost' a '127.0.0.1'. Debe intentarlo, realmente reducirá el tiempo de carga general de la página del sitio web, ya que impide que PHP verifique su archivo HOSTS y qué no está haciendo para obtener la dirección IP real detrás de 'localhost'

Xesau
fuente
No puedo entender qué estás tratando de sugerir que es el problema.
kasperd
@kasperd DNS resolviendo 'localhost'
Xesau
Nota de '17 - mysql_ * funciones están en desuso y ahora eliminados en php7
treyBake
0

También puede eliminar la desaceleración de la consulta haciendo un pequeño ajuste a su variable de conexión db (que se espera que esté en un archivo separado de sus scripts para la portabilidad). Cambie el valor del host a "127.0.0.1" en lugar de "localhost". Esto evita la larga búsqueda de DNS para localhost.

¡Espero que esto ayude!

Billman64
fuente