Conectar-se ao MySQL a partir do PHP é extremamente lento

19

Acabei de fazer uma nova instalação do XAMPP. Quando abri o PHPMyAdmin pela primeira vez, notei que era extremamente lento. Não fazia sentido que no localhost levasse quase 5 segundos para cada página abrir. Fiz um pequeno caso de teste para afastar a culpa do 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);

O script acima leva apenas cerca de 3 segundos para ser executado (embora tenha levado mais de 8 segundos para carregar na primeira vez que o executei).

Em seguida, para verificar se foi culpa do PDO, tentei usar mysql_connect:

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

Leva exatamente o tempo necessário para terminar.

Eu pensei que era culpa do PHP no começo, mas o código PHP e os arquivos estáticos são servidos mais rapidamente do que posso clicar em Atualizar. Testei o PHP executando este pequeno script:

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

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

5.000 sha1cálculos e a página ainda é exibida mais rapidamente do que posso atualizar minha janela.

Então eu achei que era culpa do MySQL. Mas, novamente, não foi preciso muito teste para descobrir que o MySQL está funcionando mais rápido do que eu preciso. Usando o cliente MySQL CLI, a consulta de seleção do usuário nem leva um tempo mensurável - é feita antes mesmo de eu deixar a tecla de retorno.

O problema deve ser a conexão do PHP ao MySQL - isso é o que eu pude argumentar. Eu posso encontrar muitas coisas sobre o PHP ser lento ou o MySQL lento, mas nada sobre o PHP + MySQL extremamente lento.

Obrigado a quem puder me ajudar a resolver isso!


Estou usando o XAMPP 1.8.0 para win32 ( Link para download )
Versão do PHP: 5.4.4
Versão do MySQL: 14.14


EDIT: Após o tempo, verifica-se que é a função de conexão que está demorando 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);

Saída:

Tempo de conexão: 1.006148
Tempo da consulta: 0.000247

O que pode fazer com que o PHP gaste muito tempo se conectando ao banco de dados? O cliente CLI, HeidiSQL e MySQL Workbench se conectam instantaneamente

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

Respostas:

17

Pode ser que o seu mysql tente executar a consulta rev-dns sempre que você se conectar? tente adicionar ao my.cnf, seção mysqld: skip-name-resolve .

pQd
fonte
Por estranho que pareça, tanto o PHPMyAdmin quanto o cliente CLI do MySQL agora me fornecem "O host '127.0.0.1' não tem permissão para se conectar a este servidor MySQL". Por alguma razão, o script PHP ainda funciona, mas tão lento quanto antes
Hubro 17/07/2012
são 'aplicativos gordos' para gerenciar o mysql - como o mysql workbench - tão lento?
PQD
A execução da mesma consulta no MySQL Workbench é tão rápida quanto o cliente CLI, assim como o HeidiSQL
Hubro
adicione algum tempo no php e verifique se está conectando ou executando a consulta que leva muito tempo.
PQD
Obrigado por esse comentário, atualizei minha pergunta. Tanto a conexão e consulta é super rápido
Hubro
30

Isso foi tirado quase literalmente da minha resposta aqui , mas eu sei que nos desaprovamos em respostas somente para links no SO, então imagino que vocês também :-)

Se você está tendo esse problema e está usando uma versão do Windows anterior ao Windows 7, provavelmente esta não é a resposta para o seu problema.

Por que isso está acontecendo?

A causa desse problema é IPv4 vs IPv6.

Quando você usa um nome de host em vez de um endereço IP, o cliente MySQL executa primeiro uma AAAApesquisa de host (IPv6) para o nome e tenta esse endereço primeiro se resolver com êxito o nome para um endereço IPv6. Se uma das etapas falhar (resolução de nome ou conexão), ela retornará ao IPv4, executando uma Apesquisa e tentando este host.

O que isso significa na prática é que, se a localhostpesquisa do IPv6 for bem-sucedida, mas o MySQL não estiver vinculado ao loopback do IPv6, será necessário aguardar um ciclo de tempo limite da conexão antes que ocorra o fallback do IPv4 e a conexão tenha êxito.

Isso não foi um problema anterior ao Windows 7, porque a localhostresolução foi feita através do arquivo hosts e veio pré-configurada apenas 127.0.0.1- ela não veio com sua contraparte do IPv6 ::1.

Desde o Windows 7, no entanto, a localhostresolução está incorporada no resolvedor de DNS, pelos motivos descritos aqui . Isso significa que a pesquisa do IPv6 agora terá êxito - mas o MySQL não está vinculado a esse endereço IPv6, portanto a conexão falhará e você verá o atraso descrito nesta pergunta.

Isso é bom. Apenas me diga como consertar já!

Você tem poucas opções. Olhando pela Internet, a "solução" geral parece ser o uso explícito do endereço IP, e não o nome, mas existem algumas razões para não fazer isso, tanto relacionadas à portabilidade, quanto sem dúvida importantes:

  • Se você mover seu script para outra máquina que suporte apenas IPv6, seu script não funcionará mais.

  • Se você mover seu script para um ambiente de hospedagem baseado em * nix, a string mágica localhostsignificaria que o cliente MySQL preferiria usar um soquete Unix se um estiver configurado, isso é mais eficiente que a conectividade baseada em loopback IP

Eles parecem muito importantes?

Eles não são. Você deve projetar seu aplicativo para que esse tipo de coisa seja definido em um arquivo de configuração. Se você mover seu script para outro ambiente, é provável que outras coisas também precisem ser configuradas.

Em resumo, usar o endereço IP não é a melhor solução, mas é provavelmente uma solução aceitável.

Então, qual é a melhor solução?

A melhor maneira seria alterar o endereço de ligação que o servidor MySQL usa. No entanto, isso não é tão simples quanto se pode gostar. Ao contrário do Apache, Nginx e quase todos os outros aplicativos de serviços de rede sãos já criados, o MySQL suporta apenas um único endereço de ligação, portanto, não é apenas o caso de adicionar outro. Felizmente, os sistemas operacionais suportam um pouco de mágica aqui, para que possamos permitir que o MySQL use IPv4 e IPv6 simultaneamente.

Você precisa estar executando o MySQL 5.5.3 ou posterior e iniciar o MySQL com o --bind-address=argumento de linha de comando. Você tem quatro opções de documentos , dependendo do que você deseja fazer:

  • Aquele com quem você provavelmente está familiarizado e o que você provavelmente (efetivamente) está usando 0.0.0.0,. Isso se liga a todos os endereços IPv4 disponíveis na máquina. Na verdade, isso provavelmente não é a melhor coisa a se fazer, mesmo que você não se importe com o IPv6, pois ele sofre os mesmos riscos de segurança que ::.

  • Um endereço IPv4 ou IPv6 explícito (por exemplo 127.0.0.1ou ::1para loopback). Isso liga o servidor a esse endereço e somente a esse endereço.

  • A corda mágica ::. Isso ligará o MySQL a todos os endereços da máquina, tanto os endereços de loopback quanto os da interface física, no modo IPv4 e IPv6. Isso é potencialmente um risco de segurança, apenas faça isso se você precisar do MySQL para aceitar conexões de hosts remotos.

  • Use um endereço IPv6 mapeado para IPv4 . Esse é um mecanismo especial incorporado ao IPv6 para compatibilidade com versões anteriores durante a transição 4 -> 6, e permite a ligação a um endereço IPv4 específico e seu equivalente ao IPv6. É pouco provável que seja útil a você para algo que não seja o endereço de "loopback duplo" ::ffff:127.0.0.1. Essa é provavelmente a melhor solução para a maioria das pessoas, vinculando apenas o loopback, mas permitindo conexões IPv4 e IPv6.

Preciso modificar o arquivo hosts?

NÃO . Não modifique o arquivo hosts. O resolvedor de DNS sabe com o que fazer localhost, redefini-lo na melhor das hipóteses não terá efeito e, na pior das hipóteses, confundir o inferno com o resolvedor.

Que tal --skip-name-resolve?

Isso também pode resolver o problema, por um motivo relacionado, mas um pouco diferente.

Sem essa opção de configuração, o MySQL tentará resolver todos os endereços IP da conexão do cliente com um nome de host por meio de uma PTRconsulta DNS. Se o seu servidor MySQL já estiver habilitado para usar o IPv6, mas as conexões ainda estiverem demorando muito, pode ser porque o registro DNS ( PTR) reverso não está configurado corretamente.

Desabilitar a resolução de nomes corrigirá esse problema, mas ele tem outras ramificações, principalmente porque agora todas as permissões de acesso configuradas para usar um nome DNS na Hostcondição falharão.

Se você fizer isso, precisará configurar todas as suas concessões para usar endereços IP em vez de nomes.

DaveRandom
fonte
2
Finalmente! Obrigado, obrigado! Por fim, encontrei uma explicação adequada, completa e clara de todas as causas, possíveis soluções e suas contra-indicações. Você deve escrever um livro sobre isso!
precisa saber é o seguinte
4
Eu estava com um atraso de 1 segundo na conexão com o MySQL no host local até alterar o endereço de ligação para ::1. Infelizmente, ::ffff:127.0.0.1continuei me dando um atraso de 1 segundo (independentemente de usar skip-name-resolveou não), alguma idéia do porquê? (no Windows 8.1)
Simon East
1
@Simon Nem uma pista sem olhar, mas o primeiro passo para depurar seria tentar se conectar ao loopback IPv6 e ao loopback IPv4 diretamente usando os endereços explícitos para verificar se o MySQL está realmente ouvindo e conectável nas duas pilhas e depurando a partir daí .
precisa saber é o seguinte
1
Sim, :: ffff: 127.0.0.1 não funciona ...
Raheel Hasan
Se eu ligá-lo com :: 1, Sqlyog não funciona ... o que fazer?
precisa
13

Normalmente, quando o IPv6 está ativado nas conexões do servidor com o MySQL, localhostsão extremamente lentos.

Alterar o endereço do servidor mysql no script para 127.0.0.1resolver o problema.

Doug
fonte
+1 foi a resposta correta para mim. Eu acho que no Windows 8 eles se mudaram a resolução de localhostque o resolvedor DNS para a razão que @DaveRandom ligada a: serverfault.com/questions/4689/...
wwarren
Funcionou para mim: D
FosAvance 25/10
Isso pode acontecer para outros servidores que não localhost. Eu tive o mesmo problema de atraso para um endereço de servidor no formulário xx.xxxx.xxxxx.xxxxx.com. Depois que alterei o nome do servidor para o seu endereço IP, o problema desapareceu.
Gruber
1
mysql_connect("localhost", "root", "");

Bem, é bastante óbvio qual é o motivo. PHP realmente bom em algumas coisas, mas não na tradução direta de 'localhost' para '127.0.0.1'. Você deve tentar isso, reduzirá muito o tempo de carregamento geral da página do site, porque impede o PHP de verificar seu arquivo HOSTS e o que não está fazendo para obter o endereço IP real atrás de 'localhost'

Xesau
fonte
Não consigo descobrir o que você está tentando sugerir.
kasperd
@kasperd Resolução de DNS de 'localhost'
Xesau
blocos de '17 - mysql_ * funções estão obsoletos e já removidos em php7
treyBake
0

Você também pode eliminar a desaceleração da consulta fazendo um pequeno ajuste na sua variável de conexão db (que, esperançosamente, está em um arquivo separado dos scripts para portabilidade). Altere o valor do host para "127.0.0.1" em vez de "localhost". Isso ignora a longa pesquisa de DNS do host local.

Espero que isto ajude!

Billman64
fonte