Sollte man in Magento den MySQL-Query-Cache aktivieren?

Die Meinungen gehen hier auseinander und findet zu beiden Meinungen Treffer in den Suchmaschinen. Die Mehrheit der Server-Betreuer rät allerdings dazu, den Query-Cache zu deaktivieren. Je mehr Traffic im Shop vorhanden ist, desto empfehlenswerter ist es das zu tun.

Warum ist das so?

Intuitiv klingt das wahrscheinlich erst einmal unlogisch. Caches verwendet man doch gerne, um Last vom Server zu nehmen und das Resultat komplexer Berechnungen oder Abfragen schnell zurückzugeben. Wie kann ein Cache hier schädlich sein?

Hier kommen mehrere Faktoren zum Tragen.

Viele Cache-Einträge

Magento setzt viele unterschiedliche Datenbankanfragen ab. Jeder Cache-Eintrag ist aber einzigartig. Das heißt: selbst wenn es nur einen minimalen Unterschied zwischen zwei Abfragen gibt, dann schreibt MySQL zwei getrennte Cache-Einträge.

Der Cache-Eintrag enthält die Abfrage als Text und das komplette Ergebnis einer Abfrage. Ist der Abfrage-Text nicht genau gleich geschrieben, dann haben wir zwei Einträge. Der Cache-Einträg enthält nicht den optimierten Ausführungsplan einer Abfrage (siehe diese Präsentation). Es geht also nicht darum, eine Art von Abfragen schneller zu machen sondern ganz genau eine bestimmte Abfrage. Führt man zuerst ein „SELECT id FROM table WHERE id = 1“ aus und dann „SELECT id FROM table WHERE id = 2“, dann profitiert die zweite Abfrage nicht vom Cache-Eintrag welchen MySQL für die erste Abfrage geschrieben hat.

Für die Magento-Funktionalitäten speichert Magento viele Daten in vielen Tabellen. Viele User surfen im Shop, lösen unterschiedliche Datenbankanfragen in vielen Variationen aus. Das bedeutet, dass sich schnell viele Cache-Einträge ansammeln.

Größe der Cache-Einträge und Cache-Größe

Dazu kommt, dass Entitäten wie Produkte oder Bestellungen viele Eigenschaften / Spalten enthalten. Daher wird ein einzelnes Resultset, also ein einzelnes Abfrage-Ergebnis bei ungünstigen Anfragen schnell groß. Und das wiederum bedeutet, dass man einen großen Cache benötigt.

Ein kurzes Beispiel, warum der Query-Cache oft nicht funktioniert wie man es sich erwartet:

  • Das MySQL-Setting query_cache_limit limitiert, wie groß ein Resultset maximal sein darf damit es in den Cache wandert. Der Standardwert ist 1 MB. Das heißt: ist das Ergebnis einer DB-Abfrage größer als 1 MB, wird es gar nicht erst gecacht. Man darf aber nicht der Versuchung erliegen, den Wert zu erhöhen um möglichst viele DB-Abfragen zu cachen. Denn:
  • es gibt das zweite wichtige MySQL-Setting query_cache_size,  das festlegt wie groß der Query-Cache überhaupt ist. Sobald der Cache voll ist, müssen alte Einträge entfernt werden („pruning“). Das braucht Zeit. Angenommen, wir stellen hundert verschiedene Anfragen deren Ergebnis je 1 MB groß ist und unser Cache ist 100 MB groß, dann ist der Speicher bereits voll und die nächste Anfrage mit einem Ergebnis <= 1 MB löscht bereits einen alten Eintrag aus dem Cache.

Jetzt könnte man sich natürlich denken: kein Problem! RAM ist doch billig. Ich blase einfach meinen Query-Cache auf 64 GB auf und kann jede Menge Abfrage-Ergebnisse speichern. Damit schießt man sich aber schnell ein Eigentor, denn:

Cache-Invalidierung

In einem Shop, in dem viel los ist und in dem es viele Daten gibt werden naturgemäßig häufig Datenbank-Einträge aktualisiert. Das führt zu einer häufigen Invalidierung von Cache-Einträgen. Da MySQL bei jeder Datenänderung überprüfen muss ob ein Cache-Eintrag noch gültig ist, ihn gegebenenfalls invalidieren und für diese Operationen einen Lock auf den gesamten Cache ausführen muss, benötigt das einiges an Zeit- und Rechenaufwand. In der Zwischenzeit sind andere Operationen gelockt, das heißt man blockiert damit andere Abfragen.

Um den Overhead der Query-Cache-Operationen nicht zu groß werden zu lassen, gilt als Faustregel dass die query_cache_size maximal einige wenige hundert MB groß sein soll. Percona empfiehlt eine Größe von maximal 256 MB.

Aber wie oft kommt das schon vor?

Häufig liest man solche Erklärungen, hat aber keine Vorstellungen davon:

  • wieviele Datenbankabfragen auf einen Server einprasseln
  • wie oft denn nun wirklich ein Cache-Eintrag entfernt werden muss weil kein Platz mehr im Query-Cache vorhanden ist
  • wie viele Cache-Einträge invalidiert werden müssen
  • wie oft (aus welchen Gründen auch immer) ein Query-Cache-Miss erfolgt

Ich habe mir daher als Beispiel die Serverdaten eines Kunden angesehen, der bei einem auf Magento spezialisierten Provider mit aktiviertem Query-Cache und guter Seitenperformance hostet.

Im letzten halben Jahr gab es dort

  • fast 6 Milliarden MySQL-Datenbankabfragen
  • mit einer Query-Cache-Hitrate von über 80 Prozent, wobei pro Sekunde
  • fast 16 Cache-Einträge aufgrund vollen Speichers entfernt sowie
  • ungefähr 44 Cache-Einträge invalidiert werden und
  • knapp 9 Query-Cache-Misses erfolgen.

Bei dieser Häufigkeit an Anfragen und Cache-Operationen ist es angebracht zu hinterfragen, ob der Overhead eines Caches den Nutzen überwiegt.

Sollte man den Query-Cache also immer deaktivieren?

Jetzt sind Sie vielleicht versucht, den Query-Cache sofort und für alle Anwendungsfälle zu deaktivieren. So weit würde ich nicht gehen.

Es ist immer am besten, mit verschiedenen Einstellungen zu experimentieren und für den eigenen Shop herauszufinden, ob ein Query-Cache einen Vorteil bringt oder nicht und mit welchen Einstellungen. Es gilt noch weit mehr Faktoren abzuwägen als in diesem Artikel angesprochen. Im obigen Beispiel erzielt der Shop übrigens trotz der häufigen Prunes und Invalidierungen mit Query-Cache eine bessere Performance als ohne.

In vielen Fällen, gerade in größeren Magento-Shops bringt das Deaktivieren des Query-Caches allerdings tatsächlich eine Verbesserung. Ab MySQL 5.6.8 ist der Query-Cache außerdem standardmäßig deaktiviert.