Łączenie się z MySQL z PHP jest bardzo wolne

19

Właśnie zrobiłem nową instalację XAMPP. Przy pierwszym otwarciu PHPMyAdmin zauważyłem, że było to bardzo wolne. Nie miało sensu, aby na localhost otwarcie każdej strony trwało prawie 5 sekund. Zrobiłem mały test, aby zrzucić winę na 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);

Uruchomienie powyższego skryptu zajmuje około 3 sekund (chociaż ładowanie przy pierwszym uruchomieniu zajęło mi około 8 sekund).

Następnie, aby sprawdzić, czy to wina ChNP, próbowałem użyć mysql_connectzamiast tego:

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

Wykończenie zajmuje dokładnie tyle samo.

Początkowo myślałem, że to wina PHP, ale kod PHP i pliki statyczne są podawane szybciej, niż mogę odświeżyć. Testowałem PHP, uruchamiając ten mały skrypt:

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

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

5000 sha1obliczeń, a strona jest nadal wyświetlana szybciej, niż mogę odświeżyć moje okno.

Potem pomyślałem, że to wina MySQL. Ale znowu, nie wymagałem wiele testów, aby dowiedzieć się, że MySQL działa szybciej niż potrzebuję. Za pomocą klienta CLI MySQL zapytanie wyboru użytkownika nie zajmuje nawet mierzalnego czasu - jest to wykonywane, zanim jeszcze wrócę klawisz powrotu.

Problemem musi być połączenie PHP z MySQL - o ile mogłem to zrozumieć. Mogę znaleźć mnóstwo rzeczy o spowolnieniu PHP lub powolnym MySQL, ale nic o spowolnieniu PHP + MySQL.

Dziękujemy wszystkim, którzy mogą mi pomóc w rozwiązaniu tego problemu!


Używam XAMPP 1.8.0 dla win32 ( Link do pobrania )
Wersja PHP: 5.4.4
Wersja MySQL: 14.14


EDYCJA: Po upływie czasu okazuje się, że tak długo trwa funkcja łączenia:

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

Wynik:

Czas połączenia: 1.006148
Czas zapytania: 0,000247

Co może spowodować, że PHP spędza dużo czasu na łączeniu się z bazą danych? Klient CLI, HeidiSQL i MySQL łączą się natychmiast

Hubro
źródło
php -m prosze
wyjsc
@ thinice: pastebin.com/4dXe7ZDa
Hubro

Odpowiedzi:

17

Czy to możliwe, że mysql próbuje uruchomić zapytanie rev-dns przy każdym połączeniu? spróbuj dodać do my.cnf, sekcja mysqld: skip-name-resolver .

pQd
źródło
Co dziwne, zarówno PHPMyAdmin, jak i klient MySQL CLI daje mi teraz, że „Host '127.0.0.1” nie może się połączyć z tym serwerem MySQL ”. Z jakiegoś powodu skrypt PHP nadal działa, ale jest tak powolny jak wcześniej
Hubro
czy „tłuste aplikacje” do zarządzania mysql - podobnie jak mysql workbench - są tak wolne?
pQd
Uruchomienie tego samego zapytania z MySQL Workbench jest tak samo szybkie jak klient CLI, podobnie jak HeidiSQL
Hubro
dodaj trochę czasu w php i sprawdź, czy łączy lub uruchamia kwerendę, która zajmuje dużo czasu.
pQd
Dzięki za ten komentarz, zaktualizowałem swoje pytanie. Zarówno połączenie, jak i zapytanie są super szybkie
Hubro
30

Zostało to zaczerpnięte niemal dosłownie z mojej odpowiedzi tutaj , ale wiem, że marszczymy brwi w odpowiedziach tylko na link w SO, więc wyobrażam sobie, że wy też tak robicie :-)

Jeśli masz ten problem i używasz wersji systemu Windows wcześniejszej niż Windows 7, prawdopodobnie nie jest to odpowiedź na Twój problem.

Dlaczego to się dzieje?

Przyczyną tego problemu jest IPv4 vs IPv6.

Gdy używasz nazwy hosta zamiast adresu IP, klient MySQL najpierw uruchamia AAAAwyszukiwanie hosta (IPv6) dla tej nazwy i najpierw próbuje tego adresu, jeśli pomyślnie rozpoznaje nazwę na adres IPv6. Jeśli jeden z kroków nie powiedzie się (rozpoznawanie nazw lub połączenie), nastąpi powrót do IPv4, uruchamiając Awyszukiwanie i próbując zamiast tego tego hosta.

W praktyce oznacza to, że jeśli localhostwyszukiwanie IPv6 zakończy się powodzeniem, ale MySQL nie jest związany z pętlą zwrotną IPv6, musisz poczekać na jeden limit czasu połączenia, zanim nastąpi awaria IPv4 i połączenie się powiedzie.

Nie było to problemem przed Windows 7, ponieważ localhostrozwiązywanie odbywało się za pomocą pliku hosts i zostało wstępnie skonfigurowane tylko z 127.0.0.1- nie pochodziło z jego odpowiednikiem IPv6 ::1.

Ponieważ jednak system Windows 7 localhostma wbudowaną funkcję rozpoznawania nazw DNS, z powodów opisanych tutaj . Oznacza to, że wyszukiwanie IPv6 zakończy się teraz sukcesem - ale MySQL nie jest powiązany z tym adresem IPv6, więc połączenie nie powiedzie się i zobaczysz opóźnienie opisane w tym pytaniu.

To miłe. Po prostu powiedz mi, jak to naprawić!

Masz kilka opcji. Rozglądając się po Internecie, wydaje się, że ogólnym „rozwiązaniem” jest jawne używanie adresu IP zamiast nazwy, ale istnieje kilka powodów, aby tego nie robić, oba związane z przenośnością, oba prawdopodobnie nieważne:

  • Jeśli przeniesiesz skrypt na inną maszynę, która obsługuje tylko IPv6, skrypt nie będzie już działał.

  • Jeśli przeniesiesz skrypt do środowiska hostingowego opartego na * nix, magiczny ciąg localhostoznaczałby, że klient MySQL wolałby używać gniazda Unix, jeśli jest skonfigurowane, jest to bardziej wydajne niż połączenie oparte na sprzężeniu zwrotnym IP

Brzmią dość ważne?

Nie są. Powinieneś projektować swoją aplikację, aby tego rodzaju rzeczy były zdefiniowane w pliku konfiguracyjnym. Jeśli przeniesiesz skrypt do innego środowiska, są szanse, że inne rzeczy również będą wymagały konfiguracji.

Podsumowując, użycie adresu IP nie jest najlepszym rozwiązaniem, ale najprawdopodobniej jest akceptowalne.

Więc jakie jest najlepsze rozwiązanie?

Najlepszym sposobem jest zmiana adresu powiązania używanego przez serwer MySQL. Nie jest to jednak tak proste, jak mogłoby się to podobać. W przeciwieństwie do Apache, Nginx i prawie każdej innej rozsądnej aplikacji usługi sieciowej, jaką kiedykolwiek stworzono, MySQL obsługuje tylko jeden adres wiązania, więc nie chodzi tylko o dodanie kolejnego. Na szczęście systemy operacyjne obsługują tutaj trochę magii, dzięki czemu możemy umożliwić MySQL jednoczesne używanie zarówno IPv4, jak i IPv6.

Musisz uruchomić MySQL 5.5.3 lub nowszy i uruchomić MySQL z --bind-address=argumentem wiersza poleceń. Masz 4 opcje dokumentów , w zależności od tego, co chcesz zrobić:

  • Ten, jesteś prawdopodobnie zna, a jeden, że jesteś najprawdopodobniej (efektywnie) za pomocą, 0.0.0.0. Powiąże to wszystkie dostępne adresy IPv4 na komputerze. To prawdopodobnie nie jest najlepsza rzecz, nawet jeśli nie zależy ci na IPv6, ponieważ wiąże się to z takimi samymi zagrożeniami bezpieczeństwa jak ::.

  • Jawny adres IPv4 lub IPv6 (na przykład 127.0.0.1lub ::1do sprzężenia zwrotnego). To wiąże serwer z tym adresem i tylko z tym adresem.

  • Magiczny sznurek ::. Spowoduje to powiązanie MySQL z każdym adresem na maszynie, zarówno sprzężeniem zwrotnym, jak i fizycznym adresem interfejsu, w trybie IPv4 i IPv6. Jest to potencjalnie zagrożenie bezpieczeństwa, rób to tylko wtedy, gdy potrzebujesz MySQL do akceptowania połączeń ze zdalnych hostów.

  • Użyj adresu IPv6 odwzorowanego na IPv4 . Jest to specjalny mechanizm wbudowany w IPv6 w celu zapewnienia wstecznej kompatybilności podczas przejścia 4 -> 6 i pozwala na powiązanie z określonym adresem IPv4 i jego odpowiednikiem IPv6. Jest to mało prawdopodobne, aby było to przydatne dla czegokolwiek innego niż adres „podwójnej pętli zwrotnej” ::ffff:127.0.0.1. Jest to najprawdopodobniej najlepsze rozwiązanie dla większości ludzi, wiążące się tylko z pętlą zwrotną, ale umożliwiające połączenia zarówno IPv4, jak i IPv6.

Czy muszę zmodyfikować plik hosts?

NO . Nie modyfikuj pliku hosts. Program rozpoznawania nazw DNS wie, co z tym zrobić localhost, przedefiniowanie go w najlepszym wypadku nie przyniesie żadnego efektu, aw najgorszym przypadku zmyli piekło z usług tłumacza.

Co --skip-name-resolve?

Może to również rozwiązać problem z pokrewnego, ale nieco innego powodu.

Bez tej opcji konfiguracji MySQL będzie próbował rozwiązać wszystkie adresy IP połączeń klienta z nazwą hosta za pomocą PTRzapytania DNS. Jeśli Twój serwer MySQL jest już włączony do korzystania z IPv6, ale połączenia wciąż trwają długo, może to być spowodowane tym, że odwrotny PTRrekord DNS ( ) nie jest poprawnie skonfigurowany.

Wyłączenie rozpoznawania nazw rozwiąże ten problem, ale wiąże się z innymi konsekwencjami, w szczególności że wszelkie uprawnienia dostępu skonfigurowane do używania nazwy DNS w tym Hoststanie nie będą działać.

Jeśli masz zamiar to zrobić, musisz skonfigurować wszystkie granty, aby używały adresów IP zamiast nazw.

DaveRandom
źródło
2
Wreszcie! Dziękuję dziękuję! W końcu znalazłem właściwe, kompletne i jasne wyjaśnienie wszystkich przyczyn, możliwych rozwiązań i ich przeciwwskazań. Powinieneś napisać o tym książkę!
tobia.zanarella
4
Miałem 1 sekundowe opóźnienie połączenia z MySQL na localhost, dopóki nie zmieniłem adresu powiązania na ::1. Niestety ::ffff:127.0.0.1nadal dawał mi 1 sekundowe opóźnienie (niezależnie od użycia skip-name-resolvelub nie), jakieś pomysły, dlaczego? (w systemie Windows 8.1)
Simon East
1
@ Simon Nie jest to wskazówka bez patrzenia, ale pierwszym krokiem do debugowania byłaby próba połączenia się zarówno z pętlą zwrotną IPv6, jak i pętlą zwrotną IPv4 bezpośrednio przy użyciu jawnych adresów w celu sprawdzenia, czy MySQL faktycznie nasłuchuje i łączy się na obu stosach, i stamtąd debuguje .
DaveRandom
1
tak, :: ffff: 127.0.0.1 nie działa ...
Raheel Hasan
Jeśli powiążę to z :: 1, Sqlyog nie działa ... co zrobić?
Raheel Hasan
13

Zwykle, gdy IPv6 jest włączony na serwerze, połączenia z MySQL localhostsą bardzo wolne.

Zmiana adresu serwera mysql w skrypcie, aby 127.0.0.1rozwiązać problem.

Doug
źródło
+1 to była dla mnie poprawna odpowiedź. Myślę, że w Windows 8 przenieśli rozdzielczość localhostdo resolvera DNS z tego powodu, że @DaveRandom połączył się z: serverfault.com/questions/4689
wwarren
To zadziałało dla mnie: D
FosAvance,
Może się to zdarzyć w przypadku serwerów innych niż localhost. Miałem ten sam problem z opóźnieniem adresu serwera w formularzu xx.xxxx.xxxxx.xxxxx.com. Gdy zmieniłem nazwę serwera na adres IP, problem zniknął.
Gruber
1
mysql_connect("localhost", "root", "");

Cóż, to całkiem oczywiste, jaki jest tego powód. PHP jest naprawdę dobry w niektórych sprawach, ale nie w bezpośrednim tłumaczeniu „localhost” na „127.0.0.1”. Musisz spróbować, to naprawdę obniży ogólny czas ładowania strony, ponieważ powstrzymuje PHP przed sprawdzaniem pliku HOSTS i co nie robi, aby uzyskać prawdziwy adres IP za „localhost”

Xesau
źródło
Nie mogę zrozumieć, co próbujesz zasugerować, na czym polega problem.
kasperd
@kasperd Rozwiązanie DNS „localhost”
Xesau,
notatka z '17 - funkcje mysql * są teraz przestarzałe i usunięte w php7
treyBake
0

Możesz także wyeliminować spowolnienie zapytania, dokonując niewielkiej korekty zmiennej połączenia db (która, mam nadzieję, znajduje się w oddzielnym pliku od skryptów w celu zapewnienia przenośności). Zmień wartość hosta na „127.0.0.1” zamiast „localhost”. Pomija to długie wyszukiwanie DNS dla hosta lokalnego.

Mam nadzieję że to pomoże!

Billman64
źródło