La connessione a MySQL da PHP è estremamente lenta


19

Ho appena fatto una nuova installazione di XAMPP. Alla prima apertura di PHPMyAdmin ho notato che era estremamente lento. Non ha senso che su localhost siano necessari quasi 5 secondi per l'apertura di ogni pagina. Ho realizzato un piccolo test per spostare la colpa su 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);

Lo script sopra richiede solo circa 3 secondi per essere eseguito (anche se ci sono voluti circa 8 secondi per caricare la prima volta che l'ho eseguito.)

Quindi per verificare se era colpa del PDO ho provato a usare mysql_connectinvece:

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

Richiede esattamente il tempo necessario per terminare.

All'inizio ho pensato che fosse colpa di PHP, ma il codice PHP e i file statici sono più veloci di quanto io possa fare clic su Aggiorna. Ho testato PHP eseguendo questo piccolo script:

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

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

5000 sha1calcoli e la pagina è ancora visualizzata più veloce di quanto possa aggiornare la mia finestra.

Poi ho pensato che fosse colpa di MySQL. Ma ancora una volta, non ci sono voluti molti test per capire che MySQL funziona più velocemente di quanto ne abbia bisogno. Utilizzando il client dell'interfaccia della riga di comando di MySQL, la query di selezione dell'utente non richiede nemmeno tempo misurabile, ma viene eseguita anche prima che io abbia rilasciato la chiave di ritorno.

Il problema deve essere la connessione di PHP a MySQL, per quanto ho potuto ragionare. Posso trovare tonnellate di cose su PHP che è lento o MySQL che è lento, ma nulla su PHP + MySQL è estremamente lento.

Grazie a chiunque mi possa aiutare a risolvere questo problema!


Sto usando XAMPP 1.8.0 per win32 ( Download link )
Versione PHP: 5.4.4
Versione MySQL: 14.14


EDIT: dopo il cronometraggio, si scopre che è la funzione di connessione che impiega così tanto tempo:

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

Produzione:

Tempo di connessione: 1.006148
Tempo di query: 0.000247

Cosa può spendere PHP a dedicare molto tempo alla connessione al database? Il client CLI, HeidiSQL e MySQL workbench si connettono all'istante


php -m output per favore
thinice

Risposte:


17

Può essere che il tuo mysql tenti di eseguire la query rev-dns ogni volta che ti connetti? prova ad aggiungere a my.cnf, sezione mysqld: skip-name-resolve .


Stranamente sia PHPMyAdmin che il client CLI MySQL ora mi danno "L'host '127.0.0.1' non è autorizzato a connettersi a questo server MySQL". Per qualche ragione lo script PHP funziona ancora, ma è lento quanto prima
Hubro,

le "applicazioni fat" per gestire mysql - come mysql workbench - sono lente?
pQd

Eseguire la stessa query da MySQL Workbench è veloce quanto il client CLI, così come HeidiSQL
Hubro

aggiungi dei tempi in php e controlla se si sta connettendo o eseguendo la query che richiede molto tempo.
pQd

Grazie per quel commento, ho aggiornato la mia domanda. Sia la connessione che la query sono super veloci
Hubro,

30

Questo è preso quasi alla lettera dalla mia risposta qui , ma so che aggrottiamo le sopracciglia solo su link, quindi immagino che facciate anche voi ragazzi :-)

Se stai riscontrando questo problema e stai utilizzando una versione di Windows precedente a Windows 7, questa probabilmente non è la risposta al tuo problema.

Perché sta succedendo?

La causa di questo problema è IPv4 vs IPv6.

Quando si utilizza un nome host anziché un indirizzo IP, il client MySQL esegue innanzitutto una AAAAricerca host (IPv6) per il nome e prova prima questo indirizzo se risolve correttamente il nome in un indirizzo IPv6. Se uno dei due passaggi ha esito negativo (risoluzione dei nomi o connessione), verrà eseguito il fallback a IPv4, eseguendo una Aricerca e provando invece questo host.

Ciò significa in pratica che se la localhostricerca IPv6 ha esito positivo ma MySQL non è associato al loopback IPv6, sarà necessario attendere un ciclo di timeout della connessione prima che si verifichi il fallback IPv4 e la connessione abbia esito positivo.

Non si trattava di un problema precedente a Windows 7, poiché la localhostrisoluzione veniva eseguita tramite il file hosts ed era preconfigurata solo con 127.0.0.1- non veniva fornita con la sua controparte IPv6 ::1.

Da Windows 7, tuttavia, la localhostrisoluzione è integrata nel resolver DNS, per i motivi descritti qui . Ciò significa che la ricerca IPv6 ora avrà esito positivo, ma MySQL non è associato a quell'indirizzo IPv6, quindi la connessione fallirà e vedrai il ritardo delineato in questa domanda.

Bello. Dimmi solo come risolverlo già!

Hai alcune opzioni. Guardando in giro per Internet, la "soluzione" generale sembra essere quella di utilizzare esplicitamente l'indirizzo IP anziché il nome, ma ci sono un paio di ragioni per non farlo, sia legate alla portabilità, sia probabilmente non importanti:

  • Se sposti lo script su un altro computer che supporta solo IPv6, lo script non funzionerà più.

  • Se sposti lo script in un ambiente di hosting basato su * nix, la stringa magica localhostsignificherebbe che il client MySQL preferirebbe utilizzare un socket Unix se configurato, questo è più efficiente della connettività basata su loopback IP

Sembrano piuttosto importanti però?

Non lo sono. Dovresti progettare la tua applicazione in modo che questo genere di cose sia definito in un file di configurazione. Se sposti lo script in un altro ambiente, è probabile che anche altre cose debbano essere configurate.

In sintesi, l'uso dell'indirizzo IP non è la soluzione migliore , ma è molto probabilmente accettabile.

Quindi qual è la soluzione migliore?

Il modo migliore sarebbe cambiare l'indirizzo di bind utilizzato dal server MySQL. Tuttavia, questo non è così semplice come si potrebbe desiderare. A differenza di Apache, Nginx e quasi ogni altra applicazione di servizio di rete sana mai realizzata, MySQL supporta solo un singolo indirizzo di bind, quindi non si tratta solo di aggiungerne un altro. Fortunatamente, i sistemi operativi supportano un po 'di magia qui, quindi possiamo consentire a MySQL di utilizzare contemporaneamente sia IPv4 che IPv6.

Devi eseguire MySQL 5.5.3 o versioni successive e avviare MySQL con l' --bind-address=argomento della riga di comando. Hai 4 documenti opzionali , a seconda di cosa vuoi fare:

  • Quello con cui probabilmente hai familiarità e quello che molto probabilmente (effettivamente) stai usando 0.0.0.0,. Questo si lega a tutti gli indirizzi IPv4 disponibili sulla macchina. Questa in realtà non è probabilmente la cosa migliore da fare anche se non ti interessa IPv6, poiché subisce gli stessi rischi di sicurezza di ::.

  • Un indirizzo IPv4 o IPv6 esplicito (ad esempio 127.0.0.1o ::1per loopback). Ciò vincola il server a quell'indirizzo e solo a quell'indirizzo.

  • La corda magica ::. Ciò vincolerà MySQL a tutti gli indirizzi sulla macchina, sia quelli di loopback che quelli di interfaccia fisica, in modalità IPv4 e IPv6. Questo è potenzialmente un rischio per la sicurezza, fallo solo se hai bisogno di MySQL per accettare connessioni da host remoti.

  • Utilizzare un indirizzo IPv6 mappato IPv4 . Questo è un meccanismo speciale incorporato in IPv6 per la compatibilità con le versioni precedenti durante la transizione 4 -> 6 e ti consente di associare un indirizzo IPv4 specifico ed è equivalente a IPv6. È molto improbabile che ciò ti sia utile per qualcosa di diverso dall'indirizzo "dual loopback" ::ffff:127.0.0.1. Questa è probabilmente la soluzione migliore per la maggior parte delle persone, vincolando solo al loopback ma consentendo entrambe le connessioni IPv4 e IPv6.

Devo modificare il file hosts?

NO . Non modificare il file hosts. Il risolutore DNS sa cosa fare localhost, ridefinirlo nella migliore delle ipotesi non avrà alcun effetto e, nella peggiore delle ipotesi, confondere il risolutore.

Che dire --skip-name-resolve?

Ciò può anche risolvere il problema, per un motivo correlato ma leggermente diverso.

Senza questa opzione di configurazione, MySQL tenterà di risolvere tutti gli indirizzi IP di connessione client in un nome host tramite una PTRquery DNS. Se il tuo server MySQL è già abilitato all'uso di IPv6 ma le connessioni impiegano ancora molto tempo, è possibile che il record DNS ( PTR) inverso non sia configurato correttamente.

La disabilitazione della risoluzione dei nomi risolverà questo problema, ma ha altre ramificazioni, in particolare che eventuali autorizzazioni di accesso configurate per utilizzare un nome DNS nella Hostcondizione ora falliranno.

Se hai intenzione di farlo, dovrai configurare tutte le tue sovvenzioni per usare indirizzi IP anziché nomi.


2
Infine! Grazie grazie! Alla fine ho trovato una spiegazione corretta, completa e chiara di tutte le cause, le possibili soluzioni e le loro controindicazioni. Dovresti scrivere un libro a riguardo!
tobia.zanarella,

4
Avevo un ritardo di 1 secondo per la connessione a MySQL su localhost fino a quando non ho cambiato l'indirizzo di bind ::1. Purtroppo ha ::ffff:127.0.0.1continuato a darmi un ritardo di 1 secondo (indipendentemente dall'uso skip-name-resolveo meno), qualche idea sul perché? (su Windows 8.1)
Simon East

1
@Simon Non è un indizio senza guardare, ma il primo passo per il debug sarebbe quello di provare a connettersi sia al loopback IPv6 che al loopback IPv4 usando direttamente gli indirizzi espliciti per verificare che MySQL sia effettivamente in ascolto e collegabile su entrambi gli stack, e il debug da lì .
DaveRandom,

1
sì, :: ffff: 127.0.0.1 non funziona ...
Raheel Hasan,

Se lo associo a :: 1, Sqlyog non funziona ... cosa fare ??
Raheel Hasan,

13

Di solito quando IPv6 è abilitato nelle connessioni del server a MySQL usando localhostsono estremamente lente.

La modifica dell'indirizzo del server mysql nello script per 127.0.0.1risolvere il problema.


+1 questa è stata la risposta corretta per me. Immagino che in Windows 8 abbiano spostato la risoluzione del localhostrisolutore DNS per il motivo che @DaveRandom si è collegato a: serverfault.com/questions/4689/…
wwarren

Ha funzionato per me: D
FosAvance,

Questo può accadere per server diversi da localhost. Ho avuto lo stesso problema di ritardo per un indirizzo del server nel modulo xx.xxxx.xxxxx.xxxxx.com. Una volta modificato il nome del server con il suo indirizzo IP, il problema era scomparso.
Gruber,

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

Bene, è abbastanza ovvio quale sia il motivo. PHP è veramente bravo in alcune cose, ma non nel tradurre direttamente "localhost" in "127.0.0.1". Devi provarlo, ridurrà davvero il tempo complessivo di caricamento della pagina del tuo sito web, perché trattiene PHP dal controllare il tuo file HOSTS e cosa non sta facendo per ottenere il vero indirizzo IP dietro 'localhost'


Non riesco a capire cosa stai cercando di suggerire che il problema sia.
Kasperd,

@kasperd Risoluzione DNS di 'localhost'
Xesau

nota da '17 - le funzioni mysql_ * sono ora obsolete e rimosse in php7
treyBake


0

È inoltre possibile eliminare il rallentamento della query apportando una piccola modifica alla variabile di connessione db (che si spera sia in un file separato dagli script per la portabilità). Modificare il valore host su "127.0.0.1" anziché "localhost". Ciò ignora la lunga ricerca DNS per localhost.

Spero che sia di aiuto!

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.