Подключение к MySQL из PHP очень медленное

19

Я только что сделал новую установку XAMPP. При первом открытии PHPMyAdmin я заметил, что это было очень медленно. Не имеет смысла, что на localhost для открытия каждой страницы требуется почти 5 секунд. Я сделал небольшой тестовый пример, чтобы снять вину с 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);

Вышеприведенный скрипт запускается всего за 3 секунды (хотя при первой его загрузке потребовалось около 8 секунд).

Затем, чтобы проверить, была ли это ошибка PDO, я попытался использовать mysql_connectвместо этого:

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

Требуется ровно столько времени, чтобы закончить.

Сначала я думал, что это вина PHP, но PHP-код и статические файлы обслуживаются быстрее, чем я могу нажать обновить. Я проверил PHP, запустив этот маленький скрипт:

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

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

5000 sha1расчетов, и страница по-прежнему отображается быстрее, чем я могу обновить свое окно.

Тогда я понял, что это вина MySQL. Но опять же, не нужно было много тестировать, чтобы понять, что MySQL работает быстрее, чем мне нужно. Используя клиентский интерфейс MySQL CLI, пользовательский запрос на выборку даже не занимает измеримое время - он выполняется еще до того, как я позволил ключу возврата вернуться вверх.

Проблема должна быть связана с подключением PHP к MySQL - насколько я могу судить. Я могу найти тонны вещей о медленном PHP или медленном MySQL, но ничего о PHP + MySQL очень медленном.

Спасибо всем, кто может помочь мне решить эту проблему!


Я использую XAMPP 1.8.0 для win32 ( Ссылка для скачивания )
Версия PHP: 5.4.4
Версия MySQL: 14.14


РЕДАКТИРОВАТЬ: после синхронизации оказывается, что это функция подключения, которая занимает так много времени:

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

Выход:

Время подключения: 1.006148
Время запроса: 0.000247

Что может заставить PHP тратить много времени на подключение к базе данных? Клиент CLI, рабочая среда HeidiSQL и MySQL подключаются мгновенно

Hubro
источник
php -m выведите пожалуйста
тоньше
@thinice: pastebin.com/4dXe7ZDa
Hubro

Ответы:

17

Может быть, ваш mysql пытается выполнить запрос rev-dns при каждом подключении? попробуйте добавить в my.cnf раздел mysqld: skip-name-Resolution .

PQD
источник
Как ни странно, и PHPMyAdmin, и MySQL CLI-клиент теперь дают мне сообщение «Хосту« 127.0.0.1 »не разрешено подключаться к этому серверу MySQL». По какой-то причине PHP-скрипт все еще работает, но так же медленно, как и раньше
Hubro
«толстые приложения» для управления MySQL - как MySQL Workbench - медленно?
PQD
Выполнение того же запроса из MySQL Workbench так же быстро, как и для клиента CLI, так же, как и для HeidiSQL
Hubro
добавьте немного времени в php и проверьте, подключается ли он или выполняет запрос, который занимает много времени.
PQD
Спасибо за этот комментарий, я обновил свой вопрос. И соединение, и запрос очень быстрые
Hubro
30

Это почти дословно взято из моего ответа здесь , но я знаю, что мы недовольны ответами только для ссылок на SO, так что я думаю, что вы, ребята, тоже так делаете :-)

Если у вас возникла эта проблема и вы используете версию Windows до Windows 7, это, вероятно, не ответ на вашу проблему.

Почему это происходит?

Причиной этой проблемы является IPv4 против IPv6.

Когда вы используете имя хоста вместо IP-адреса, клиент MySQL сначала запускает AAAAпоиск хоста (IPv6) для имени и сначала пробует этот адрес, если он успешно разрешает имя в адрес IPv6. Если какой-либо шаг завершится неудачно (разрешение имени или соединение), он вернется к IPv4, запустив Aпоиск и попробовав этот хост.

На практике это означает, что если localhostпоиск IPv6 успешен, но MySQL не связан с обратной связью IPv6, вам нужно будет подождать один цикл тайм-аута соединения, прежде чем произойдет откат IPv4 и соединение установится успешно.

Это не было проблемой до Windows 7, потому что localhostразрешение было сделано через файл hosts, и он был предварительно сконфигурирован только с 127.0.0.1- он не шел с его аналогом IPv6 ::1.

Однако, поскольку в Windows 7 localhostразрешение встроено в распознаватель DNS по причинам, изложенным здесь . Это означает, что поиск IPv6 теперь будет успешным, но MySQL не привязан к этому IPv6-адресу, поэтому соединение не будет установлено, и вы увидите задержку, описанную в этом вопросе.

Это мило. Просто скажи мне, как это исправить уже!

У вас есть несколько вариантов. Оглядываясь в Интернете, можно сказать, что общим «решением» является явное использование IP-адреса вместо имени, но есть несколько причин не делать этого, обе связаны с переносимостью, обе, возможно, не важны:

  • Если вы переместите свой скрипт на другой компьютер, который поддерживает только IPv6, ваш скрипт больше не будет работать.

  • Если вы переместите свой сценарий в среду размещения * nix, волшебная строка localhostбудет означать, что клиент MySQL предпочтет использовать сокет Unix, если он настроен, это более эффективно, чем возможность подключения на основе петли IP

Они звучат довольно важно, хотя?

Это не так. Вы должны разрабатывать свое приложение так, чтобы такого рода вещи определялись в файле конфигурации. Если вы переместите свой сценарий в другую среду, скорее всего, другие вещи также потребуются в настройке.

Таким образом, использование IP-адреса не является лучшим решением, но, скорее всего, приемлемым.

Так что же является лучшим решением?

Лучшим способом было бы изменить адрес привязки, который использует сервер MySQL. Однако это не так просто, как хотелось бы. В отличие от Apache, Nginx и почти любого другого приложения для работы с сетевыми службами, MySQL поддерживает только один адрес привязки, поэтому это не просто случай добавления другого. К счастью, операционные системы поддерживают магию, поэтому мы можем позволить MySQL одновременно использовать как IPv4, так и IPv6.

Вам необходимо запустить MySQL 5.5.3 или более позднюю версию и запустить MySQL с --bind-address=аргументом командной строки. У вас есть 4 варианта документации , в зависимости от того, что вы хотите сделать:

  • Тот, с которым вы, вероятно, знакомы, и тот, который вы наиболее вероятно (эффективно) используете 0.0.0.0. Это привязывает ко всем доступным IPv4-адресам на машине. На самом деле это, вероятно, не лучшая вещь, даже если вы не заботитесь об IPv6, поскольку он несет те же риски безопасности, что и ::.

  • Явный адрес IPv4 или IPv6 (например, 127.0.0.1или ::1для обратной связи). Это связывает сервер с этим адресом и только с этим адресом.

  • Волшебная строка ::. Это свяжет MySQL с каждым адресом на машине, как с обратной связью, так и с адресами физического интерфейса, в режимах IPv4 и IPv6. Это потенциально угроза безопасности, делайте это только в том случае, если вам нужен MySQL для приема соединений от удаленных хостов.

  • Используйте IPv4-сопоставленный IPv6-адрес . Это специальный механизм, встроенный в IPv6 для обратной совместимости при переходе 4 -> 6, и он позволяет вам привязываться к определенному адресу IPv4 и его эквиваленту IPv6. Это вряд ли пригодится вам для чего-либо, кроме адреса «двойной петли» ::ffff:127.0.0.1. Скорее всего, это лучшее решение для большинства людей, только связывание с обратной связью, но допускающее как IPv4, так и IPv6-соединения.

Нужно ли изменять файл hosts?

NO . Не изменяйте файл hosts. DNS-распознаватель знает, что делать localhost, переопределение его в лучшем случае не даст никакого эффекта, а в худшем - запутает его.

Как насчет --skip-name-resolve?

Это также может решить проблему по связанной, но немного другой причине.

Без этой опции конфигурации MySQL будет пытаться разрешить все IP-адреса клиентских подключений к имени хоста через PTRзапрос DNS. Если ваш сервер MySQL уже включен для использования IPv6, но соединения все еще занимают много времени, это может быть связано с PTRнеправильной настройкой обратной записи DNS ( ).

Отключение разрешения имен решит эту проблему, но оно имеет и другие последствия, в частности, что любые разрешения доступа, настроенные для использования DNS-имени в Hostусловии, теперь не будут выполнены.

Если вы собираетесь это сделать, вам нужно будет настроить все свои права на использование IP-адресов вместо имен.

DaveRandom
источник
2
В заключение! Спасибо Спасибо! Наконец я нашел правильное, полное и четкое объяснение всех причин, возможных решений и их противопоказаний. Вы должны написать книгу об этом!
tobia.zanarella
4
У меня была задержка соединения в 1 секунду с MySQL на localhost, пока я не изменил адрес привязки на ::1. К сожалению, ::ffff:127.0.0.1продолжал давать мне задержку в 1 секунду (независимо от использования skip-name-resolveили нет), есть идеи, почему? (на Windows 8.1)
Саймон Ист
1
@Simon Не подсказка без поиска, но первым шагом к отладке будет попытка подключиться как к петле IPv6, так и к петле IPv4 напрямую, используя явные адреса, чтобы убедиться, что MySQL действительно прослушивает и подключается в обоих стеках, и отлаживает оттуда ,
DaveRandom
1
да, :: ffff: 127.0.0.1 не работает ...
Рахил Хасан
Если я свяжу это с :: 1, Sqlyog не работает ... что делать ??
Рахил Хасан
13

Обычно, когда IPv6 включен на сервере, соединения с MySQL используются localhostкрайне медленно.

Изменение адреса сервера mysql в скрипте для решения 127.0.0.1проблемы.

Doug
источник
+1 это был правильный ответ для меня. Я предполагаю, что в Windows 8 они перенесли разрешение localhostв DNS-распознаватель по той причине, что @DaveRandom ссылается на: serverfault.com/questions/4689/…
wwarren
У меня это сработало: D
FosAvance
Это может произойти для других серверов, чем localhost. У меня была такая же проблема задержки для адреса сервера в форме xx.xxxx.xxxxx.xxxxx.com. Как только я изменил имя сервера на его IP-адрес, проблема исчезла.
Грубер
1
mysql_connect("localhost", "root", "");

Ну, это совершенно очевидно, в чем причина. PHP действительно хорош в некоторых вещах, но не в прямом переводе «localhost» в «127.0.0.1». Вы должны попробовать это, это действительно уменьшит общее время загрузки страницы вашего сайта, потому что он удерживает PHP от проверки вашего файла HOSTS и того, что он не делает, чтобы получить реальный IP-адрес за «localhost»

Xesau
источник
Я не могу понять, что вы пытаетесь предположить, в чем проблема.
kasperd
@kasperd DNS разрешение «localhost»
Xesau
примечание от '17 - mysql_ * функции устарели и удалены в php7
treyBake
0

Вы также можете устранить замедление запросов, внеся небольшую корректировку в переменную соединения с БД (которая, мы надеемся, находится в отдельном файле от ваших скриптов для переносимости). Измените значение хоста на «127.0.0.1» вместо «localhost». Это обходит долгий поиск DNS для localhost.

Надеюсь это поможет!

Billman64
источник