Archive for the ‘Cache’ tag
High Performance: Caching (reloaded) mit PHP
Gastartikel von Oliver Sperke.
Ich bin 35 Jahre alt und seit 10 Jahren selbständiger Webentwickler. Mein Fokus liegt dabei auf der Erstellung, Beratung und Optimierung in den Bereichen High Performance, Usability und Sicherheit in den gängigsten Internetsprachen: PHP, HTML, Javascript und CSS.
Nach langem Arbeiten an einem Projekt fängt der ambitionierte Entwickler an, zu testen, wie sich seine dynamische Internetseite unter Last verhält. Da ja jeder von uns von Millionen Besuchern träumt, will man natürlich auch wissen, wie sich Millionen von Besucher anfühlen und ob unser „kleines Kunstwerk“ davon genau so begeistert wäre wie wir. Dynamische Webseiten sind toll, allerdings hat der gemeine Internetserver ein großes Problem damit. Die Erzeugung ist meist sehr aufwendig. Daten müssen aus Datenbanken geholt werden, Berechnungen wollen berechnet werden und Blogeinträge müssen wie Blogeinträge aussehen.
Seit Jahren hat sich eine simple Technik etabliert, die diese gequälten Webserver entlastet. Jeder fortgeschrittene Entwickler kennt und liebt sie, weil sie so schön einfach und universal einsetzbar ist: *trommelwirbel* Das Caching *tusch*. Da aber Caching an sich ein uralter Hut ist, will ich Euch zeigen, wie Ihr evtl. Eure Performance mit minimalen Änderungen mehr als verdoppeln könnt.
Am Anfang war der Benchmark
Meine Lieblings-Session auf der PHP-Unconference 2009
Die Unconference war viel zu kurz, die Sessions waren zu 95% extrem interessant und spannend. Mindestens genauso spannend waren die Gespräche zwischendurch. Es gibt im Wiki der Unconf zu jeder Session eine Zusammenfassung, trotzdem möchte ich hier versuchen, die wichtigsten Inhalte meines favorisierten Vortrags nochmal nachzuarbeiten. Die Session trug erst den Titel „PHP Performance Optimierung“, doch die 4 Vortragenden haben sich dazu entschlossen, ihn kurzerhand umzubenennen in „PHP Performance Pessimierung“. Denn aus Fehlern lernt man am besten.
Die Liste der Speaker lässt Großes erwarten: Johann-Peter Hartmann, Kris Köhntopp, Lars Jankowfsky und Stefan Priebsch
Die einführenden Worte begannen mit einer kurzen Zusammenfassung. Frameworks und vor allem das ganze „drumherum“ helfen uns, ein Webprojekt langsam zu machen, es ist nicht nur PHP selbst. Und man kann sehr vieles tun, damit doch wieder eine unerträgliche Performance zu Tage kommt und der Benutzer keine Lust mehr auf die Webseite hat.
Was bedeutet Performance? Die Stichworte „Latenz“ (ideal sind 29 Sekunden, weil nach 30 Sekunden der Browser in ein Timeout läuft), „Durchsatz“ (wie schaffe ich es, dass möglichst nur 1 Benutzer gleichzeitig die Webseite nutzen kann), und Funktion (wenn nach einem Klick auf einen Knopf nichts passiert) wurden genannt. Daran müssen wir nun arbeiten.
Damit wir etwas zu pessimieren haben, wurde eine häufig auftretende Architektur aufgemalt, wo wir ansetzen können. Diese sieht so aus:
Wir beginnen im Browser. Javascript und vor allem AJAX ist ein prima Werkzeug, um eine Seite unbenutzbar zu machen, und möglichst viele Ressourcen auf dem Server zu verbrauchen. Ideal sind viele AJAX-Requests, möglichst viele Informationen durch Javascript verarbeiten („quatsch, wer braucht schon ein LIMIT im SQL-Server, Javascript kann auch nur die ersten 10 Ergebnisse anzeigen und den Rest verwerfen“). Dabei laden wir möglichst viele Dinge, die wir dann nicht anzeigen.
Im Loadbalancer wollen wir natürlich immer Level 7 inspecten, damit wir möglichst viel zu verarbeiten haben und es möglichst lang dauert, eine Entscheidung zu finden. Wir bauen viel Logik darein, damit wir idealerweise jeden Request auf den am meisten belasteten Server zuteilen.
Der Webserver speichert idealerweise die Sessions in einer Datenbank, damit wir schon für sowas ordentlich viel Traffic und Performance auf die Datenbank-Server verlagern. Die Dateien liegen auf einem NFS, damit bei jedem stat auch ein Netzzugriff stattfindet. Auf keinen Fall darf dort Memcache verwendet werden, wir cachen nicht im Speicher sondern auf der Festplatte. Durch möglichst komplexe Logik, gleichzeitigen Cache-Wipe, kompliziertes (Dead)-Locking, Abhängigkeiten und nicht eindeutige Cache-Keys bekommen wir ideale Voraussetzungen, um durch Caching noch langsamer als vorher zu arbeiten. Wunderbar!
Wenn dann PHP selbst drankommt, dürfen wir auf keinen Fall einen Byte-Code-Cache nutzen. PHP muss bei jedem Request die Scripte neu kompilieren, es könnte ja sein, dass sich die Dateien von einen auf den anderen Request geändert haben! Möglichst viele include_once auf relative Pfade helfen uns, viele Festplatten-Zugriffe (file-exists usw) zu generieren. Einige Frameworks unterstützen uns dabei auch ideal. Falls wir einen Autoloader verwenden, sollte das am häufigsten verwendete Verzeichnis auch als letztes durchsucht werden, so generieren wir für jeden Loadversuch extra Zugriffsversuche! Natürlich dürfen wir auch nicht vergessen, den Virenscanner zu aktivieren. PHP-Scripte sind hoch gefährlich und müssen bei jedem Zugriff gecheckt werden!
Mit version_compare und ini_set werden wir auch bei jedem Script die grundlegenden Einstellungen prüfen und neu setzen. Die php.ini könnte sich ja auch geändert haben.
Auch wenn wir sie anfangs nicht brauchen, stellen wir natürlich zu jeder Datenbank eine Verbindung her, wir könnten sie ja später brauchen, und dann haben wir sie schon da. Das selbe gilt für große Klassen, die wir evtl. später brauchen könnten. Auch ist die Datenbank ideal, um Bilder und anderen statischen Content zu speichern. Diese Daten holen wir dann mit PHP und liefern sie an den Browser. Statischen Content direkt ausliefern können wir nicht tun, wir müssen doch jeden Zugriff mittels PHP zählen, und das Cachen im Browser verhindertn, die Bilder könnten sich ja jederzeit ändern.
Wir brauchen zur Anzeige natürlich auch eine effektive, sehr funktionsreiche Template-Engine. SMARTY bietet sich da an, da haben wir ähnlich wie in PHP die if-Abfragen, Schleifen und Ausgaben. Perfekt, das brauchen wir, und PHP kann das nicht!
ORM ist auch gerade in der Mode, und wir wollen ja nicht unsere SQL-Statements selbst schreiben. Manuell Optimieren ist out, ORM bieten die Möglichkeit, den Query bei jedem Request neu zu generieren. Außerdem sind wir damit unabhängig von der Datenbank, denn wir wechseln sie ja ab und zu, deshalb lieber abstrakt bleiben und das von komplexen Frameworks erledigen lassen. Stored Procedures lassen wir auch weg, denn wir wollen ja die Datenbank entlasten und darauf nichts speichern.
—–
Es war auf jeden Fall der lustigste Vortrag auf der Unconf, die Speaker und die Hörer hatten viel Spass bei der „Optimierung“ und auch ein sehr schöner Einstieg und Überblick für weitere Sessions.
Stichworte und Bilder findet ihr im Wiki-Eintrag zur Session. Hier nochmal ein Dankeschön an die Speaker, ihr seid spitze!