• Nimmer Ärger mit den Persistenten Verbindungen von MySQL? (Teil 1/2)

    von am 18. Februar 2009

    Harry ist da. Harry hat sich in die Beta-Version von PHP 5.3 eingeschlichen. Es ist der sprichwörtliche Harry, der immer Ärger macht: seit PHP 5.3 kann ext/mysqli Persistente Verbindungen aufbauen.

    Als Persistente Verbindungen werden Ressourcen bezeichnet, die über die Dauer einer Webanfrage hinaus zur Verfügung stehen. Ressourcen deren Auf- und Abbau besonders rechnen- oder speicherintensiv ist, sind ideale Kandidaten für Persistente Verbindungen. Persistente Verbindungen stehen jedoch nicht immer zur Verfügung.

    Resourcen lassen sich nicht persistieren, oder doch?

    PHP operiert anfragebasiert. PHP wurde entwickelt um genau eine Anfrage an einen Webserver zu verarbeiten. Eine PHP-Anwendung wird nach Beantwortung einer Anfrage beendet. Dabei geht das Datensegment der PHP-Anwendung verloren. Um den Status einer PHP-Anwendung, ihr Datensegment, über die Dauer einer Anfrage hinaus zu erhalten, wurde zu Zeiten von PHP 3 mit der phpLib erstmals eine Session-Bibliothek vorgestellt. Die phpLib von NetUSE wurde zum Vorbild der in PHP 4 eingeführten Session-Erweiterung. Seitdem können PHP-Anwendungen ihre Daten persistieren. Die Session-Erweiterung speichert Variablen serialisiert in einem nicht-flüchtigem Medium. Bei Bedarf deserialisiert die Erweiterung die Informationen und importiert sie als Variablen in das Datensegment einer neu gestarteten PHP-Anwendung.

    Ressourcen wie ein Dateihandle oder eine Datenbankverbindung lassen sich nicht serialisieren. Sie sind nicht mit dem Daten- sondern dem Programmsegment einer Anwendung verbunden. Mit der Beendigung einer Anwendung geht die Assoziation verloren. Es ist nicht möglich die Ressource an das Programmsegment einer neuen PHP-Anwendung, einem neuen PHP-Prozess zu binden.

    Überdauert ein PHP-Prozess im Gegensatz zum Programm, welches er ausführt, eine Webanfrage so können auch die vom Prozess geöffneten Handles die Webanfrage überdauern. Ein PHP-Prozess überlebt eine Webanfrage, wenn er beispielsweise als Webservermodul ausgeführt wird. In diesem Deployment-Modell, kann der PHP-Prozess den PHP-Anwendungen, die vom ihm ausgeführt werden, Persistente Verbindungen zur Verfügung stehen. Der PHP-Prozess poolt Verbindungen, er cacht sie.

    Mein alter Freund der Cache

    Eine MySQL-Datenbankverbindung ist schnell auf- und abgebaut. Der MySQL Server verwendet für jede Verbindung einen Thread. Der Thread wird aus einem vorallokierten Pool entnommen.

    Harry greift trotzdem zu Slim Fast. Egal wie “leichtgewichtig” ein Verbindungsaufbau ist, die Wiederverwendung einer Verbindung ist schneller. Außerdem darf das Flaggschiff ext/mysqli nicht weniger bieten als ext/mysql und PDO_MYSQL, egal wie gruselig die Syntax ausfällt:
    mysqli_connect(“p:localhost”, ...)
    Ein Skript demonstriert das theoretische Potential des Cachings. Das Skript kann auf der Kommandozeile ausgeführt werden. Weil der PHP-Prozess nicht wechselt, können innerhalb der Schleife (persistente) Ressourcen wiederverwendet werden.

    for ($i = 0; $i < 100000; $i++) {
       $mysqli = new mysqli("p:localhost", "root", "root", "test");
       $mysqli->close();
    }

    Beim ersten Schleifendurchlauf baut ext/mysqli eine Verbindung zur Datenbank auf und legt die Verbindung im Cache ab. Der Aufruf der close()-Methode schließt die Verbindung nicht. Die Verbindung verbleibt im Cache und wird als wiederverwendbar markiert. Beim zweiten und allen folgenden Schleifendurchläufen wird geprüft ob im Cache eine freie Verbindung für die angegebenen Verbindungsparameter abgelegt ist. Da dies im Beispiel der Fall ist, wird die Verbindung entnommen. ext/mysqli prüft anschließend mittels eines COM_PING ob ein Timeout aufgetreten ist. Falls ja, wird die Verbindung neu aufgebaut. Falls nein, wird die Verbindung dem PHP-Skript zur Verfügung gestellt.

    78000 Connections pro Sekunde mit MySQL!

    Auf diese Weise erzielt mein Desktop-PC mit PHP 5.3 RC4 fast 47000 Schleifendurchläufe pro Sekunde. Mit einem C-Programm werden sogar über 78000 Schleifendurchläufe pro Sekunde erreicht. Entfernt man das “p:” aus dem Hostparameter des Konstruktors der MySQLi-Klasse, verwendet man keine Persistenten Verbindungen, erreicht PHP (mysqlnd) nur noch 1816 Schleifendurchläufe pro Sekunde. Ein vergleichbares C-Programm (libmysql) sinkt sogar auf 1783 Schleifendurchläufe.
    grafik1
    Der Test demonstriert das theoretische Potential von Persistenten Verbindungen. Sein Praxiswert ist jedoch gering.

    Die Zahlen lassen sich durch den Einsatz einer schnelleren CPU weiter steigern. Der verwendete Rechner verfügt über einen Intel Core 2 Duo Prozessor E6750 der mit 2.66 GHz getaktet ist. Einer der beiden CPU-Cores wird zu 100% von MySQL 5.1.30 ausgelastet. Das Skript ist der einzige Client des MySQL Servers. Es wird eine Verbindung geöffnet und der Server verwendet einen Thread zum Abarbeitung. Dieser Thread lastet einen der zwei CPU-Cores vollständig aus. Der Test ist also CPU limitiert. Hardwareanfragen sind an die Marketingabteilung zu richten.

    Was die Geschwindigkeit beeinflusst

    Jede Datenbankverbindung allokiert und blockiert knappe Ressourcen auf einem Datenbankserver. Applikationsentwickler sollte deshalb so wenig Verbindungen wie nötig öffnen. Der obige Benchmark jedoch öffnet und schließt viele Verbindungen ohne eine Anfrage zu senden. Dieses Verhalten ist nur bei wenigen, ineffizienten Programmen anzutreffen.

    Durch das Einfügen einer Anfrage wird praktische Aussagekraft des Benchmarks gesteigert:

    for ($i = 0; $i < 100000; $i++) {
    	$mysqli = new mysqli("p:localhost", "root", "root", "test");
    	$res = $mysqli->query("SELECT 1");
    	while ($row = $res->fetch_row())
    		;
    	$res->close();
    	$mysqli->close();
    }

    Eine erneute Messung zeigt, dass ohne Persistente Verbindungen 1522 Schleifendurchläufe pro Sekunde erzielt werden. Beim Einsatz von Persistenten Verbindungen erfolgt eine Steigerung auf mehr als das siebenfache – 11056. Obschon dies beeindruckend ist, ist es nur ein Viertel des vorhergehenden Benchmarks.
    grafik2
    Die Zahl ist heiß! Richtig, Harry. Aber wer führt schon “SELECT 1” aus? Die Zahlenspiele lassen sich beliebig fortführen ohne viel Praxiswert. Das Verhältnis von Verbindungskosten zu Ausführungskosten hat wesentlichen Einfluss auf das Ergebnis der Messung. Dieses variiert mit jedem Anwendungsfall. Für jede Anwendung ist zu prüfen, ob der Verbindungsaufbau einen signifikanten Einfluss auf die Gesamtlaufzeit hat. Eigene Messungen sind Pflicht.

    Betrachtet man das Gesamtsystem aus Applikations-, Web- und Datenbankserver sind Performanzverbesserungen keinesfalls garantiert. Ein exzessives Caching von Verbindungen kann den Datenbankserver stark belasten. Jede Verbindung entspricht einem Thread im MySQL-Server. Jeder Thread verbraucht und blockiert Ressourcen wie beispielsweise Hauptspeicher oder Dateihandles. Viele gleichzeitig geöffnete – möglicherweise nicht genutzte Verbindungen – führen zu einem hohen Ressourcenverbrauch. Damit einhergehend kann die Leistung der Datenbank sinken. Der Datenbankserver wird überlastet.

    Mit den PHP-Konfigurationsdirektiven mysqli.allow_persistent, mysqli.max_persistent, mysqli.max_links und der MySQL Serveroption max_connections können Grenzwerte gesetzt werden, um einer Überlastung zu vorzubeugen. Der php.ini-Wert mysqli.max_links begrenzt die maximale Anzahl der Summe aller persistenten- und nicht-persistenten Verbindungen, die ein PHP-Prozess öffnen darf. Mittels mysqli.max_persistent wird eine Obergrenze für persistente Verbindungen definiert. Wird eines der Limits erreicht, lehnt ext/mysqli den Aufbau der angeforderten Verbindung ab. Dem Wert -1 kommt eine besondere Bedeutung zu. Er steht für unendlich.

    Beim Setzen der MySQL Serveroption max_connections ist zu bedenken, dass mehrere PHP-Prozesse gleichzeitig auf einen Server zugreifen können.

    Ulf Wendel

    Ulf Wendel arbeitet als Senior Software Engineer im Connectors-Team bei MySQL/Sun. Er verdiente sich von 1997-2004 seine Brötchen als Entwickler von PHP und MySQL basierten Webanwendungen. In dieser Zeit ...

    Zum Profil von Ulf Wendel

    7 Kommentare »


    • admin
      am 18. Februar 2009 um 13:54 Uhr

      In meinem derzeitigen Projekt feuern wir so viele Datenbank Anfrage ab, dass es schon fast unglaubwürdig ist. Leider kommt PHP 5.3 erst nach dem Tag, an dem ich das Projekt verlasse. Würde echt gerne mal ein wenig mit pers. Verbindungen rumprobieren.


    • Ulf Wendel
      am 18. Februar 2009 um 15:41 Uhr

      Ich habe absichtlich provokante und extreme Beispiele in den Grafiken gewählt. Ob und in welchem Rahmen ein Projekt von Persistenten Verbindungen profitiert hängt von vielen Faktoren ab. Desto großer der Einfluss des Verbindungsaufbau (langsame Anbindung an TukaHost aus TakaTuka-Land) auf die Gesamtlaufzeit ist, desto mehr macht es Sinn auf Persistent Verbindungen zu schielen.

      Beim Dell DVD-Benchmark, den die c’t vor Jahren für ihren Datenbank-Shoutout gewählt hat, hat es etwas gebracht. Es hat so viel gebracht, daß der MySQL Wettbewerbsbeitrag ext/mysql und nicht ext/mysqli verwendet hat. Und in den Messungen, die ich zu Beginn der mysqlnd-Entwicklung gemacht habe, habe ich Persistente Verbindungen in ext/mysqli schmerzlich vermisst…

      Aber wie immer bei Detailoptimierungen kann ich nur auffordern im Einzelfall zu prüfen ob eine Optimierung mehr hilft oder schadet.

      Im zweiten Teil des Artikels geht es um das neue, alte “ABER”…. Ein großes “ABER”.

      Ulf


    • Timo Holzherr
      am 18. Februar 2009 um 23:47 Uhr

      Hi Ulf,

      bin sehr gespannt auf deinen zweiten Teil!

      Liebe Grüße,
      Timo


    • Internet Super Hero » Blog Archive » PHP 5.3: Persistent Connections with ext/mysqli
      am 19. Februar 2009 um 09:07 Uhr

      [...] what Persistent Database Connections are and you speak german I would like to point you to my guest posting [...]


    • Blog der Woche „PHP hates me“: „Als ich angefangen habe, gab es kaum gute deutschsprachige PHP-Blogs“ » t3n Magazin
      am 23. März 2009 um 10:39 Uhr

      [...] Die meisten Besucher: Nimmer Ärger mit den Persistenten Verbindungen von MySQL? [...]


    • Using MySQL with PHP mysqli: Connections, Options, Pooling | Dacoders Pvt. Ltd.
      am 9. November 2011 um 02:54 Uhr

      [...] Of course you, as a german reader, know it. I blogged about it in 2009 over at phphatesme.com (Nimmer Ärger mit den Persistenten Verbindungen von MySQL? ) [...]


    • Supercharging PHP MySQL applications using the best API | Ulf WendelUlf Wendel
      am 22. November 2012 um 19:03 Uhr

      [...] pitfall" persistent connections is performance (german speaking readers can find details here and [...]

    RSS Feed für Kommentare zu diesem Artikel. TrackBack URL

    Hinterlasse einen Kommentar

    Werbung
    PHP Magazin
    Ausgabe 02/2010

    Dieses Mal mit Artikeln zu den Themen OpenSocial und Apache Shindig, Graphentheorie, Smarty3

    t3n
    Ausgabe 19

    Social Media (R)evolution. Weitere Themen sind noSQL, Crowdsourcing ...

    PHP Journal
    Ausgabe 2/2010

    PHP & Windows optimal nutzen, die besten PHP-CMS im Überblick, Google-API mit Zend Framework nutzen.

    Wir wurden schon öfters gefragt, ob man uns nicht irgendwie unterstützen kann. Die Antwort war immer einfach: Klar! Am einfachsten ist es eure nächsten Einkäufe bei Amazon über unsere Link abzuwickeln. Damit würdet ihr uns schon sehr helfen. Über Co-Autoren freuen wir uns aber noch mehr.