Facebook
Twitter
Google+
Kommentare
8

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

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.

Über den Autor

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 arbeitete er für NetUSE (phpLib), stellte auf dem ersten deutschen PHP-Kongress im Jahr 2000 ein Dokumentationsskript vor (phpdoc.de) vor und genoß die Zeit als MySQL-Consultant zusammen mit Mayflower/thinkPHP für die Hypovereinsbank arbeiten zu dürfen. Von 2004-2006 kümmerte er sich als MaxDB Support Manager bei MySQL, um Support, Training und Consulting von MaxDB. Im Jahr 2006 wechselter er in das Connectors-Team Das Team betreut und entwickelt die MySQL-Treiber (JDBC, ODBC, C-API, PHP, ...), die es einem Client ermöglichen auf einen MySQL-Server zuzugreifen. Derzeit arbeitet Ulf an der PHP-MySQL Anbindung, so wie den Neuentwicklungen MySQL Connector/C++ und dem nativen MySQL Treiber für OpenOffice.org. Wenn es ihm die Zeit erlaubt, schreibt er gerne einmal einen Artikel. Zuletzt erschien der Artikel “Fünf Fragen zu MySQL” von ihm im Buch “Linux Technical Review 09: Datenbanken: Praxistipps von Entwurf bis Tuning”.
Kommentare

8 Comments

  1. 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.

    Reply
  2. 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

    Reply
  3. Cool Baby!

    Was so ein kleines P ausmacht. Ich weiß nicht warum, aber ohne (p:) schafft mein I7 bis 18 Verbindungen pro Sekunde. Nach mehrmaligen Durchläufen sogar bis etwas über hundert. Mit dem p: waren es knapp 300. Das ist schon ein vernüftiger Zuwachs. Ich muss jedoch erwähnen, dass ich nach new mysqli auch einen Query abgesetzt habe.

    Erst Mal danke für den Tipp!

    Gruß Thor

    Reply

Leave a Comment.

Link erfolgreich vorgeschlagen.

Vielen Dank, dass du einen Link vorgeschlagen hast. Wir werden ihn sobald wie möglich prüfen. Schließen