Facebook
0
Twitter
0
Google+
0
Kommentare
18

Ulf Wendel und das MySQL-Mofa

PHP (Paolo hat’s programmiert) ist gerade 15 Jahre alt geworden. Es darf jetzt Mofa fahren. Glückwunsch zur Mofa-Prüfbescheinigung, Finger weg von Alkohol, Zigaretten und Mädels. Und nichts tunen!

In 15 Jahren habe ich Rasmus zwei Fragen zu MySQL gestellt. Die erste Frage schickte ich an die php-generals Mailingliste. Ich hatte einen Fehler bei der Benutzung der MySQL Schnittstelle begangen und fand ihn nicht. Rasmus beantwortete meine Mail. Was für ein Gefühl – das erste Mal vergisst man nicht! Jahre später trafen wir uns auf dem ersten PHP-Vikinger in Skien. Ich fragte ihn was er sich von MySQL wünsche. Er sagte mir, daß MySQL dämlich sei. Was für ein Gefühl – auch das zweite Mal vergisst man nicht!

In Skien sprachen wir über Caching. Rasmus arbeitete in einer Firma die bei Lastproblemen einfach ein paar Lastwagen mit Servern bestellte. Es waren etwas andere Dimensionen als ich gewohnt war. Und es waren andere Ansprüche. Der Weg von PHP zum MySQL Query Cache des MySQL Servers war ihm zu weit und zu langsam. Und er fragte sich welche Hardware er für den MySQL Server und dessen MySQL Anfrage-Cache benutzen sollte, wenn ein weiterer Lastwagen von PHP-Webservern beginnt Anfragen an den MySQL Server zu senden. Als MySQL in Sun aufging hätte ich ihm passende Hardware anbieten können. Doch damals skalierte der MySQL Server nicht besonders gut bei vielen Cores und CPUs. Rasmus hätte den müden Versuch einer vertikalen Skalierung per Hardware vermutlich nicht einmal als Lacher in einen seiner Performanzvorträge aufgenommen. 2006 fragte Rasmus nach einem Client-Cache für horizontale Skalierung. Kaum vier Jahre später gibt es ein Cache Plugin für die mysqlnd-Datenbankbibliotek. Rasmus arbeitet inzwischen für eine andere Firma. Ich weiß nicht ob er auch heute noch einen Client-Cache braucht. Ist noch einer da, der ihn will?

(Kasten – Haupteigenschaften, Überschrift “Spaß beiseite – die wichtigsten Fakten” oder ähnlich)

Kurzbeschreibung Semi-transparenter clientseitiger Anfrage-Cache.
Unterstützte PHP MySQL Extensions ext/mysqli, PDO_MySQL, ext/mysql
Vorraussetzungen PHP 5.3.3-dev oder neuer, Verwendung von mysqlnd
Lizenz PHP Lizenz
Status Prototyp, erfüllt MySQL Alpha/Beta QA-Richtlinien
Programmiersprache C, stellt PHP-Schnittstelle bereit
Invalidierungsstrategie Time-to-live (TTL) oder benutzerdefiniert
Verdrängungsstrategie entsprechend Speicherhandler (keine oder benutzerdefiniert)
Speicherhandler Default: Prozessspeicher, APC: Shared Memory, Memory Mapped IO, Memcache, SQLite (Berkeley DB)
Speicherbare Anfragen (1.0.0-prototyp) Gepufferte nicht vorbereitete Anfragen
Nicht speicherbare Anfragen (1.0.0-prototyp) Ungepufferte Anfragen, Prepared Statements
Speicherbare Anfragen (1.0.1+) Wahrscheinlich alle
Projektseite http://forge.mysql.com/wiki/MySQLnd_Query_Cache_Plugin_for_PHP

Mofatuning

Wir sind jetzt 15, wir haben jetzt eine Mofa und nun tunen wir sie auch mit dem “mysqlnd query result cache plugin”, damit wir schneller zur Party kommen!

Der MySQL native driver for PHP (kurz: mysqlnd) ist eine in C geschriebene Datenbankbibliothek. Die Bibliothek ist in PHP 5.3 enthalten. Die C basierten PHP MySQL Erweiterungen können mysqlnd als Alternative MySQL Client Library (AKA libmysql, libmysql client, …) benutzen. Mysqlnd arbeitet also unterhalb der PHP-Anwendungen und unterhalb der PHP MySQL Erweiterungen im PHP selbst.

Vor kurzem wurde die erste Tuninganleitung für mysqlnd veröffentlich. Der Plugin-Leitfaden beschreibt wie die mysqlnd-Bibliothek mittels C-Programmierung um neue Funktionen erweitert werden kann. Das erste vom Haustuner Andrey Hristov stammende Tuningbauteil ist ein Client-Cache Plugin. Die genaue Bezichnung lautet “mysqlnd query result cache plugin for PHP”. In der Szene auch “mysqlnd query cache” oder “QC” genannt.

Drupal, phpMyFAQ, phpMyAdmin, Oxid, …
|
ext/mysql, ext/mysqli, ext/PDO_MYSQL
Mysqlnd
Mysqlnd plugin
Load Balancing Monitoring Performance: Cache
|
MySQL Server

Wie jedes andere mysqlnd-Plugin auch ist der QC in C geschrieben. Das entspricht dem was dem Spediteur dem keine Last zu schwer immer gepredigt hat: PHP für den Glue-Code, C für die Geschwindigkeit. Angenehmer Seiteneffekt: als C-Erweiterung der Datenbankbibliothek ist der Cache fast transparent und funktioniert mit allen bestehenden PHP MySQL Programmierschnittstellen und Anwendungen. Das Plugin ist einem clientseitigen transparenten Proxy ähnlich.

Mit 300 über die Bahn

QC in die Mofa reinstecken und sie rennt über 300 Sachen – schneller geht es nicht! Schön wäre es… Es ist unmöglich eine seriöse Aussage über mögliche Geschwindigkeitsgewinne zu treffen. Zu groß und zu zahlreich sind die Variablen. Jeder Anwender ist auf eigene Tests angewiesen. Nur so viel: mit einer kleinen 2-Rechner (1x App-Server, 1x DB-Server, jeweils 2-Core-CPUs) Konfiguration und dem Oxid eShop liegen der MySQL Anfrage-Cache des Servers und der Client-Cache gleichauf. Die meiste Zeit verbringen die CPUs nicht mit Datenbankanfragen und dem Warten auf Ergebnisse vom entfernten und sich langweilenden MySQL-Server sondern mit anderen Aufgaben. Deshalb gibt es kaum Unterschiede zwischen dem serverseitigem und dem clientseitigem Cache in genau dieser Konfiguration. Andere Konfiguration – andere Effekte. Wenn einer mal einen Lastwagen mit Servern…

Wenn ich als 15jähriger mit 300 über die Bahn donnern will, dann gehe ich nicht zu den anderen Kiddies, um meine Mofa zu tunen. Ich leihe mir den Sportwagen aus dem Villenviertel aus. Der Sportwagen wurde für die Rennstrecke konzipiert, die Mofa für den lokalen Nahverkehr. Eine Anwendung die vom ersten Tag an für gezieltes Caching optimiert wurde, braucht den QC nicht. Die Anwendung kann viel gezielter cachen. Sie kann Produkte von Datenbankergebnissen – wie ein HTML-Snippet – zwischenspeichern. Wer den Sportwagen in der Garage hat, braucht die Mofa nicht zu tunen. Wer eine Mofa nutzen muss, wer seine Anwendung nicht im großen Maße verändern will oder kann, wer eine schnelle Problemlösung bei minimalen Kosten will, der sollte sich den QC anschauen.

(Kasten/Exkurs)

Aber das geht doch auch mit PHP!

Die Zwischenspeicherung von Datenbankergebnissen auf dem Client ist ein alter Hut. Datenbankergebnisse sind mit wenigen Zeilen PHP-Code gesichert, hier ein schematisches Beispiel unter Verwendung von ext/mysqli und APC:

$cache_hit = false;
$rows = apc_fetch("slow_table", $cache_hit);
if (!$cache_hit) {
 $res = $mysqli->query("SELECT data FROM slow_table");
 $rows = $res->fetch_all();
 apc_store("slow_table", $rows);
}


Mit dem QC im Halbautomatikmodus ist es etwas einfacher:

$res = $mysqli->query("/*qc=on*/SELECT data FROM slow_table");
$rows = $res->fetch_all();

Mindestens so wichtig wie das Zählen der geänderten Zeilen ist es sich klar zu machen, daß ein PHP basierter Cache zwei Serialisierungen benutzt, während der QC mit einer auskommt. Eine Serialisierung wandelt Daten von einem Format in ein anderes, um sie transportieren zu können. Zur ersten Serialisierung kommt es innerhalb der MySQL Datenbank. MySQL wandelt im Hauptspeicher vorliegende Anfrageergebnisse in eine binäre Zeichenkette, um diesen über das Netzwerk an PHP zu senden. Dort angekommen werden die Netzwerkdaten von der Datenbankbibliothek (hier: mysqlnd) in eine PHP-Variable gewandelt. Diese Serialisierung tritt immer auf. Sie kann nicht eingespart werden.

Alle PHP basierte Zwischenspeicher müssen die PHP-Variable erneut serialisieren, um sie im Zwischenspeicher abzulegen. Im Beispiel wird diese zweite Serialisierung von apc_store()/apc_fetch() vorgenommen. Aus der PHP-Variablen wird eine Zeichenkette, die beim Cache-Miss im Cache abgelegt wird. Beim Cache-Hit erfolgt die Deserialiserung von der Zeichenkette in eine PHP-Variable.

PHP basierte Lösung mysqlnd query cache plugin (QC)
1. Serialisierung (MySQL) MySQL -> binäre Zeichenkette – Netzwerk -> PHP-Variable
2. Serialisierung (Cache) PHP-Variable -> Zeichenkette – Cache -> PHP-Variable entfällt (siehe Text)

Der QC benötigt keine zweite Serialisierung. Er speichert die aus der ersten Serialisierung stammenden Netzwerkdaten von MySQL zwischen. Es erfolgt keine Wandelung der vorhandenen Netzwerkdaten in eine andere Form, um die Daten im Cache ablegen zu können. Eine mögliche Fehlerquelle wird ausgeschlossen.

Die drei Betriebsarten des Cache-Plugins

Das Budget ist knapp, eine Villa ist weit und breit nicht zu sehen, das Geld hat kaum ausgereicht um eine vollgetankte Mofa zu kaufen. QC kommt gerade Recht. QC ist so einfach zu bedienen wie es der Tuninganfänger benötigt. Es gibt nur drei Betriebsarten: Vollautomatik, Halbautomatik, Manuell.

Die Vollautomatik speichert blind jede gepufferte, nicht preparte Datenbankanfrage (php.ini – mysqlnd_qc.cache_by_default = 1) für eine vorgegebene Anzahl von Sekunden (mysqlnd_qc.ttl = 30), deren Metadaten in allen Spalten einen Tabellennamen aufweisen (mysqlnd_qc.cache_no_table = 0). Eine gepufferte, nicht vorbereitete Datenbankanfrage von folgenden PHP MySQL Funktionsaufrufen generiert:

  • mysqli_query()
  • mysqli_real_query() + mysqli_store_result()
  • PDO::query(), PDO::exec(), PDO::prepare() wenn die Voreinstellung PDO::ATTR_EMULATE_PREPARES = 1 verwendet wird
  • mysql_query()

In der Standardeinstellung (mysqlnd_qc.cache_no_table = 0) werden nur diejenigen Anfragen zwischengespeichert, bei denen alle Ergebnisspalten in ihren Metadaten einen Tabellennamen zeigen. Damit soll verhindert werden, daß versehentlich Inhalte gespeichert werden, die nicht veraltern dürfen: SELECT SLEEP(1), SELECT CURTIME(), …

Zwischengespeicherte Anfragen werden für einen bestimmten Zeitraum aus dem Zwischenspeicher beantwortet. Die Invalidierungsstrategie ist Time-to-live (TTL). Beim Einsatz von TTL kann es zur Auslieferung von veralteten Daten kommen. Zwischengespeicherte Daten werden nicht automatisch invalidiert, wenn sich dem Anfrageergebnis zugrundeliegende Daten ändern. Diese Beschränkung kann im manuellen Modus aufgehoben werden (siehe unten).

Die Halbautomatik (mysqlnd_qc.cache_by_default = 1) ist der voreingestellte Standardbetriebsmodus des QC. Der eilige Mofa-Rennfahrer aktiviert den Turbo per Knopfdruck auf den Feldwegen, die nicht von den Rennkommissaren überwacht werden. Nur die Anwendung und der der Mofa-Rennfahrer kennen diese Feldwege. Nur sie verfügen über das Wissen, wann eine Anfrage zwischengespeichert werden darf und wann nicht, beispielsweise, weil sie einen Lagerbestand anzeigt.

Der QC bekommt seine Anweisungen per SQL-Hint. SQL-Hints sind SQL-Kommentare, die Aktionen auslösen. Für den Einsatz von SQL-Hints sind keine API-Änderungen an den bestehenden PHP MySQL Schnittstellen ext/mysqli, PDO_MySQL und ext/mysql notwendig. Es wird nur der Anfragetext verändert. Eine zu cachende Anfrage wird mit “/*qc=on*/” gekennzeichnet. Der SQL-Hint muss am Anfang der Anfrage stehen. Es darf ein optionaler, zweiter SQL-Hint “/*qc_ttl=n*/” folgenden, der die voreingestellte Standardlebenszeit (mysqlnd_qc.ttl) überstimmt.

$res = $mysqli->query("/*qc=on*/SELECT data FROM slow_table");
$res = $mysqli->query("/*qc=on*//*qc_ttl=3600*/SELECT news FROM hourly_update");

Das Tuningteil tunen: mehr als TTL

Mann kennt seine Pappenheimer. Auf dem Feldweg wird nie kontrolliert, weil es fast schon ein Privatweg ist. Hier darf die Mofa bei gutem Wetter alles geben: kachel Mann! Damit dies automatisch passiert, gibt es den manuellen Betriebsmodus bei dem der Anwender eigene Speicherhandler erstellt.

Die Speicherhandler des QC sind dafür zuständig eine zu cachende Anfrage zu erkennen. Die eingebauten Speicherhandler für Memcache, APC, Hauptspeicher und SQLite werden über SQL-Hints gesteuert. Benutzerdefinierte Handler können jede andere Form der Steuerung implementieren. Speicherhandler sind weiterhin für die Umsetzung einer Invalidierungs- und Verdrängungsstrategie zuständig. Die eingebauten Speicherhandler verwenden TTL als Invalidierungsstrategie. Benutzerdefinierte Handler können komplexere Invalidierungen umsetzen, beispielsweise um die Auslieferung von veralteten Daten vermeiden.

Die ersten Schritte mit eigenen Handlern lassen sich am Besten mit Ableitungen vom eingebauten Default-Handler (Speicherung der Daten im Prozessspeicher) machen. Der Default-Handler ist der einzige, welcher als Klasse in PHP zur Verfügung steht. Wer gleich alles tunen will, der registiert Callback-Funktionen oder implementiert das öffentliche Handlerinterface mysqlnd_qc_handler. Mehr dazu, irgendwann, in einem Folgeartikel.

class mofatuning extends mysqlnd_qc_handler_default {
 public function is_select($query) {
   if ($this->have_sun() && stristr($query, "feldweg")) {
     printf("... activating turbo for 60s!");
     return 60;
   }
   return parent::is_select($query);
 }
 private function have_sun() {
   return true;
 }
}

Die Methode is_select(string $query) ist dafür verantworlich zu prüfen, ob der Cache aktiviert werden soll und wenn ja welche TTL benutzt werden soll. Im Beispiel prüft is_select() ob die Sonne scheint und die Mofa auf einem Feldweg fährt. Ist dies der Fall, wird der Turbo für 60 Sekunden gezündet, genauer: die Datenbankanfrage wird für 60 Sekunden zwischengespeichert. Falls die Bedingungen nicht zutreffen, wird das eingebaute Standardverhalten verwendet. Speicherhandler, die das komplette Handlerinterface implementieren, können dem Rückgabewert von is_select() eine andere Bedeutung zukommen lassen als die einer Lebenszeit (TTL).

PECL wir kommen!

Wem der vorgestellte Prototyp zusagt, der ist herzlich aufgefordert dabei zu helfen das Plugin für PECL aufzubereiten. PECL wäre eine ideale Schraubergarage! Hilfe kann in viellerlei Form erbracht werden: mittels Erfahrungsberichten und Fragen auf der Mailingliste, durch eigenes Blogging oder noch besser durch das Schreiben von Dokumentation oder gar der aktiven Mithilfe an der Programmierung. Es gibt noch viel zu entdecken! Packen wir das Thema Doku an – vielleicht schon vor dem nächsten Blogartikel.

Das Potential der mysqlnd C plugin API

Poweranwender sollten bei all den albernen Mofavergleichen nicht übersehen, daß dies das erste öffentliche mysqlnd Plugin ist. Es ist nur eine Möglichkeit die mysqlnd C plugin API zu nutzen. Die API ist ein Bestandteil von mysqlnd und steht jedem PHP-Anwender, der in der Lage ist eigene C-Extensions zu entwicklen, zur Verfügung. Die notwendige C API Dokumentation für Eigenentwicklungen wurde auf der IPC-Spring vorgestellt.

Ü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

18 Comments

  1. Cooler Artikel, sehr informativ!

    Über eins muss ich aber doch meckern, weil sich mir da beim Lesen die Zehennägel hochgeklappt haben: Es heißt DAS Mofa! DAS motorisierte Fahrrad. Dankeschön :)

    Reply
  2. Abwechslungsreich geschriebener Artikel und schöne Anekdoten von Rasmus, Danke Ulf!

    Wie hoch ist der Prozentsatz an Applikationen, die noch keine Prepared Statements nutzen?

    Reply
  3. PHPGangsta,

    wieso “noch” nicht verwenden? :-)

    Klar, an vielen Stellen heißt es PS seien toll für die Sicherheit, was ne Aussage ist die nicht ganz unproblematisch ist. Klar manuelles Escaping ist unnötig, dadurch verringert sich die Menge an möglichen Problemen. Weg ist sie aber nicht: Man denke an den IN-Fall oder Abfragen bei denen man die projizierten Felder oder die WHERE-Bedingung dynamisch zusammen baut. Da darf man escaping nicht plötzlich vergessen.

    Desweiteren haben PS ein paar Nachteile: Wenn man echte prepared statements nutzt hat man mehrere round-trip zwischen Client und Server, was je nach Latenz zwischen DB-Server und App bemerkbar ist zudem belegt ein PS am Server resourcen.

    Der PDO_mysql-Treiber “löst” das Latenz-Problem dadurch, dass er eine Emulation nutzt. (ok, historisch hat die Emulation nen anderen Hintergrund) Diese Emulation hat aber wieder ihre eigenen Probleme (hat jemand mal “Spaß” mit LIMIT und PDOs Emulation gehabt?)

    Klar macht es Sinn das ganze Escaping was zu abstrahieren, aber PS muss es nicht unbedingt sein – zumal, wie gesagt, PDO per default emulation von PS nutzt, d.h. so tut als würde man PS nutzen unten drin aber doch die Query selber zusammen baut und dann aber doch eine “normale” Query sendet. Von daher ist unsere Annahme, dass der QC in der Form einen Großteil der Anwendungsfälle erschlägt. Über Feedback der Form “Finde ich super, geht für uns aber nicht weil” (im Zweifel auch per Mail z.B. johannes punkt schlueter bei oracle punkt com) würden wir uns aber definitiv freuen. Wir machen das ja nicht so sehr für uns oder als reinen Selbstzweck sondern für unsere Anwender.

    johannes

    Reply
  4. Ahoi!

    Praktisch alles was heute “modern” ist, benutzt PDO. Über PDO mag man denken was man will. Ich kann mir nicht verkneifen anzumerken, daß ich es für eine sehr fehlerbehaftete Entwicklung halte bei der zu viel Wert auf die Anbiederung gegenüber Herstellen gelegt wurde und gelegt wird. Hersteller sind keine Anwender. Anwender schreiben gute Open Source Software. Es mangelt an Anwendern, die das Wissen und die Zeit haben, um PHP mit einer sauber integrierten Datenbankzugriffsabstraktion in C zu beglücken.

    Wie dem auch sei – moderne Anwendungen benutzen PDO. PDO benutzt die Prepared Statement Emulation für MySQL, solange die Standardeinstellung nicht verändert wird. Die Emulation ist kaput, siehe die vielen PDO-Fehlerberichte für alle PDO-Treiber.

    Aber sie ist da. Damit tut der QC 1.0.0.-*prototype* auch mit PDO und den “Prepared Statement”. Wievielen Anwendern ist eigentlich klar, daß PDO eine Emulation benutzt? Und wieviele benutzen mysqli anstatt mysql? Letzteres unterstützt keine Prepared Statements und ist immer noch extrem beliebt. Nach allesm was ich sehe sind es wenige, welche die APIs als mehr als ein Ding begreifen mit dem Anfragen gesendet werden und als etwas betrachten was sowieso unter einer Abstraktionsschicht versteckt wird.

    Der QC-Code ist inzwischen in PECL. 1.0.0 ist getaggt, ein Packet wurde erstellt, die initiale Dokumentation dem Handbuch hinzugefügt. Es ist eine Frage von Tagen bis diese Dinge sichtbar werden.

    In trunk hat Andrey Neuerungen eingecheckt. Die erste könnte sein, daß der Code nicht baut — am Freitag um 19 Uhr bei über 30 Grad Außentemperatur war es uns egal. Vor dem Check-In bauten die Neuerungen auf allen Testmaschinen.

    Was für eine Neuerung hat er eingecheckt: Prepared Statement Support.

    Wie stabil, wie abgehangen oder blutig es ist, muß die Zukunft zeigen. Ebenso wann ein 1.0.x mit PS-Support kommt. In trunk ist der erste Entwurf – http://news.php.net/php.pecl.cvs/14412 .

    Ulf

    Reply
  5. Wer mehr über den Query Cache wissen will, der sollte sich die Slides anschauen bis irgendwann mal die Slides in Dokumentation übersetzt wurden.

    Grundlagen (62 Slides) –
    http://www.slideshare.net/nixnutz/buildin-query-caching-for-all-php-mysql-extensionsapis

    Benutzerdefinierte Speicherhandler, um TTL zu überwinden (48 Slides) –
    http://www.slideshare.net/nixnutz/mysqlnd-query-cache-plugin-userdefined-storage-handler

    Tuning und Monitoring mit Statistiken und Backtraces (66 Slides) –
    http://www.slideshare.net/nixnutz/mysqlnd-query-cache-plugin-statistics-and-tuning

    Beobachtungen und Einschätzungen zur Performanz am Beispiel Oxid eShop (45 Slides) –
    http://www.slideshare.net/nixnutz/mysqlnd-query-cache-plugin-benchmark-report-4734189

    Reply
  6. Ich weiß dass ich nichts weiß, Danke für die vielen interessanten internen Infos. Es ist eben doch ein großer Unterschied ob man richtig Ahnung hat oder “nur” Anwender ist ;-)

    Termin in 4 Wochen erstellt: mysqlnd QC testen mit SP Support

    Reply
  7. PHPGangsta: Nimmst Du meine Entschuldigung an – ich bin zu weit gegangen!

    Prepared Statements ist ein Thema auf das ich extrem reagiere. Ich bin da auf der Seite von Brian Aker, der es für ein veraltetes Konzept hält. Du hast einen Knopf gefunden und ich bin explodiert.

    Für eine 1.0.0-prototype halte ich den Verzicht auf PS für absolut Ok. Ja, es gibt Power-User, die PS benutzen. Sie haben gute Gründe es zu tun. Die Masse der Leute und Anwendungen vermute ich jedoch nicht in dieser Gruppe.

    Es gibt zu viel ext/mysql und PDO_MySQL mit Emulation. Die Masse der Anwendungen wird ohne PS arbeiten. Jede neue Anwendung muß sich fragen ob Query Caching der richtige Ansatz ist. Will ich wirklich Queries cachen oder die Produkte von Queries – beispielsweise HTML-Snippets? Anstatt mich auf dieses Thema PS zu versteifen, würde ich eher den gesamten Ansatz eines Anfrage-Caches in Frage stellen.

    Ulf

    Reply
  8. Kein Grund zur Entschuldigung, mir (und wahrscheinlich vielen anderen hier) ist nur das Prinzip von Prepared Statements bekannt, wie das intern umgesetzt wird (und dass es da Emulationen gibt usw.) werden nur die wenigsten wissen.

    Und in Zeiten von Frameworks, die intern automatisch Prepared Statements nutzen und einem nur PDO erlauben (Zend Framework, Doctrine glaube ich auch) dachte ich nun dass Prepared Statement = Prepared Statement ist. Wohl falsch gedacht. Wenn das Framework “verspricht” Prepared Statements zu nutzen hinterfrage ich das erstmal nicht, dann nehme ich an dass es auch “echte” Prepared Statements sind.

    Nichts für ungut, Entschuldigung angenommen, werde mich da wohl beizeiten etwas mehr einlesen müssen. Und den QueryCache dann auch gleich ausprobieren, Ob man ihn in der Praxis braucht (memcached im Einsatz + MySQL Query Cache aktiviert) wird man sehen.

    Michael

    Reply
  9. Puuh, habe ich ja nochmal Glück gehabt.

    PDO setzt fast ausschließlich auf Prepared Statements. Es bietet eine API, die das binden von Parametern an Anfragen erlaubt. Genau diese Funktionalität wird von vielen gewünscht und diese API wird von PDO bevorzugt.

    Man möchte Eingabewerte vom SQL-Kommando trennen. Die Zusammenführung soll erst dort passieren wo ein Parser, der über Kontextinformationen verfügt, die Gültigkeit der Eingabewerte überprüfen kann. Die Zusammenführung und Validierung soll auf dem Server passieren, weil nur dort alle Informationen vorliegen, um Angriffsversuche zu erkennen.

    In meinen Augen werden zwei Dinge vermischt: Prepared Statements und Bind-API. Die Bind-API ist eine Eigenschaft von Prepared Statements. Ich bin nicht sicher wieviele Benutzer sich wirklich für die anderen Eigenschaften von *Prepared* Statements interessieren.

    PDO kann die Bind-API emulieren, wenn die Datenbank es nicht nativ zur Verfügung stellt. PDO kommt aus einer Zeit in der, die Prepared Statement Unterstützung durch MySQL noch in den Kinderschuhen steckte. Und auch andere Datenbanken kennen oder kannten keine Prepared Statements. Als einheitliche Schnittstelle musste PDO die Prepared Statement emulieren.

    Die Emulation ist bis heute die Voreinstellung für PDO_MySQL. Bei der Emulation führt der PDO-Kern die SQL-Anfrage und die Parameter auf dem Klient zusammen und sendet eine normale Anfrage. Es steht zwar PS drauf, aber es sind keine nativen Prepared Statements drin.

    Der PDO-Kern übernimmt die Maskierung. Es passiert nichts anderes als würde man in PHP mit mysqli_query() + mysqli_real_escape_string() arbeiten. Es ist in keinster Weise “sicherer”.

    Seine Arbeit verrichtet der PDO-Kern mal mehr mal minder gut. Der MySQL-Server erwartet bei der SQL-Anfrage “SELECT something FROM table LIMIT ?” für den Platzhalter “?” einen numerischen Wert. PDO weiß das nicht. Der PDO SQL-Parser ist einfach und allgemeiner Natur. Er kennt die Eigenheiten der Datenbanknen nicht. Werden keine weiten Hinweise vom Programmierer über die PDO-API gegeben, daß hier ein numerischer Wert gefordert ist, benutzt PDO die Standardeinstellung String. Aus der Anfrage wird: SELECT something FROM table LIMIT “3”. MySQL machert zu Recht, ein Bugreport gegen PDO_MySQL wird eröffnet, der Bug wird auf Bogus gesetzt, der Frust steigt. Das gleiche passiert mit anderen Datenbanken und anderen Anfragen. Es ist kein MySQL spezifisches Problem.

    Darum reagiere ich allergisch auf das Thema PDO und Prepared Statements…

    Mögliche Lösungen wäre der Verzicht auf eine Emulation oder der Einsatz von spezialisierten Parsern in den PDO-Treibern (nicht im PDO-Kern). Letzteres würde das Problem aber nur verschieben. Manche Anfragen brauchen Kontextinformationen, die nur auf dem Server zur Verfügung steht. Denkt man weiter, ist man wieder bei Brian, der den Wunsch nach einer Bind-API zum Anlaß nimmt grundlegend über Prepared Statements nachzudenken.

    Wenn Du schon Memcache und den MySQL (Server) Query Cache im Einsatz hast, wird die Luft dümm für das Plugin. Der MySQL (Server) Query Cache liefert Dir nie veraltete Daten. Sein einziger Angriffspunkt ist das Thema Skalierung.

    Bin gespannt…

    Reply
  10. Hallo Ulf,

    entnehme ich das deinem Kommentar richtig, dass man es PDO auch austreiben kann, seine eigene PS-Emulation zu nutzen? Wenn ja, welche Knöpfe muss ich denn da drücken? Ich hab beim (zugegebenermaßen kurzen) Überfliegen des Manuals nix in der Richtung gefunden…

    Sehe ich es außerdem richtig, dass MySQLi “echte” Prepared Statements benutzt?

    Schöne Grüße,
    Tobi

    Reply
  11. Oh, da sind noch einige Kommentare dazugekommen die vorher nicht da waren ;-)

    @Johannes: ich schrieb “noch” genau wegen dem Escapen und dem allgemeinen Trend hin zu PS. Vielerorts werden PS nunmal als sicherer beschrieben (was sie vom Prinzip her sind, denn 90% der Hobby-PHPler wissen nichts von Escaping oder vergessen es alle Nase lang), die Round-Trips sind in 99% der Fälle auch kein Problem: PHP und MySQL-Server auf der selben Maschine oder im schnellen GBit Netzwerk, oder diese Latenz ist kein Flaschenhals. Ebenso sind ein paar KB Ressourcen auf dem MySQL Server auch kein Problem in den allermeisten Fällen. Wenn man PS häufiger aufruft spart man dafür Traffic im Netz und Ressourcen auf dem Server (der Parser muss den Query nicht nochmals durchackern und optimieren soweit ich weiß).
    Im Prinzip sind PS eine tolle Sache, klar gibt es Ausnahmen und Nachteile, aber auch Vorteile. Wie gesagt, von Emulationen auf der Ebene war mir bisher nichts bekannt. Deshalb umso besser wenn ihr uns aufklärt ;-)

    Reply
  12. Das Thema PS habe ich vor einiger Zeit schon mal durchgekaut. Deshalb ein Verweis auf http://blog.ulf-wendel.de/?p=187 – PDO_MYSQLND: Prepared Statements, again .

    Die PDO-Doku ist in der Tat verbesserungswürdig. Was Du suchst ist das Päärchen PDO::ATTR_EMULATE_PREPARES und PDO::MYSQL_ATTR_DIRECT_QUERY . Beides auf 0 und die Emulation ist weg.

    http://de.php.net/manual/en/pdo.prepared-statements.php

    “Prepared statements are so useful that they are the only feature that PDO will emulate for drivers that don’t support them. This ensures that an application will be able to use the same data access paradigm regardless of the capabilities of the database. ”

    http://de.php.net/manual/en/pdo.constants.php

    “PDO::ATTR_EMULATE_PREPARES ( integer )
    Available since PHP 5.1.3. ”

    http://de.php.net/manual/en/ref.pdo-mysql.php

    “PDO::MYSQL_ATTR_DIRECT_QUERY ( integer )
    Perform direct queries, don’t use prepared statements.”

    Alles weitere sollte sich im verlinkten Blogposting finden: warum macht PDO eine Emulation, was taugt die, was kann MySQL nicht usw.

    Gestern abend habe ich mir noch QC und PS zum ersten Mal angeschaut. Der erste Test brachte den ersten Crash, der erste Fix beseitigte den ersten Crash, …. Wer Tests schreiben will: der Code ist in PECL: mysqlnd_qc/trunk .

    Ulf

    Reply
  13. Ja, *schäm*. Es ist die Rohfassung online gegangen. Es sind noch mehr Fehler drin. Da habe ich mich mit Nils nicht gut abgestimmt.

    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