Was sind die Nachteile der Verwendung einer dauerhaften Verbindung in PDO?

181

In PDO kann eine Verbindung mithilfe des PDO::ATTR_PERSISTENTAttributs dauerhaft hergestellt werden . Laut PHP-Handbuch -

Permanente Verbindungen werden nicht am Ende des Skripts geschlossen, sondern zwischengespeichert und wiederverwendet, wenn ein anderes Skript eine Verbindung mit denselben Anmeldeinformationen anfordert. Mit dem permanenten Verbindungscache können Sie den Aufwand vermeiden, jedes Mal eine neue Verbindung herzustellen, wenn ein Skript mit einer Datenbank kommunizieren muss, was zu einer schnelleren Webanwendung führt.

In diesem Handbuch wird außerdem empfohlen, während der Verwendung des PDO-ODBC-Treibers keine dauerhafte Verbindung zu verwenden, da dies den ODBC-Verbindungspooling-Prozess behindern kann.

Anscheinend scheint es keine Nachteile bei der Verwendung einer dauerhaften Verbindung in PDO zu geben, außer im letzten Fall. Ich würde jedoch gerne wissen, ob es andere Nachteile bei der Verwendung dieses Mechanismus gibt, dh eine Situation, in der dieser Mechanismus zu Leistungseinbußen oder Ähnlichem führt.

MD Sayem Ahmed
quelle
Wow, Sie haben 1000 Wiederholungen für diese einfache Frage bezahlt?
Pacerier
@ Pacerier, nein, es war jemand anderes .
Charles

Antworten:

287

Bitte lesen Sie diese Antwort weiter unten , in der die Möglichkeiten zur Minderung der hier beschriebenen Probleme aufgeführt sind.


Bei der Verwendung von PDO bestehen dieselben Nachteile wie bei jeder anderen PHP-Datenbankschnittstelle, die dauerhafte Verbindungen herstellt: Wenn Ihr Skript während eines Datenbankvorgangs unerwartet beendet wird, wird die nächste Anforderung, die die verbleibende Verbindung erhält, dort fortgesetzt, wo das tote Skript aufgehört hat. Die Verbindung wird auf Prozessmanagerebene offen gehalten (Apache für mod_php, der aktuelle FastCGI-Prozess, wenn Sie FastCGI verwenden usw.), nicht auf PHP-Ebene, und PHP weist den übergeordneten Prozess nicht an, die Verbindung zu beenden, wenn Das Skript wird abnormal beendet.

Wenn das tote Skript Tabellen gesperrt hat, bleiben diese Tabellen gesperrt, bis die Verbindung unterbrochen wird oder das nächste Skript, das die Verbindung erhält, die Tabellen selbst entsperrt.

Wenn sich das Dead-Skript mitten in einer Transaktion befand, kann dies eine Vielzahl von Tabellen blockieren, bis der Deadlock-Timer aktiviert wird, und selbst dann kann der Deadlock-Timer die neuere Anforderung anstelle der älteren Anforderung, die das Problem verursacht, beenden.

Wenn sich das tote Skript mitten in einer Transaktion befand, erhält das nächste Skript, das diese Verbindung erhält, auch den Transaktionsstatus. Es ist sehr wahrscheinlich (abhängig von Ihrem Anwendungsdesign), dass das nächste Skript möglicherweise nie versucht, die vorhandene Transaktion festzuschreiben, oder festschreibt, wenn dies nicht der Fall sein sollte, oder ein Rollback, wenn dies nicht der Fall sein sollte.

Dies ist nur die Spitze des Eisbergs. Dies kann bis zu einem gewissen Grad gemildert werden, indem bei jeder einzelnen Skriptanforderung immer versucht wird, nach einer fehlerhaften Verbindung aufzuräumen. Dies kann jedoch je nach Datenbank problematisch sein. Es sei denn , Sie identifiziert haben Datenbankverbindungen wie das Erstellen der eine Sache , die ein Engpass in Ihrem Skript (das heißt , Sie done Code haben Profilierungs mit xdebug und / oder xhprof ), sollten Sie nicht persistente Verbindungen als Lösung für alles in Betracht ziehen.

Darüber hinaus haben die meisten modernen Datenbanken (einschließlich PostgreSQL) ihre eigenen bevorzugten Methoden zum Durchführen von Verbindungspools, die nicht die unmittelbaren Nachteile aufweisen, die einfache Vanilla-PHP-basierte persistente Verbindungen haben.


Um einen Punkt zu verdeutlichen, verwenden wir an meinem Arbeitsplatz dauerhafte Verbindungen, jedoch nicht nach Wahl. Wir hatten ein seltsames Verbindungsverhalten, bei dem die anfängliche Verbindung von unserem App-Server zu unserem Datenbankserver genau drei Sekunden dauerte, wenn sie einen Bruchteil einer Sekunde hätte dauern sollen. Wir denken, es ist ein Kernel-Bug. Wir haben es aufgegeben, Fehler zu beheben, da dies zufällig geschah und nicht bei Bedarf reproduziert werden konnte und unsere ausgelagerte IT nicht die konkrete Fähigkeit hatte, es aufzuspüren.

Unabhängig davon, wann die Leute im Lager ein paar hundert eingehende Teile bearbeiten und jedes Teil dreieinhalb Sekunden statt einer halben Sekunde dauert, mussten wir Maßnahmen ergreifen, bevor sie uns alle entführten und uns dazu brachten, ihnen zu helfen. Wir haben also ein paar Kleinigkeiten in unserer selbst entwickelten ERP / CRM / CMS-Monstrosität auf den Kopf gestellt und alle Schrecken dauerhafter Verbindungen aus erster Hand erlebt. Wir haben Wochen gebraucht, um all die subtilen kleinen Probleme und bizarren Verhaltensweisen aufzuspüren, die scheinbar zufällig passiert sind. Es stellte sich heraus, dass diese einmal pro Woche schwerwiegenden Fehler, die unsere Benutzer fleißig aus unserer App herausgedrückt hatten, gesperrte Tabellen, abgebrochene Transaktionen und andere unglückliche Wonky-Zustände hinterließen.

Diese Schluchzergeschichte hat einen Punkt: Sie hat Dinge gebrochen, von denen wir nie erwartet hatten, dass sie brechen, alles im Namen der Aufführung. Der Kompromiss hat sich nicht gelohnt und wir warten gespannt auf den Tag, an dem wir ohne Aufruhr unserer Benutzer zu normalen Verbindungen zurückkehren können.

Charles
quelle
2
Ich hoffe, ich hatte diese Antwort vor dem Laufen gelesenSELECT orders.* FROM orders LEFT JOIN items USING(item_id)
Ast Derek
31
Ich kenne eine große Website, die seit fast einem Jahrzehnt dauerhafte Verbindungen verwendet. Der Trick besteht darin, eine Ebene über der DB-Erweiterung zu verwenden und sie an die Dinge zu erinnern, die mithilfe von bereinigt werden müssen register_shutdown_function(). Wenn der Prozess abbricht, stirbt auch die Verbindung. Ist dies nicht der Fall, wird die Verbindung auf ihren sauberen Zustand zurückgesetzt (z. B. werden offene Transaktionen zurückgesetzt). Wenn dies fehlschlägt, wird die Verbindung geschlossen und bei der nächsten Anforderung an denselben Prozess wird eine neue geöffnet. Es besteht keine Notwendigkeit, dauerhafte Verbindungen zu dämonisieren.
Walter Tross
Ich bin neugierig @Charles ... wurde Ihr Problem jemals gelöst?
Tschallacka
@MichaelDibbets Wir haben den Anwendungsserver vor einigen Monaten ausgetauscht und pconnect deaktiviert, um festzustellen, ob der Fehler von drei Sekunden noch besteht. Es war nicht. Es wurde durch einen Proxy gelöst, nehme ich an. Die folgende Antwort in Bezug auf mysqli_change_userist wahrscheinlich immer noch die beste Problemumgehung für Personen, die dauerhafte Verbindungen in einer Anwendung herstellen müssen, die nicht für die Behandlung staatlicher Probleme ausgelegt ist.
Charles
5
Wir hatten eine Verzögerung von 5 Sekunden beim Verbinden, die wir als DNS + IPv6-Problem isolieren konnten. Der Server suchte nach einer v6-Adresse, schlug fehl und verwendete dann die IPv4-Adresse.
Nigel Atkinson
45

Als Antwort auf Charles 'Problem oben,

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

Eine häufige Beschwerde über dauerhafte Verbindungen ist, dass ihr Status vor der Wiederverwendung nicht zurückgesetzt wird. Beispielsweise werden offene und nicht abgeschlossene Transaktionen nicht automatisch zurückgesetzt. Aber auch Autorisierungsänderungen, die in der Zeit zwischen dem Einfügen der Verbindung in den Pool und der Wiederverwendung aufgetreten sind, werden nicht berücksichtigt. Dies kann als unerwünschte Nebenwirkung angesehen werden. Im Gegenteil, der Name persistent kann als Versprechen verstanden werden, dass der Staat bestehen bleibt.

Die mysqli-Erweiterung unterstützt beide Interpretationen einer dauerhaften Verbindung: Status bleibt bestehen und Status wird vor der Wiederverwendung zurückgesetzt. Der Standardwert wird zurückgesetzt. Bevor eine dauerhafte Verbindung wiederverwendet wird, ruft die mysqli-Erweiterung implizit mysqli_change_user()auf, um den Status zurückzusetzen. Die dauerhafte Verbindung erscheint dem Benutzer so, als wäre sie gerade geöffnet worden. Es sind keine Artefakte aus früheren Verwendungen sichtbar.

Die mysqli_change_user()Funktion ist eine teure Operation. Um eine optimale Leistung zu erzielen, möchten Benutzer die Erweiterung möglicherweise neu kompilieren, wobei das Kompilierungsflag MYSQLI_NO_CHANGE_USER_ON_PCONNECTgesetzt ist.

Es bleibt dem Benutzer überlassen, zwischen sicherem Verhalten und bester Leistung zu wählen. Beides sind gültige Optimierungsziele. Aus Gründen der Benutzerfreundlichkeit wurde das sichere Verhalten auf Kosten maximaler Leistung zum Standard gemacht.

Prashant
quelle
+1, wenn nicht die Tatsache, dass wir das Chaos auf andere Weise beseitigt haben , würde ich gerne sehen, ob das manuelle Aufrufen von change_user unsere bizarren Probleme mit unbekanntem Status behoben hätte.
Charles
Was ist das Äquivalent für dauerhafte Verbindungen von PDO Postgres? Ich habe ähnliche Probleme wie @Charles, bei denen Benutzer nach einer Weile Fehler wie das Abrufen von SQL erhalten - Server hat die Verbindung unerwartet geschlossen. Dies bedeutet wahrscheinlich, dass der Server abnormal beendet wurde, wenn eine einfache SELECT-Abfrage ausgeführt wurde (nicht einmal Transaktionen).
Carmageddon
1
@Carmageddon, das ist besser für eine neue Frage geeignet, aber das Problem ist, dass Postgres keine Verbindung herstellt und Sie stattdessen einen der externen Verbindungspools verwenden sollten.
Charles
@ Charles, was meinst du damit? Entspricht die Verwendung der dauerhaften Verbindung des PDO nicht der Verwendung von "externen Verbindungspools"? oder was hast du gemeint
Carmageddon
@Carmageddon, ich meine, dass sich die Postgres-Community für das Zusammenlegen von Verbindungen als bessere Lösung als pconnect entschieden hat. Schauen Sie sich pgbouncer oder pgpool-II an. Ich bin mir nicht sicher, ob PDO Postgres pconnect trotzdem ausführt, aber ich bin möglicherweise völlig außer mir.
Charles
13

Permanente Verbindungen sind nur dann eine gute Idee, wenn die Verbindung zu Ihrer Datenbank (relativ) lange dauert. Heutzutage ist das fast nie der Fall. Der größte Nachteil bei dauerhaften Verbindungen besteht darin, dass die Anzahl der Benutzer, die Ihre Website durchsuchen können, begrenzt ist: Wenn MySQL so konfiguriert ist, dass nur 10 gleichzeitige Verbindungen gleichzeitig zulässig sind, funktioniert dies nicht für sie, wenn eine elfte Person versucht, Ihre Website zu durchsuchen .

PDO verwaltet die Persistenz nicht. Der MySQL-Treiber tut dies. Verbindungen werden wiederverwendet, wenn a) sie verfügbar sind und Host / Benutzer / Kennwort / Datenbank übereinstimmen. Wenn sich etwas ändert, wird eine Verbindung nicht wiederverwendet. Der beste Nettoeffekt ist, dass diese Verbindungen so oft gestartet und gestoppt werden, weil Sie verschiedene Benutzer auf der Site haben und es nicht gut tut, sie dauerhaft zu machen.

Das Wichtigste, was Sie über dauerhafte Verbindungen wissen sollten, ist, dass Sie sie in den meisten Webanwendungen NICHT verwenden sollten. Sie klingen verlockend, sind aber gefährlich und ziemlich nutzlos.

Ich bin sicher, dass es andere Threads gibt, aber eine dauerhafte Verbindung ist gefährlich, da sie zwischen Anforderungen bestehen bleibt. Wenn Sie beispielsweise eine Tabelle während einer Anforderung sperren und dann nicht entsperren können, bleibt diese Tabelle auf unbestimmte Zeit gesperrt. Permanente Verbindungen sind für 99% Ihrer Apps praktisch nutzlos, da Sie nicht wissen können, ob dieselbe Verbindung zwischen verschiedenen Anforderungen verwendet wird. Jeder Web-Thread verfügt über einen eigenen Satz dauerhafter Verbindungen, und Sie können nicht steuern, welcher Thread welche Anforderungen verarbeitet.

Die prozedurale MySQL-Bibliothek von PHP verfügt über eine Funktion, mit der nachfolgende Aufrufe von MySQL_connect denselben Link zurückgeben, anstatt eine andere Verbindung zu öffnen (wie zu erwarten). Dies hat nichts mit dauerhaften Verbindungen zu tun und ist spezifisch für die MySQL-Bibliothek. PDO zeigt kein solches Verhalten


Ressourcenlink: Link

Im Allgemeinen können Sie dies als groben "Regelsatz" verwenden ::

JA , verwenden Sie dauerhafte Verbindungen, wenn:

  • Es gibt nur wenige Anwendungen / Benutzer, die auf die Datenbank zugreifen, dh Sie führen nicht zu 200 offenen (aber wahrscheinlich inaktiven) Verbindungen, da 200 verschiedene Benutzer auf demselben Host gemeinsam genutzt werden.
  • Die Datenbank wird auf einem anderen Server ausgeführt, auf den Sie über das Netzwerk zugreifen

  • Eine (eine) Anwendung greift sehr oft auf die Datenbank zu

NEIN , verwenden Sie keine dauerhaften Verbindungen, wenn:

  • Ihre Anwendung muss nur 100 Mal pro Stunde auf die Datenbank zugreifen.

  • Sie haben viele, viele Webserver, die auf einen Datenbankserver zugreifen

Die Verwendung dauerhafter Verbindungen ist erheblich schneller, insbesondere wenn Sie über ein Netzwerk auf die Datenbank zugreifen. Es macht keinen großen Unterschied, ob die Datenbank auf demselben Computer ausgeführt wird, aber sie ist immer noch etwas schneller. Wie der Name schon sagt, bleibt die Verbindung jedoch bestehen, dh sie bleibt offen, auch wenn sie nicht verwendet wird.

Das Problem dabei ist, dass MySQL in der "Standardkonfiguration" nur 1000 parallele "offene Kanäle" zulässt. Danach werden neue Verbindungen abgelehnt (Sie können diese Einstellung anpassen). Wenn Sie also beispielsweise 20 Webserver mit jeweils 100 Clients haben und jeder von ihnen nur einen Seitenzugriff pro Stunde hat, zeigt Ihnen eine einfache Mathematik, dass Sie 2000 parallele Verbindungen zur Datenbank benötigen. Das wird nicht funktionieren.

Ergo: Verwenden Sie es nur für Anwendungen mit vielen Anfragen.

Jhonathan H.
quelle
4
Nach der Zeile ist Ihre Antwort Kopieren Einfügen von stackoverflow.com/a/51583/718224
Tony Stark
1
"JA, verwenden Sie dauerhafte Verbindungen, wenn: [...] nur wenige Anwendungen / Benutzer auf die Datenbank zugreifen" widerspricht "Nur für Anwendungen mit vielen Anforderungen verwenden". Letzteres ist jedoch richtig. Situation: Tausende von Anforderungen pro Sekunde führen zu Hunderten von aktiven Datenbankverbindungen. Wenn ein System linear skaliert, wird auch die Anzahl der Verbindungen zur Datenbank linear skaliert. Mehr Anfragen (mehr Benutzer) führen also zu mehr Verbindungen. Sie benötigen also begrenzte (!)
Und
12

Bei meinen Tests hatte ich eine Verbindungszeit von mehr als einer Sekunde zu meinem lokalen Host, daher wurde davon ausgegangen, dass ich eine dauerhafte Verbindung verwenden sollte. Weitere Tests zeigten, dass es sich um ein Problem mit 'localhost' handelt:

Testergebnisse in Sekunden (gemessen durch PHP-Mikrozeit):

  • gehostetes Web: connectDB: 0.0038912296295166
  • localhost: connectDB: 1.0214691162109 (über eine Sekunde: Verwenden Sie localhost nicht!)
  • 127.0.0.1: connectDB: 0,00097203254699707

Interessanterweise: Der folgende Code ist genauso schnell wie die Verwendung von 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));
Gunnar Bernstein
quelle
Es scheint, als hätte PDO Schwierigkeiten, Domainnamen zu übersetzen! Vielen Dank, ich habe mich gefragt, warum jede Verbindung auf meinem Quad-Core-Computer verdammt lange gedauert hat!
Mustafa
@ Gunnar Bernstein +1 schöner Fund. "localhost" dauert sicherlich länger und dies hat die Geschwindigkeit meiner Web-App etwas verbessert (es werden viele Verbindungen hergestellt).
imperium2335
1
Das ist toll. Irgendetwas stimmt nicht mit der Auflösung auf meinem Entwicklungscomputer ... Durch die Verwendung einer IP wurde mein Skript von 6,1 auf 1,1 Sekunden verschoben
Pete
localhostverwendet Socket-Verbindung, Socket-Verbindung ist bekannt dafür, dass sie bei einer großen Anzahl von Verbindungen schlecht ist
mente
@mente Gibt es eine Referenz, eine Ressource, die diese Tatsache beweisen kann? Ich würde eher denken, dass UDS TCP vorgezogen werden. Vielen Dank.
Nuxwin
6

Dauerhafte Verbindungen sollten eine beträchtliche Leistungssteigerung bewirken. Ich bin nicht einverstanden mit der Behauptung, dass Sie Beharrlichkeit "vermeiden" sollten.

Es hört sich so an, als würden die oben genannten Beschwerden von jemandem verursacht, der MyIASM-Tabellen verwendet und seine eigenen Transaktionsversionen hackt, indem er sich Tabellensperren schnappt. Natürlich werden Sie zum Stillstand kommen! Verwenden Sie beginTransaction () von PDO und verschieben Sie Ihre Tabellen in InnoDB.

Stephen
quelle
2
Ein Jahr später, stelle ich fest, aber um es festzuhalten: Meine Geschichte stammt aus einer Datenbank, die vollständig aus InnoDB-Tabellen besteht, mit Ausnahme einer Handvoll denormalisierter Klone, die im Sumpf von MyISAM stecken, um die Volltextindizierung zu unterstützen.
Charles
Pfft, Sphinx ist alt und kaputt, ElasticSearch ist die neue Schärfe. Eines schönen Tages werden wir es tatsächlich für unsere alten Apps verwenden, anstatt nur für die neuen ...
Charles
Die Volltextsuche in PostgreSQL ist der wahre Gewinner. Es ist wunderbar. Erfordert kein anderes Tool / Server, das ausgeführt wird, um seine Arbeit zu erledigen. Sie müssen sich keine Sorgen machen, dass die Daten synchron bleiben. Sehr granulare Kontrollen. Mehrere Wörterbücher oder schreiben Sie Ihre eigenen. Und da PostgreSQL automatisch Multi-Index-Abfragen verwendet, können Sie diese einfach bei jeder anderen Abfrage ablegen, die Sie ausführen.
Brightball
2
MySQL 5.6 bietet Volltextunterstützung für InnoDB-Tabellen.
Zeitplan
2

Eine dauerhafte Verbindung scheint mir mehr Systemressourcen zu verbrauchen. Vielleicht eine unbedeutende Menge, aber immer noch ...

Wachsmalstift gewalttätig
quelle
Oft ein Handel mit viel menschlicher Zeit für Mikrosekunden Computerzeit
Andy Chase
1

Die Erklärung für die Verwendung dauerhafter Verbindungen ist offensichtlich die Reduzierung der Anzahl der Verbindungen, die ziemlich kostspielig sind, obwohl sie mit MySQL im Vergleich zu anderen Datenbanken erheblich schneller sind.

Das allererste Problem mit dauerhaften Verbindungen ...

Wenn Sie Tausende von Verbindungen pro Sekunde erstellen, stellen Sie normalerweise nicht sicher, dass diese sehr lange geöffnet bleiben, das Betriebssystem jedoch. Basierend auf dem TCP / IP-Protokoll können Ports nicht sofort recycelt werden und müssen auch eine Weile in die Phase „FIN“ investieren, bevor sie möglicherweise recycelt werden.

Das zweite Problem ... mit vielen MySQL-Serververbindungen.

Viele Leute wissen einfach nicht, dass Sie in der Lage sind, die Variable * max_connections * zu erhöhen und über 100 gleichzeitige Verbindungen mit MySQL zu erhalten. Andere wurden von älteren Linux-Problemen geschlagen, weil sie nicht mehr als 1024 Verbindungen mit MySQL übertragen konnten.

Ermöglicht jetzt die Diskussion darüber, warum dauerhafte Verbindungen in der mysqli-Erweiterung deaktiviert wurden. Trotz der Tatsache, dass Sie dauerhafte Verbindungen missbrauchen und eine schlechte Leistung erzielen können, war dies nicht der Hauptgrund. Der eigentliche Grund ist - Sie können viel mehr Probleme damit bekommen.

In MySQL 3.22 / 3.23 wurden dauerhafte Verbindungen in PHP hergestellt, wenn MySQL nicht so schwierig war, was bedeutet, dass Sie Verbindungen problemlos recyceln konnten. In späteren Versionen kam es jedoch zu einer Reihe von Problemen. - Wenn Sie eine Verbindung mit nicht festgeschriebenen Transaktionen recyceln, treten Probleme auf. Wenn Sie Verbindungen mit benutzerdefinierten Zeichensatzkonfigurationen recyceln, sind Sie erneut in Gefahr und möglicherweise pro Sitzung transformierte Variablen.

Ein Problem bei der Verwendung dauerhafter Verbindungen ist, dass sie nicht wirklich so gut skaliert werden können. Für diejenigen, die 5000 Personen verbunden haben, benötigen Sie 5000 dauerhafte Verbindungen. Um das Erfordernis der Persistenz zu beseitigen, haben Sie möglicherweise die Möglichkeit, 10000 Personen mit einer ähnlichen Anzahl von Verbindungen zu bedienen, da sie in der Lage sind, einzelne Verbindungen zu teilen, wenn sie nicht mit ihnen verbunden sind.

Tony Stark
quelle
0

Ich habe mich nur gefragt, ob eine Teillösung darin bestehen würde, einen Pool von einmaligen Verbindungen zu haben. Sie können Zeit damit verbringen, einen Verbindungspool zu erstellen, wenn das System bis zu einem gewissen Grad nur wenig ausgelastet ist, diese zu verteilen und zu beenden, wenn sie entweder abgeschlossen sind oder eine Zeitüberschreitung aufweisen. Im Hintergrund erstellen Sie neue Verbindungen, während sie aufgenommen werden. Im schlimmsten Fall sollte dies nur so langsam sein wie das Herstellen der Verbindung ohne den Pool, vorausgesetzt, dass das Herstellen der Verbindung der begrenzende Faktor ist?

James
quelle