Jakie są wady korzystania z trwałego połączenia w PDO


181

W PDO połączenie można ustawić na stałe za pomocą tego PDO::ATTR_PERSISTENTatrybutu. Zgodnie z instrukcją php -

Trwałe połączenia nie są zamykane na końcu skryptu, ale są buforowane i ponownie używane, gdy inny skrypt żąda połączenia przy użyciu tych samych poświadczeń. Trwała pamięć podręczna połączeń pozwala uniknąć narzutów związanych z nawiązywaniem nowego połączenia za każdym razem, gdy skrypt musi rozmawiać z bazą danych, co powoduje szybszą aplikację internetową.

Podręcznik zaleca również, aby nie używać trwałego połączenia podczas korzystania ze sterownika ODBC PDO, ponieważ może to utrudnić proces tworzenia puli połączeń ODBC.

Wygląda więc na to, że nie ma żadnych wad używania trwałego połączenia w PDO, z wyjątkiem ostatniego przypadku. Chciałbym jednak wiedzieć, czy istnieją inne wady korzystania z tego mechanizmu, tj. Sytuacja, w której ten mechanizm powoduje pogorszenie wydajności lub coś w tym rodzaju.


Wow, zapłaciłeś 1000 powtórzeń za to proste pytanie?
Pacerier

@Pacerier, nie , to był ktoś inny .
Charles

Odpowiedzi:


287

Przeczytaj tę odpowiedź poniżej , która szczegółowo opisuje sposoby złagodzenia opisanych tutaj problemów.


Przy użyciu PDO występują te same wady, co w przypadku każdego innego interfejsu bazy danych PHP, który wykonuje trwałe połączenia: jeśli skrypt nieoczekiwanie zakończy się w trakcie operacji na bazie danych, następne żądanie, które spowoduje przerwanie połączenia, odbierze miejsce, w którym przerwany został martwy skrypt. Połączenie jest utrzymywane otwarte na poziomie menedżera procesów (Apache dla mod_php, bieżący proces FastCGI, jeśli używasz FastCGI itp.), A nie na poziomie PHP, a PHP nie mówi procesowi nadrzędnemu, aby pozwolił umrzeć połączeniu, gdy skrypt kończy się nienormalnie.

Jeśli martwe skrypty zablokowały tabele, tabele te pozostaną zablokowane, dopóki połączenie nie zostanie przerwane lub następny skrypt, który uzyska połączenie, odblokuje same tabele.

Jeśli martwy skrypt znajdował się w środku transakcji, może blokować wiele tabel, dopóki nie uruchomi się licznik zakleszczenia, a nawet wtedy zegar zakleszczenia może zabić nowsze żądanie zamiast starszego żądania, które powoduje problem.

Jeśli martwy skrypt znajdował się w środku transakcji, następny skrypt, który pobierze to połączenie, również otrzyma stan transakcji. Jest bardzo możliwe (w zależności od projektu aplikacji), że następny skrypt może nigdy nie próbować zatwierdzić istniejącej transakcji, lub zatwierdzi, gdy nie powinien, lub wycofa się, gdy nie powinien.

To tylko wierzchołek góry lodowej. Można to wszystko złagodzić do pewnego stopnia, zawsze próbując wyczyścić po brudnym połączeniu przy każdym żądaniu skryptu, ale może to być uciążliwe w zależności od bazy danych. Chyba, że zidentyfikowali tworzenia połączeń bazie danych jako jedna rzecz, która jest wąskim gardłem w skrypcie (oznacza to zrobiłeś kod profilowania za pomocą Xdebug i / lub xhprof ), należy nie rozważyć stałe połączenia w postaci roztworu do niczego.

Co więcej, większość współczesnych baz danych (w tym PostgreSQL) ma swoje preferowane sposoby wykonywania pul połączeń, które nie mają bezpośrednich wad, jak zwykłe trwałe połączenia oparte na PHP.


Aby wyjaśnić tę kwestię, używamy trwałych połączeń w moim miejscu pracy, ale nie z wyboru. Napotkaliśmy dziwne zachowanie połączenia, gdy początkowe połączenie z naszego serwera aplikacji do naszego serwera bazy danych trwało dokładnie trzy sekundy, kiedy powinno to zająć ułamek sekundy. Uważamy, że to błąd jądra. Zrezygnowaliśmy z próby rozwiązania tego problemu, ponieważ zdarzyło się to losowo i nie można go było odtworzyć na żądanie, a nasze outsourcingowe informatyki nie miały konkretnej możliwości wyśledzenia.

Niezależnie od tego, kiedy ludzie w magazynie przetwarzają kilkaset przychodzących części, a każda część zajmuje trzy i pół sekundy zamiast pół sekundy, musieliśmy podjąć działania, zanim nas porwali i zmusili do pomocy. Więc włączyliśmy kilka bitów w naszą domową potworność ERP / CRM / CMS i doświadczyliśmy wszystkich okropności trwałych połączeń z pierwszej ręki. Wyszukanie wszystkich subtelnych drobnych problemów i dziwnych zachowań, które wydawały się przypadkowe, zajęło nam tygodnie . Okazało się, że te fatalne błędy raz w tygodniu, które pilnie wycisnęli nasi użytkownicy z naszej aplikacji, pozostawiały zablokowane tabele, porzucone transakcje i inne niefortunne kłopotliwe stany.

Ta szlochająca historia ma sens: zepsuła rzeczy, których nigdy się nie spodziewaliśmy, wszystko w imię występu. Kompromis nie był tego warty iz niecierpliwością czekamy na dzień, w którym możemy wrócić do normalnych połączeń bez zamieszek ze strony naszych użytkowników.


2
Mam nadzieję, że przeczytałem tę odpowiedź przed bieganiemSELECT orders.* FROM orders LEFT JOIN items USING(item_id)
Ast Derek

31
Znam dużą stronę internetową, która korzysta z trwałych połączeń od prawie dekady. Sztuczka polega na użyciu warstwy powyżej rozszerzenia DB i zapamiętywaniu rzeczy, które należy wyczyścić za pomocą register_shutdown_function(). Jeśli proces umrze, połączenie również umrze. Jeśli nie, połączenie jest resetowane do stanu czystego (np. Otwarte transakcje są wycofywane). Jeśli to się nie powiedzie, połączenie zostanie zamknięte, a nowe zostanie otwarte przy kolejnym żądaniu tego samego procesu. Nie ma potrzeby demonizacji trwałych połączeń.
Walter Tross,

Jestem ciekawy @Charles ... czy Twój problem został kiedykolwiek rozwiązany?
Tschallacka

@MichaelDibbets Wymieniliśmy serwer aplikacji kilka miesięcy temu i wyłączyliśmy pconnect, aby sprawdzić, czy trzysekundowy błąd nadal występuje. Nie było To chyba rozwiązane przez proxy. Poniższa odpowiedź w odniesieniu do mysqli_change_userjest prawdopodobnie najlepszym rozwiązaniem dla osób, które muszą utrzymywać trwałe połączenia w aplikacji nieprzeznaczonej do rozwiązywania problemów ze stanem.
Charles

5
Mieliśmy 5 sekundowe opóźnienie przy połączeniu, które udało nam się wyodrębnić jako problem DNS + IPv6. Serwer szukał adresu v6, awaria, a następnie używał adresu IPv4.
Nigel Atkinson,

45

W odpowiedzi na powyższy problem Charlesa,

Od: http://www.php.net/manual/en/mysqli.quickstart.connections.php -

Częstą skargą na trwałe połączenia jest to, że ich stan nie jest resetowany przed ponownym użyciem. Na przykład otwarte i niedokończone transakcje nie są automatycznie wycofywane. Ale również zmiany autoryzacji, które nastąpiły w czasie między umieszczeniem połączenia w puli a ponownym użyciem go, nie są odzwierciedlane. Może to być postrzegane jako niepożądany efekt uboczny. Wręcz przeciwnie, nazwa uparta może być rozumiana jako obietnica, że ​​państwo będzie trwać.

Rozszerzenie mysqli obsługuje obie interpretacje trwałego połączenia: stan trwał i reset stanu przed ponownym użyciem. Domyślnie jest resetowany. Przed ponownym użyciem trwałego połączenia rozszerzenie mysqli domyślnie wywołuje mysqli_change_user()resetowanie stanu. Trwałe połączenie wydaje się użytkownikowi tak, jakby właśnie zostało otwarte. Żadne artefakty z poprzednich zastosowań nie są widoczne.

Ta mysqli_change_user()funkcja jest kosztowną operacją. Aby uzyskać najlepszą wydajność, użytkownicy mogą chcieć ponownie skompilować rozszerzenie z ustawioną flagą kompilacji MYSQLI_NO_CHANGE_USER_ON_PCONNECT.

Użytkownik może wybrać bezpieczne zachowanie i najlepszą wydajność. Oba są ważnymi celami optymalizacji. Dla łatwości użytkowania bezpieczne zachowanie stało się domyślnym kosztem maksymalnej wydajności.


+1, gdyby nie to, że wyczyściliśmy bałagan na inne sposoby, chciałbym zobaczyć, czy ręczne wywołanie change_user rozwiązałoby nasze dziwne problemy z nieznanym stanem.
Charles

Jaki jest odpowiednik trwałych połączeń PDO Postgres? Mam podobne problemy, jakie miał @Charles, gdzie po pewnym czasie użytkownicy otrzymywali błąd, taki jak pobieranie sql - serwer nieoczekiwanie zakończył połączenie. Prawdopodobnie oznacza to, że serwer zakończył się nienormalnie podczas uruchamiania prostej kwerendy SELECT (nawet transakcji).
Carmageddon

1
@ Carmageddon, to bardziej pasuje do nowego pytania, ale tl; dr jest taki, że Postgres nie robi pconnect i zamiast tego powinieneś użyć jednej z zewnętrznych pul połączeń.
Charles,

@Charles, co przez to rozumiesz? czy używanie trwałego połączenia PDO nie jest równoważne z używaniem „zewnętrznych pul połączeń”? lub co miałeś na myśli?
Carmageddon,

@Carmageddon, mam na myśli to, że społeczność Postgres zdecydowała się na pule połączeń jako lepsze rozwiązanie niż pconnect. Sprawdź pgbouncer lub pgpool-II. Nie jestem pewien, czy PDO i tak łączy się z Postgres, ale mogę być całkowicie z rockerem.
Charles,

13

Trwałe połączenia są dobrym pomysłem tylko wtedy, gdy połączenie z bazą danych zajmuje (stosunkowo) dużo czasu. W dzisiejszych czasach prawie nigdy tak nie jest. Największą wadą trwałych połączeń jest to, że ogranicza liczbę użytkowników, którzy mogą przeglądać twoją stronę: jeśli MySQL jest skonfigurowany tak, aby zezwalał tylko na 10 równoczesnych połączeń na raz, to kiedy 11. osoba próbuje przeglądać twoją stronę, nie będzie dla nich działać .

PDO nie zarządza trwałością. Sterownik MySQL działa. Ponownie wykorzystuje połączenia, gdy a) są dostępne, a host / użytkownik / hasło / baza danych są zgodne. W przypadku jakichkolwiek zmian połączenie nie zostanie ponownie użyte. Najlepszym efektem netto jest to, że te połączenia, które masz, będą tak często uruchamiane i zatrzymywane, ponieważ masz różnych użytkowników w witrynie, a ich utrzymywanie nie przyniesie nic dobrego.

Kluczową rzeczą do zrozumienia na temat trwałych połączeń jest to, że NIE powinieneś ich używać w większości aplikacji internetowych. Brzmią kusząco, ale są niebezpieczne i prawie bezużyteczne.

Jestem pewien, że są w tym inne wątki, ale trwałe połączenie jest niebezpieczne, ponieważ utrzymuje się między żądaniami. Jeśli na przykład zablokujesz tabelę podczas żądania, a następnie nie odblokujesz, wówczas ta tabela pozostanie zablokowana na czas nieokreślony. Trwałe połączenia są również prawie bezużyteczne dla 99% twoich aplikacji, ponieważ nie masz możliwości dowiedzenia się, czy to samo połączenie będzie używane między różnymi żądaniami. Każdy wątek internetowy będzie miał swój własny zestaw trwałych połączeń i nie będziesz mieć możliwości kontrolowania, który wątek obsłuży które żądania.

Proceduralna biblioteka mysql PHP ma funkcję, dzięki której kolejne wywołania mysql_connect zwrócą to samo łącze, zamiast otwierać inne połączenie (jak można się spodziewać). Nie ma to nic wspólnego z trwałymi połączeniami i jest specyficzne dla biblioteki mysql. ChNP nie wykazuje takiego zachowania


Link do zasobu: link

Ogólnie możesz użyć tego jako szorstkiego „zestawu reguł”:

TAK , użyj trwałych połączeń, jeśli:

  • Dostęp do bazy danych ma tylko kilka aplikacji / użytkowników, tzn. Nie spowoduje to 200 otwartych (ale prawdopodobnie bezczynnych) połączeń, ponieważ na tym samym hoście jest 200 różnych użytkowników.
  • Baza danych działa na innym serwerze, do którego uzyskujesz dostęp przez sieć

  • (Jedna) aplikacja bardzo często uzyskuje dostęp do bazy danych

NIE , nie używaj trwałych połączeń, jeśli:

  • Twoja aplikacja musi mieć dostęp do bazy danych tylko 100 razy na godzinę.

  • Masz wiele serwerów dostępu do jednego serwera bazy danych

Korzystanie z trwałych połączeń jest znacznie szybsze, szczególnie jeśli uzyskujesz dostęp do bazy danych przez sieć. Nie ma znaczenia, czy baza danych działa na tym samym komputerze, ale wciąż jest nieco szybsza. Jednak - jak sama nazwa wskazuje - połączenie jest trwałe, tzn. Pozostaje otwarte, nawet jeśli nie jest używane.

Problem polega na tym, że w „domyślnej konfiguracji” MySQL dopuszcza tylko 1000 równoległych „otwartych kanałów”. Następnie nowe połączenia są odrzucane (możesz dostosować to ustawienie). Jeśli więc masz, powiedzmy, 20 serwerów WWW ze 100 klientami, a każdy z nich ma dostęp tylko do jednej strony na godzinę, prosta matematyka pokaże, że potrzebujesz 2000 równoległych połączeń z bazą danych. To nie zadziała.

Ergo: używaj go tylko do aplikacji z dużą ilością żądań.


4
Po wierszu twoją odpowiedzią jest skopiuj wklej ze stackoverflow.com/a/51583/718224
Tony Stark

1
„TAK, używaj trwałych połączeń, jeśli: [...] Dostęp do bazy danych ma tylko kilka aplikacji / użytkowników” jest sprzeczne z „Używaj go tylko do aplikacji z dużą liczbą żądań”. To ostatnie jest jednak poprawne. Sytuacja: tysiące żądań na sekundę spowodują setki aktywnych połączeń z bazą danych. Gdy system skaluje się liniowo, skaluje również liniowo liczbę połączeń z bazą danych. Więc więcej żądań (więcej użytkowników) spowoduje więcej połączeń. Potrzebujesz więc (!) Ograniczonej liczby aktywnych połączeń, gdy masz dużo żądań (użytkowników)
Ken Van Hoeylandt

12

Podczas moich testów miałem ponad sekundowy czas połączenia z moim lokalnym hostem, zakładając, że powinienem używać stałego połączenia. Dalsze testy wykazały, że był to problem z „localhost”:

Wyniki testu w sekundach (mierzone mikrotimem php):

  • hostowana strona: connectDB: 0,0038912296295166
  • localhost: connectDB: 1.0214691162109 (w ciągu jednej sekundy: nie używaj localhost!)
  • 127.0.0.1: connectDB: 0.00097203254699707

Co ciekawe: poniższy kod jest tak samo szybki jak przy użyciu 127.0.0.1:

$host = gethostbyname('localhost');
// echo "<p>$host</p>";
$db = new PDO("mysql:host=$host;dbname=" . DATABASE . ';charset=utf8', $username, $password,
    array(PDO::ATTR_EMULATE_PREPARES => false,
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));

Wygląda na to, że PDO ma trudności z tłumaczeniem nazw domen! Dziękuję, zastanawiałem się, dlaczego każde połączenie zajmuje tak długo moją czterordzeniową maszynę!
Mustafa,

@Gunnar Bernstein +1 ładne znalezisko. „localhost” z pewnością trwa dłużej, co nieco poprawiło prędkość mojej aplikacji internetowej (tworzy wiele połączeń).
imperium2335

1
To jest świetne. Coś jest nie tak z rozdzielczością na moim komputerze deweloperskim ... użycie adresu IP zabrało mój skrypt z 6.1s do 1.1s
Pete

localhostużywa połączenia z gniazdem, połączenie z gniazdem słynie z tego, że jest złe przy dużej ilości połączeń
mente

@mente Wszelkie odniesienia, zasoby, które mogą to udowodnić? Wydaje mi się, że UDS są preferowane przez TCP. Dzięki.
Nuxwin

6

Trwałe połączenia powinny znacznie zwiększyć wydajność. Nie zgadzam się z twierdzeniem, że powinieneś „Unikać” wytrwałości.

Wygląda na to, że powyższe skargi są kierowane przez kogoś, kto korzysta z tabel MyIASM i włamuje się do własnych wersji transakcji, chwytając blokady tabel. Cóż, oczywiście, że jesteś w impasie! Użyj PDT beginTransaction () i przenieś swoje tabele do InnoDB ..


2
Rozumiem, że rok później, ale dla przypomnienia: moja opowieść pochodzi z bazy danych złożonej w całości z tabel InnoDB, z wyjątkiem garstki zdormalizowanych klonów utkniętych w bagnie MyISAM w celu obsługi indeksowania pełnotekstowego.
Charles

Pfft, Sphinx jest stary i zepsuty, ElasticSearch to nowa gorączka. Pewnego pięknego dnia użyjemy go do naszych starych aplikacji zamiast tylko nowych ...
Charles

Wyszukiwanie pełnotekstowe w PostgreSQL jest prawdziwym zwycięzcą. To niesamowite. Nie wymaga uruchomionego innego narzędzia / serwera. Nie musisz się martwić o synchronizację danych. Bardzo szczegółowe kontrole. Wiele słowników lub napisz własne. A ponieważ PostgreSQL automatycznie korzysta z zapytań o wielu indeksach, możesz po prostu dodać je do każdego innego uruchomionego zapytania.
brightball

2
MySQL 5.6 oferuje obsługę pełnego tekstu dla tabel InnoDB.
timofly

2

Wydaje mi się, że trwałe połączenie zjadłoby więcej zasobów systemowych. Może trywialna kwota, ale wciąż ...


Często wymiana czasu ludzkiego na mikrosekundy czasu komputerowego
Andy Chase,

1

Wyjaśnienie używania trwałych połączeń w oczywisty sposób zmniejsza liczbę połączeń, które są dość kosztowne, pomimo faktu, że są one znacznie szybsze w MySQL w porównaniu do innych baz danych.

Pierwszy problem z trwałymi połączeniami ...

Jeśli tworzysz 1000 połączeń na sekundę, zwykle nie upewniasz się, że pozostaje ono otwarte przez bardzo długi czas, ale robi to System operacyjny. Oparty na protokole TCP / IP Porty nie mogą być natychmiast poddane recyklingowi, a także muszą zainwestować chwilę na etapie „FIN”, zanim będą mogły zostać poddane recyklingowi.

Drugi problem ... przy użyciu wielu połączeń z serwerem MySQL.

Wiele osób po prostu nie zdaje sobie sprawy, że jesteś w stanie zwiększyć zmienną * max_connections * i uzyskać ponad 100 równoczesnych połączeń z MySQL, inni zostali pobici przez starsze problemy Linuksa z niemożnością przekazania więcej niż 1024 połączeń z MySQL.

Pozwala teraz mówić o tym, dlaczego trwałe połączenia zostały wyłączone w rozszerzeniu mysqli. Pomimo tego, że możesz niewłaściwie używać trwałych połączeń i uzyskiwać słabą wydajność, co nie było głównym powodem. Rzeczywisty powód jest taki - możesz mieć z tym o wiele więcej problemów.

Trwałe połączenia były umieszczane w PHP przy różnych okazjach MySQL 3.22 / 3.23, kiedy MySQL nie był tak trudny, co oznacza, że ​​można łatwo przetwarzać połączenia bez żadnych problemów. W późniejszych wersjach pojawiło się jednak wiele problemów - w przypadku recyklingu połączenia z niezatwierdzonymi transakcjami masz problemy. Jeśli przetwarzasz połączenia z niestandardowymi konfiguracjami zestawów znaków, ponownie jesteś w niebezpieczeństwie, a także o możliwości przekształcenia zmiennych sesji.

Jednym z problemów z korzystaniem z trwałych połączeń jest to, że tak naprawdę nie skaluje się tak dobrze. Dla tych, którzy mają 5000 podłączonych osób, potrzebujesz 5000 trwałych połączeń. Aby uniknąć wymogu wytrwałości, możesz mieć możliwość obsługi 10000 osób o podobnej liczbie połączeń, ponieważ są one w stanie dzielić się indywidualnymi połączeniami, gdy ich nie ma.


0

Zastanawiałem się tylko, czy częściowym rozwiązaniem byłoby posiadanie puli jednorazowych połączeń. Możesz poświęcić czas na tworzenie puli połączeń, gdy system jest w niskim zużyciu, do limitu, rozdawać je i zabijać, gdy zostaną ukończone lub przekroczą limit czasu. W tle tworzysz nowe połączenia podczas ich pobierania. W najgorszym przypadku powinno to być tak powolne jak tworzenie połączenia bez puli, zakładając, że ustanowienie łącza jest czynnikiem ograniczającym?

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.