Einführung
Der MySQL Native Driver verwaltet den Speicher anders als die MySQL Client Library. Die Bibliotheken unterscheiden sich in der Art und Weise, wie Speicher zugewiesen und freigegeben wird, wie Speicher beim Lesen von MySQL-Ergebnissen in Teilstücken zugewiesen wird, welche Debug- und Entwicklungsoptionen existieren und wie von MySQL gelesene Ergebnisse mit PHP-Benutzervariablen verknüpft werden.
Die folgenden Hinweise sind als Einführung und Zusammenfassung für Benutzer gedacht, die daran interessiert sind, den MySQL Native Driver auf C-Code-Ebene zu verstehen.
Für die Speicherverwaltung verwendete Funktionen
Alle Zuweisungen und Freigaben von Speicher erfolgen über die Funktionen der PHP-Speicherverwaltung, weshalb der Speicherverbrauch von mysqlnd mit Hilfe von PHP-API-Aufrufen wie memory_get_usage() verfolgt werden kann. Da der Speicher über die PHP-Speicherverwaltung zugewiesen und freigegeben wird, werden die Änderungen möglicherweise nicht sofort auf der Ebene des Betriebssystems sichtbar. Die PHP-Speicherverwaltung fungiert als Proxy, der die Freigabe von Speicher an das System eventuell verzögert. Aus diesem Grund ist ein Vergleich des Speicherverbrauchs des MySQL Native Drivers und der MySQL Client Library schwierig. Die MySQL Client Library verwendet direkt die Aufrufe der Speicherverwaltung des Betriebssystems, sodass die Auswirkungen sofort auf der Betriebssystemebene beobachtet werden können.
Jede von PHP erzwungene Speicherbegrenzung wirkt sich auch auf den MySQL Native Driver aus. Dies kann zu "Out-of-Memory"-Fehlern führen, wenn große Ergebnismengen abgerufen werden, die die Größe des von PHP zur Verfügung gestellten Speichers überschreiten. Da die MySQL Client Library nicht die Funktionen der PHP-Speicherverwaltung verwendet, unterliegt sie nicht den von PHP gesetzten Speichergrenzen. Wenn die MySQL Client Library verwendet wird, kann der Speicherbedarf eines PHP-Prozesses je nach Einsatzmodell über das PHP-Speicherlimit hinaus anwachsen. Gleichzeitig sind PHP-Skripte in der Lage, größere Ergebnismengen zu verarbeiten, da der für die Speicherung der Ergebnismengen vorgesehene Speicher nicht unter der Kontrolle von PHP steht.
Die Funktionen der PHP-Speicherverwaltung werden vom MySQL Native Driver über einen schlanken Wrapper aufgerufen. Der Wrapper erleichtert unter anderem das Debugging.
Handhabung von Ergebnismengen
Die verschiedenen MySQL-Server- und Client-APIs unterscheiden zwischen gepufferten und ungepufferten Ergebnismengen. Ungepufferte Ergebnismengen werden Zeile für Zeile von MySQL zum Client übertragen, während der Client die Ergebnisse durchläuft. Gepufferte Ergebnisse werden in ihrer Gesamtheit von der Client-Bibliothek abgerufen, bevor sie an den Client weitergegeben werden.
Der MySQL Native Driver verwendet PHP-Streams für die Kommunikation mit dem MySQL-Server. Die von MySQL gesendeten Ergebnisse werden aus den Netzwerkpuffern der PHP-Streams in den Ergebnispuffer von mysqlnd abgerufen. Der Ergebnispuffer besteht aus Zvals. In einem zweiten Schritt werden die Ergebnisse dem PHP-Skript zur Verfügung gestellt. Diese abschließende Übertragung vom Ergebnispuffer in PHP-Variablen wirkt sich auf den Speicherverbrauch aus und macht sich vor allem dann bemerkbar, wenn gepufferte Ergebnismengen verwendet werden.
Standardmäßig versucht der MySQL Native Driver zu vermeiden, dass gepufferte Ergebnisse doppelt im Speicher gehalten werden. Die Ergebnisse werden nur einmal im internen Ergebnispuffer und ihren Zvals gespeichert. Wenn die Ergebnisse vom PHP-Skript in PHP-Variablen abgerufen werden, verweisen die Variablen auf den internen Ergebnispuffer. Ergebnisse von Datenbankabfragen werden nicht kopiert und nur einmal im Speicher gehalten. Wenn ein Benutzer den Inhalt einer Variable ändert, die Ergebnisse einer Datenbankabfrage enthält, muss ein Copy-on-Write (Kopieren beim Schreiben) durchgeführt werden, um zu vermeiden, dass der referenzierte interne Ergebnispuffer geändert wird. Der Inhalt des Puffers darf nicht verändert werden, weil der Benutzer die Ergebnismenge möglicherweise ein zweites Mal lesen möchte. Der Copy-on-Write-Mechanismus wird durch eine zusätzliche Liste zur Verwaltung der Referenzen und die Verwendung von Standard-Zval-Referenzzählern realisiert. Copy-on-Write muss auch dann durchgeführt werden, wenn der Benutzer eine Ergebnismenge in PHP-Variablen einliest und eine Ergebnismenge wieder freigibt, bevor die Variablen zurückgesetzt werden.
Im Allgemeinen funktioniert diese Vorgehensweise gut bei Skripten, die eine Ergebnismenge einmal lesen und die Variablen mit den Ergebnissen nicht verändern. Ihr größter Nachteil ist der durch die zusätzliche Referenzverwaltung verursachte zusätzliche Speicherbedarf. Dieser rührt in erster Linie daher, dass Benutzervariablen, die Ergebnisse enthalten, erst dann vollständig freigegeben werden können, wenn sie nicht mehr von der mysqlnd-Referenzverwaltung referenziert werden. Der MySQL Native Driver entfernt die Referenz auf die Benutzervariablen, wenn die Ergebnismenge freigegeben wird oder wenn ein Copy-on-Write durchgeführt wird. Ein Beobachter sieht, wie der Gesamtspeicherbedarf solange ansteigt, bis die Ergebnismenge freigegeben wird. Mit Hilfe der Statistiken kann überprüft werden, ob ein Skript die Ergebnismenge explizit freigibt oder ob sie vom Treiber implizit freigegeben wird und somit der Speicher länger als nötig verwendet wird. Anhand der Statistiken lässt sich auch feststellen, wie viele Copy-on-Write-Operationen durchgeführt wurden.
Ein PHP-Skript, das viele kleine Zeilen aus einer gepufferten Ergebnismenge
ausliest und dazu einen Codeschnipsel verwendet, der while ($row =
$res->fetch_assoc()) { ... }
entspricht, kann den
Speicherbedarf optimieren, indem es Kopien anstelle von Referenzen
anfordert. Kopien anzufordern bedeutet zwar, dass die Ergebnisse doppelt im
Speicher gehalten werden, aber es erlaubt PHP, die in
$row
enthaltene Kopie schon freizugeben, während die
Ergebnismenge durchlaufen wird, statt erst, wenn die Ergebnismenge selbst
freigegeben wird. Auf einem stark ausgelasteten Server kann die Optimierung
der Spitzenspeichernutzung dazu beitragen, die Gesamtleistung des Systems zu
verbessern, obwohl der Weg über Kopien für ein einzelnes Skript aufgrund
zusätzlicher Zuweisungen und Speicherkopiervorgänge langsamer sein kann.
Überwachung und Fehlersuche
Es gibt mehrere Möglichkeiten, die Speichernutzung des MySQL Native Drivers zu verfolgen. Um einen schnellen Überblick zu erhalten oder die Speichereffizienz von PHP-Skripten zu überprüfen, empfiehlt sich ein Blick auf die Statistiken, die von der Bibliothek gesammelt werden. Anhand der Statistiken können z. B. SQL-Anweisungen erkannt werden, die mehr Ergebnisse erzeugen als von einem PHP-Skript verarbeitet werden.
Das Debug-Trace-Protokoll kann so konfiguriert werden, dass es Aufrufe der Speicherverwaltung aufzeichnet. Das ist hilfreich, um zu sehen, wann Speicher zugewiesen oder freigegeben wird. Die Größe der angeforderten Speicherabschnitte wird jedoch möglicherweise nicht aufgeführt.
Einige neuere Versionen des MySQL Native Drivers bieten die Emulation von zufälligen "Out-of-Memory"-Situationen. Diese Funktion ist nur für die C-Entwickler der Bibliothek oder die Autoren des mysqlnd-Plugins gedacht. Die entsprechenden PHP-Konfigurationseinstellungen und weitere Details sind im Quellcode zu finden. Die Funktion wird als nicht für die Allgemeinheit bestimmt betrachtet und kann jederzeit ohne vorherige Ankündigung geändert werden.