Schon seit einigen Wochen beschäftige ich mich intensiv damit, Magento-Installationen auch unter größerer Last performant zu bekommen. Der vielversprechendste Ansatz dabei, ist das Caching zu überdenken und zu optimieren.

Im Einsatz haben wir dabei einen Two-Level-Cache:

  • 1st Level: APC (schnell, aber kleine Speicherkapazität)
  • 2nd Level: Dateisystem (langsam, aber große Speicherkapazität)

Ich wollte genauer wissen, was da im Hintergrund passiert, also habe ich detailierte Daten über die Zugriffshäufigkeit und die Zugriffsgeschwindigkeit auf die beiden Caches gesammelt:

In diesem Graphen sind alle Zugriffe pro Minute auf die beiden Caches zu sehen: Die grüne Kurve beschreibt alle Zugriffe auf das Two-Level-Cache-Backend. Diese treffen zunächst auf den 1st-Level-Cache ein. Die gelbe Kurve beschreibt alle Zugriffe, die im 1st-Level-Cache nicht gefunden werden ("misses"). Der Bereich zwischen der grünen und der gelben Kurve beschreibt also die Treffer im 1st-Level-Cache ("hits").

Die rote Kurve beschreibt alle Zugriffe, die weder im 1st-Level-Cache noch im 2nd-Level-Cache gefunden wurden. Der Bereich zwischen der gelben und der roten Kurve sind also die Treffer im 2nd-Level-Cache und alles unterhalb der roten Kurve wurde in keinem der Caches gefunden und muss also aufwändig neu berechnet werden.

Daraus ableiten kann man nun für die beiden Caches jeweils ein Hitrate (in %):

Spätestens hier wird deutlich, dass alle zwei Stunden die Hitrate des 1st-Level-Caches deutlich sinkt und darunter auch die Gesamtperformance des Shops leidet, da der 2nd-Level-Cache deutlich langsamer ist.

Zunächst hatte ich vermutet, dass der Server durch andere Jobs alle zwei Stunden regelmäßig belastet wird, fand aber keinen Hinweis darüber, was da den Cache beeinflussen könnte.

Unabhängig davon fiel mir beim Prüfen der gecachten Blöcke auf, dass diese im APC Cache immer genau ein drittel der Zeit gültig waren wie als Cache-Lifetime angegeben. Einen Einblick in die eingelagerten Cacheelemente des APC gibt das Skript apc.php sehr schön, dass in den Quelldateien des APC zu finden ist.

Schließlich habe ich das Problem lokalisieren können:

Magento verwendet das Zend Framework um das Caching zu realisieren. Die dabei verwendete Methode _Zend_CacheCore->save() hat dabei einen fünften optionalen Parameter "priority", der von Magento nicht verwendet wird und im Zend Framework somit mit 8 vorbelegt ist. Diese Priority wird zur Methode _Zend_Cache_BackendTwoLevels->save() durchgereicht. Dort wird (innerhalb der _->getFastLifetime()) für den 1st-Level-Cache eine eigene Cache-Lifetime berechnet:

$fastLifetime = (int) ($lifetime / (11 - $priority));

Das hat zur Folge, dass die Cache-Lifetime für den 1st-Level-Cache in Magento immer ein Drittel so groß wie die ursprünglich angebene Lifetime.

Die Zwei-Stunden-Periode kommt dabei durch die Default-Lifetime von 7200 Sekunden (die allerdings in der local.xml überschrieben werden kann).

Hier passiert also folgendes: Die meisten Cache-Einträge laufen nach zwei Stunden ab und müssen dann neu erzeugt werden. Die Cache-Lifetime im APC beträgt also nur 40 Minuten. Nach diesen 40 Minuten ist der Cache-Eintrag allerdings noch im 2nd-Level-Cache vorhanden und wird daher nicht neu erzeugt. In den restlichen 80 Minuten bis der Cache-Eintrag auch im langsam Cache abläuft liefert der APC also nur Misses.

Stellt man die Priority hoch auf 10 ist die Cache-Lifetime im APC immer die Original-Cache-Lifetime. Die Caches laufen also immer gleichzeitig ab (wenn der Eintrag im APC nicht vorher verdrängt wurde) und damit kann der schnelle Cache durchgehend zum Einsatz kommen. Der Graph sieht dann so aus:

Schön sichtbar ist hier nun wie die Hitrate des 1st-Level-Caches (grüne Linie) ab dem Zeitpunkt an dem ich die Priorität auf 10 gesetzt habe auf fast 100% steigt und dort auch konstant bleibt.

Dass die Hitrate des 2nd-Level-Caches (gelbe Linie) nun auf fast 0% sinkt ist die logische Konsequenz: Da der 1st-Level-Cache noch nicht vollgelaufen ist, werden fast alle Cache-Einträge schon dort gefunden. Diejenigen, die dort nicht gefunden werden, sind auch noch nicht im Dateisystem vorhanden und müssen neu generiert werden. Das Dateisystem ist nun deutlich entlastet.

Interessant ist nun auch das Verhalten der Caches, wenn der 1st-Level Cache vollgelaufen ist und wie sich die Caches bei verschiedenen Größen des APC und Default-Lifetimes verhalten. Ich werde das im Auge behalten und ggf. hier noch über spannende Erkenntnisse berichten.

Comments

This website uses disqus for the commenting functionality. In order to protect your privacy comments are disabled by default.

Enable Comments