QuickCache (früher JPCache) – Vorstellung und Konfigurationsempfehlung

Dass Caching eine feine Sache ist, habe ich ja in diesem Blog sowohl für die Client- als auch für die Server-Seite schon einige Male aufgezeigt. Vor einiger Zeit habe ich mal das Script JP Cache empfohlen. Mittlerweile wurde dieses Script unter dem neuem Namen QuickCache aktualisiert. Da ich damit bislang gute Erfahrungen gemacht habe, wollte ich es hier nochmal kurz vorstellen – und potenzielle Nutzern dieses Scripts davor bewahren einen Fehler zu begehen.

QuickCache ist ein Script, das durch Einbinden einer PHP-Datei den gesamten Output eines eurer PHP-Scripte zwischenspeichert – entweder in der Datenbank oder auf dem Dateisystem. Abhängig von der URL der Seite kann somit der Server stark entlastet werden.
Das Script schaut sich dazu die URL an, guckt dann, ob in der Datenbank ein zwischengespeichertes Dokument zu dieser URL vorhanden ist, das noch nicht abgelaufen ist (Ablaufzeit dynamisch einstellbar). Findet es eines, wird einfach das Dokument aus der Datenbank geholt und zum Client geschickt. Der restliche DB- und Server-Aufwand für die „normale Bearbeitung“ des Scripts entfällt somit.
Aufgrund der für jedes Script separat festlegbaren Ablaufzeit ist man recht flexibel: Produkt-Detailseiten eines Online-Shops ändern sich beispielswiese nicht so häufig, da könnte man einen Tag als Ablaufzeit wählen. Bei einer Startseite eines minutenaktuellen Nachrichtenportals empfiehlt sich eine sehr viel geringere Einstellung (bzw. ist fraglich, ob hier überhaupt Caching eingesetzt werden sollte).

Ganz nebenbei kümmert sich das Script dann noch um das GZIPen des Dokuments. Man kann es also bedenkenlos in jedes PHP-Script einbinden, denn auch, wenn man die Ablaufzeit des Dokuments auf -1 stellt, wird das Dokument zumindest geGZIPt (in diesem Fall aber nicht gecached). Natürlich achtet das Script dabei darauf, dass der Client das auch unterstützt (HTTP-Header Accept-Encoding).

Nun zu meinen einleitenden Worten mit dem Fehler, vor dem ich Nutzer bewahren möchte, und zum eigentlichen Sinn dieses Postings. Ich setze das Script sehr gern ein, wunderte mich aber, weshalb meine Datenbank sporadisch den Server total auslastete. In den PHPMyAdmin-Prozessen tauchten nur die normalen Abfragen auf, nur dauerten diesmal alle ca. 10 Sekunden länger. So lange wartet natürlich kein Besucher.

Also machte ich mich auf die Suche und stellte schon bald fest, dass es meine eigenen Scripte eigentlich nicht sein können – selbst die Cronjob-Scripte habe ich mir mal angesehen (um das sporadische Element zu erklären), aber auch die gingen flott durch.
Letztlich überlegte ich, welche Drittanbieter-Scripte ich laufen hatte. Zunächst fielen mir keine ein – bis ich an QuickCache dachte. Also hab ich mir da die Scripte und Einstellungen mal genauer angesehen.

Und zwar gibt es eine Einstellung $QUICKCACHE_GC, die festlegt, wie wahrscheinlich es ist, dass eine Garbage Collection durchgeführt wird. Im Klartext bedeutet das, dass Datensätze aus der Cache-Tabelle gelöscht werden, die nicht mehr gültig sind (Ablaufzeit überschritten). Der Standardwert (out-of-the-box) ist 1.
Hinzu kommt noch die Einstellung $QUICKCACHE_OPTIMIZE, die bei Aktivieren (=true) dazu führt, dass nach jeder Garbage Collection ein OPTIMIZE TABLE durchgeführt wird. Diese Einstellung ist standardmäßig aktiviert.

Bis hierhin klingt das alles prima. Die Tabelle wird ab und zu aufgeräumt und das Optimieren ist ja auch nicht verkehrt. Problematisch ist allerdings, dass bei mehreren Tausend Besuchern dieses 1 % einfach sehr häufig ist.
Eine kleine Beispielrechnung:
– 1 mal pro 100 Seitenzugriffe wird eine Garbage Collection durchgeführt.
– eine Website hat 10.000 Seitenzugriffe pro Tag
=> pro Tag wird 100 Mal ein aufwändiges DELETE-Statement ausgeführt aber noch viel schlimmer: 100 Mal pro Tag wird ein OPTIMIZE-TABLE-Statement ausgeführt! Da letzteres recht aufwändig ist, bremst das natürlich den ganzen DB-Server.

Ich schaute also mal weiter und fand folgendes komische Snippet zur Ermittlung, ob eine Zufallszahl unterhalb der Prozentgrenze liegt. Dies wird hier meines Erachtens sehr merkwürdig durchgeführt (habs bis jetzt nicht verstanden, wie das funktioniert):

if ($GLOBALS["QUICKCACHE_GC"]>0) {
  mt_srand(time(NULL));
  $precision=100000;
  // Garbagecollection probability
  if (((mt_rand()%$precision)/$precision) <=
      ($GLOBALS["QUICKCACHE_GC"]/100))
  {
    quickcache_do_gc();
  }
}

Das mt_srand() ist ja eigentlich nicht mehr nötig seit PHP 4.2.0 – vermutlich wurde es aus Kompatibilitätsgründen dringelassen (welcher Webserver allerdings auf PHP<4.2 läuft, sollte sich vielleicht andere Gedanken als die über Performance machen 😉 ) Gut, aber nun zu der Zufallszahlenberechnung: mt_rand() berechnet eine Zufallszahl zwischen 0 und der rechnerabhängigen Konstante MAX_RAND (seeeehr große Zahl). Und jetzt wirds für mich mystisch: Modulo die Precision und danach noch ne Division durch die gleiche Zahl. Wer das versteht, darfs mir gern mal in den Kommentaren erläutern (hab aber auch nicht weiter drüber nachgedacht, geschweige denn mir das auf Papier mal aufgeschrieben - fands einfach einen merkwürdigen Weg). Jedenfalls habe ich diesen Abschnitt lieber umgeschrieben, weil ich ihm misstraut habe, denn allein davon ist ja abhängig, wie oft die Garbage Collection durchgeführt wird. Und wenn da ein Fehler passiert, wird diese vielleicht noch viel häufiger durchgeführt als in den Konfigurationseinstellungen festgelegt. Deshalb hab ich folgendes daraus gemacht:

if(mt_rand(0,100000)/1000<$GLOBALS["QUICKCACHE_GC"]) {
  quickcache_do_gc();
}

Normalerweise würde mt_rand(0,100) bzw. sogar mt_rand(100) ausreichen (weil die linke Grenze bei Übergabe nur eines Parameters automatisch 0 ist), aber mir war ja das 1 % von oben zu viel. Ich wollte also kleinere Werte angeben können (aber natürlich >0). Deshalb bilde ich eine größere Zufallszahl und dividiere danach, sodass am Ende eine Zufallszahl zwischen 0 und 100 mit 2 Dezimalstellen herauskommt.
Die Variable $QUICKCACHE_GC habe ich folglich auf 0.01 gesetzt. Nun wird sehr viel seltener die Garbage Collection durchgeführt und MySQL kann sich um wichtigere Dinge kümmern. Die Einstellung $QUICKCACHE_OPTIMIZE habe ich deaktiviert, da ein OPTIMIZE TABLE in meinen Augen nur sehr selten durchgeführt werden muss. Das mache ich nun per Hand 1 mal im Monat. Wem das zu bieder ist, der setzt nen Cronjob dafür ein.

Jetzt ist natürlich jegliche Update-Möglichkeit versaut, aber Updates scheint das Projekt sowieso ziemlich selten spendiert zu bekommen 😉 Und zur Not kann man diese kleine Änderung bei der Zufallsberechnung auch später nochmal nachziehen.

Ich hoffe ich konnte euch QuickCache etwas näher bringen und zeigen, dass es sehr sinnvoll ist, jedoch nur, wenn es richtig eingestellt wird.
Und wer dieses Script nicht nutzen möchte, der muss ja nicht 😉

Jan hat 152 Beiträge geschrieben

14 Kommentare zu “QuickCache (früher JPCache) – Vorstellung und Konfigurationsempfehlung

  1. Die Rechnung ist relativ einfach:

    mt_rand() % x / x gibt eine Zahl zwischen 0 und 1, genauer (0;1] als Intervall

    p / 100 ebenfalls (p= Prozentwert)

    also werden nur zwei Zahlen zwischen 0 und 1 verglichen

  2. Auf die QC-Art hat man auf jeden Fall korrekt (bei Voraussetzung der Gleichverteilung des mt_rand()) die Wahrscheinlichkeit in % ganz exakt.
    Eleganz ist hierbei eher eine Streitfrage als eine, dir man wirklich beantworten kann.

  3. Ich habe QuickCache bei meiner Seite, die mit DokuWiki läuft, integriert.
    Das Problem hierbei ist, dass die Seiten nicht gecached werden dürfen, wenn ich angemeldet bin (wegen „Seite bearbeiten“).
    Ich habe daraufhin die Response-Header analysiert und herausgefunden, dass wenn man angemeldet ist es einen Cookie gibt, der mit „DW“ anfängt.
    Heraus kam dann folgende Lösung:

    $cachetimeout=900;
    foreach ($_COOKIE as $key => $value) {
    if (strpos($key,’DW‘)===0) {
    $cachetimeout=-1;
    break;
    }
    }
    require $_SERVER[„DOCUMENT_ROOT“].“/quickcache/quickcache.php“;

  4. Jan sagt:

    Jo genau. Bei meinen Projekten hab ich eine zusätzliche Funktion isLoggedIn() – und wenn da der Rückgabewert true ist (Login-Session ist gesetzt und gültig), dann wird $cachetimeout auf -1 gesetzt.
    Das ist eben das tolle an dem System: Man kann relativ flexibel auf äußere Gegebenheiten eingehen und trotzdem für die Massen eine gute Performance bieten.

  5. Also ich komme im Moment auch drauf, aber vor ein par Wochen hatte ich ebenfalls Probleme. Vielleicht hängt das auch mit dem DNS-Server vom Internet-Provider zusammen?

  6. Steffan sagt:

    Ich komm da auch nicht gut rein. daauert immer eine halbe ewigkeit….
    Ist dieser blog jetzt tod oder kommen noch ein paar Antworten???

  7. Sehr guter Artikel. Ich habe meine Projekte bisher immer durch eine eigene CachingEngine „beschleunigt“. Die TemplateEngine Smarty hat ja bekanntlich schon ein recht gutes Caching integriert an welche ich meine eigene CachingEngine angelehnt habe. Rennt echt super und bringt massive Performance-Gewinne. Werde mir aber QuickCache auf jeden Fall einmal ansehen – vielleicht kann man ja die ein oder andere Idee adaptieren 🙂

  8. @ Gedichte Geburtstag

    Mit Smarty arbeite ich fast täglich. Es ist einfach super. Nachdem ich meine Projekte auf Smarty umgestellt habe, ist die Performance um einiges angestiegen. Ich kann Smarty jedem nur wikrlich ans Herz legen. Es bedarf zwar einiger Einarbeitungszeit, aber wenn man erstmal drin ist, geht es superleicht von der Hand.

    QuickCache sagt mir nur vom Namen was. Ist das in einer Weise mit Smarty vergleichbar?

Eine Antwort schreiben

Ihre E-Mail-Adresse wird nicht veröffentlicht. Benötigte Felder sind markiert mit *

You may use these HTML tags and attributes: <a href=""> <blockquote cite=""> <pre lang=""> <b> <strong> <i> <em>