Startseite aufrufen
« Zurück
Vor »

Kommentar schreiben | Drucken

Traffic und Serverlast in den Griff bekommen, Website-Geschwindigkeit steigern

-- 24.Januar 2006 (#57)

Wenn die Besucherzahlen steigen, dann steigt auch der Traffic und die Serverbelastung. Statt aufzurüsten und sich neue Server zu holen, sollte man erstmal darauf achten, dass man keine künstlichen Software-Bremsen eingebaut hat, die die Effizienz der Website senken.

In der nachfolgenden Liste werden Aspekte aus allen Bereichen der Website-Optimierung aufgegriffen. Dementsprechend werden sowohl die üblichen Fehler bei der PHP-Programmierung, als auch der suboptimale Einsatz von Grafiken angeschnitten. Die Liste ist dabei aber alles andere als vollständig – Ergänzungen sind also willkommen.


MySQL: Indizes benutzen

SQL-Datenbanken (ich werde im weiteren Verlauf stellvertretend für viele andere Datenbanken nur noch von MySQL sprechen) bieten die Möglichkeit an, über einen oder mehrere Indizes eine Art Vorsortierung vorzunehmen. Wenn eine Spalte einen Index hat, dann verwaltet MySQL intern eine sortierte „Kopie“ dieser Spalte. Sobald eine SQL-Abfrage kommt, kann MySQL auf diese Kopie zurückgreifen und ist dadurch schneller, als wenn die SQL-Query auf der unsortierten Spalte statfinden würde.
Spalten von großen Tabellen, die häufig SQL-Abfragen herangezogen werden, sollten daher einen Index bekommen. Es können auch mehrere Spalten einer Tabelle einen Index haben.


MySQL: Indizes temporär benutzen

Indizes haben auch einen Nachteil, nämlich dass sie bei jeder Änderung einer Tabelle neu angelegt werden müssen. Wenn etwas aus einer Spalte gelöscht, geändert oder hinzugefügt wird, muss MySQL also mehr Arbeit erledigen als bei Tabellen ohne Indizes. Indizes sind also beim Lesen schneller und beim Schreiben langsamer.
Wenn man also viele Änderungen an einer Datenbank hintereinander ausführt, dann kann es sinnvoll sein, wenn man die Indexierung kurzzeitig entfernt und am Ende der Änderungen wieder hinzufügt. So muss MySQL nur einmal alle Indizes neu anlegen.
Der folgende Tipp ist ganz ähnlich: Wenn man auf einer indexlosen Tabellen mehrere SQL-Abfragen machen muss, dann kann es sinnvoll sein, hierfür einen Index anzulegen und danach wieder zu entfernen.


MySQL: Filterfunktionen der Datenbank nutzen

Es gibt 2 Möglichkeiten, die Daten aus einer Datenbank zu filtern: Einmal über MySQL und einmal über PHP, Perl oder einer anderen Programmiersprache. Man könnte also theoretisch die ganze Datenbank mit einem Query einlesen und sich dann mit PHP die Informationen rauspicken, die man wirklich braucht.
Das ist aber eine schlechte Idee, denn in jedem Fall ist MySQL schneller. MySQL besitzt diverse mathematische Funktionen, Aliase und Abfragemöglichkeiten, die man als Filterfunktionen nutzen kann. Also: Filterung sollten von der Datenbank durchgeführt werden.


MySQL: Normalisierung nicht übertreiben

Bei Datenbanken will man Redundanzen möglichst vermeiden, weil man sonst die Konsistenz der Daten schneller gefährdet und der Platzverbrauch größer ist. Deshalb soll man alle Daten, die doppelt vorkommen, in Extra-Tabellen auslagern. Die Verknüpfung von zusammengehörigen Tabellen erfolgt dann über einen gemeinsamen Schlüssel. Diese Aufsplittung in kleine Teil-Tabellen wird „Normalisierung“ genannt.
Wenn man die Normalisierung übertreibt, erzielt man aber einen negativen Effekt auf die Geschwindigkeit. Bei der Normalisierung sollte man also einen Kompromiss eingehen; eine gewisse Redundanz ist akzeptabel.


MySQL: COUNT, LIMIT und SELECT

  • SELECT COUNT( * )

    Um die Anzahl der Suchtreffer zu überprüfen, kann man mysql_num_rows benutzen oder COUNT. Besonders effizient, um die Anzahl von Zeilen herauszufinden, ist COUNT( * ) ohne WHERE, denn dann kann MySQL das Ergebnis direkt aus den (schnellen) internen Verwaltungstabellen holen.

  • LIMIT 1

    Es sollten stets nur so wenige Zeilen wie möglich aus der Datenbank gefischt werden. Wenn zu erwarten ist, dass auf einen Filter mehrere Zeilen zutreffen, obwohl vielleicht nur eine Zeile benötigt wird, dann sollte man die Anzahl der Ergebnisse mit LIMIT 1 begrenzen.

  • SELECT

    Genauso wie man die Anzahl der Zeilen mit LIMIT begrenzen kann, so kann man auch die Anzahl der Spalten begrenzen. SELECT * wählt alle Spalten aus, für die der Suchfilter zutrifft. Das ist aber überflüssig und durch die große Ergebnistabelle wird unnötiger Speicher verbraucht. Wenn man beispielsweise nur eine Spalte benötigt, dann sollte man nicht SELECT * benutzen, sondern SELECT spaltenname(n). Statt die Namen der Spalten direkt anzugeben, kann man auch Spaltennummern verwenden.


MySQL/PHP: mysql_unbuffered_query statt mysql_query

mysql_unbuffered_query führt dazu – wie es der Name vermuten lässt – dazu, dass die SQL-Abfragen nicht zwischengespeichert werden. Das spart zum einen Speicher (insbesondere bei langen Ergebnissen), zum anderen kann man die Ergebnisse direkt in PHP weiterverarbeiten, während die Abfrage eigentlich noch läuft. Andernfalls muss man warten, bis MySQL die Query abgeschlossen hat.
Leider kann man bei mysql_unbuffered_query aber nicht mysql_num_rows() und mysql_data_seek() benutzen. Außerdem kann man erst wieder eine neue SQL-Anfrage stellen, wenn die alte abgeschlossen wurde.


PHP-Optimierungen

Website-Optimierung muss früher oder später natürlich auch bei der Programmiersprache ansetzen. Zwar ist gegen schlechte Algorithmen kein Kraut gewachsen, aber dafür gegen schlechte Programmiertechniken.
Zu PHP gibt es auf http://www.thegeek.de/blog/index.php?type=archiv&post=200412 einige nützliche Kniffe und Tricks. Ich greife nochmal einige Punkte auf und ergänze…


PHP/Andere Programmiersprachen: switch-case-Optimierung

Bei switch-Bedingungen sollten die Fälle, die der Wahrscheinlichkeit nach am häufigsten eintreten, ganz oben stehen. Wenn der Parser auf diesen Fall tritt, kann er die switch-Bedingung verlassen und mit dem restlichen Quellcode fortfahren. Andernfalls muss er erst noch die restlichen case-Bedingungen prüfen.


PHP/Andere Programmiersprachen: if-elseif-else-Optimierung

Im Prinzip gilt bei if, elseif und else dasselbe wie bei switch. Die Fälle, die am häufigsten eintreten, sollten zuerst abgefragt werden stehen und die seltensten Fälle sollten zuletzt werden. Bei verschachtelten if-Bedingungen sollten also (insofern sich die if-Bedingungen voneinander unabhängig sind und sich austauschen lassen) häufige Fälle außen und seltene Fälle innen abgefragt werden.


PHP/Andere Programmiersprachen: Referenzierung benutzten

Es gibt zwei Möglichkeiten, Funktionen aufzurufen: Call by value und Call by reference. Call by value wird am häufigsten benutzt, dabei wird die Variable beim Funktionsaufruf kopiert und die Funktion kann mit diesem Wert dann weiterarbeiten. Bei Call by value finden die Änderungen an den Variablen nur innerhalb der Funktion statt. Bei Call by reference hingegen wird die Reference auf eine Variable übergeben. Das Kopieren entfällt und daher ist der Funktionsaufruf per Referenz schneller. Nachteilig daran ist aber, dass Änderungen in der Funktion auf eine referenzierte Variable sich auch auch unmittelbar auf die Variable im Rahmenprogramm auswirken, was bei sorgloser Benutzung schneller zu Seiteneffekten führen kann.


PHP: Kompiler/Optimizer

PHP-Skripte werden – wie alle Skripte – von einem Interpreter verarbeitet. Skripte in Skriptsprachen werden nicht im eigentlichen Sinne kompiliert, wie es z.B. bei C/C++ oder Pascal der Fall ist. Das führt dazu, dass PHP-Skripte bei jedem Aufruf neu durch den Interpreter gejagt werden müssen, auch wenn sich nichts geändert hat. Veschiedene Optimierungsprogramme versuchen Abhilfe zu schaffen und klinken sich dazwischen. Sie speichern die PHP-Skripte im kompilierten Zustand, so dass beim nächsten Aufruf des Skriptes keine unnötige Verarbeitungszeit anfällt. Das Skript wird dann häufig direkt aus dem Hauptspeicher ausgeführt.
PHP-Optimierer sind z.B. TurckMMCache oder eAccelerator.


PHP/Andere Programmiersprachen: Kurzschlussauswertung

Funktionale Programmiersprachen benutzen häufig eine Kurzschlussauswertung. Das heißt, dass z.B. bei einem AND-Konstrukt (bzw. &&) die zweite Bedingung gar nicht mehr abgefragt wird, wenn die erste Bedingung bereits falsch ist.

Beispiel:

int a = 5;
int b = 100;
if ((a > 10) && (b > 50)) {…}

Der Fall a > 10 ist bereits falsch, also wird b > 50 erst gar nicht mehr geprüft, weil der ganze Ausdruck schon falsch ergibt. Für OR (||) gilt ähnliches:

int a = 5;
int b = 100;
if ((a < 10) || (b > 50)) {…}

Der erste Fall a < 10 ist bereits wahr, also braucht b > 50 nicht mehr ausgewertet werden.

Ohne die Kurzschlussauswertung würde auch die folgende Bedingung zu einem Divisionsfehler durch 0 führen:

int a = 0;
int b = 10;
if ((a > 0) && ((100/a) > b)) {…}

a ist 0, also würde (100/a) > b eigentlich einen Divisionsfehler verursachen. Da die Auswertung aber bereits bei a > 0 abbricht, läuft das Programm ohne Fehler weiter.

Diese Verhalten kann man sich zunutze machen. Im Prinzip ist die Idee genauso wie bei switch-case und if-elseif-else. Bei AND (&&) sollten die am seltensten auftretenden Fälle zuerst stehen. So muss die zweite, dritte, vierte… Bedingung seltener geprüft werden. Bei OR (&&) hingegen ist es umgekehrt, hier sollten die am häufigsten eintretenden Fälle ganz vorne stehen.


Apache: SSI benutzen

SSI ist keine vollständige Programmiersprache, bietet aber doch ein paar nette Möglichkeiten. Das besondere daran ist, dass SSI sozusagen im Server integriert ist und damit sehr effizient ist. Die Bedingung ist zudem recht leicht. Mit SSI kann man z.B. die letzte Änderung an einer Datei feststellen oder das Datum anzeigen. Man kann auch verschiedene Umgebungsvariablen auslesen und anzeigen (z.B. den Servernamen). SSI kann auch Programme ausführen, die Rückgabeparameter der Programme weiterarbeiten oder an ein CGI-Skript weiterreichen. Es lassen sich auch Frames und PHP-Includes durch SSI-Includes ersetzen. Zusätzlich bietet SSI auch rudimentäre Sprachkonstrunkte wie if und else an.
Kurz gesagt: SSI ist schnell und sollte, wenn möglich, anstelle von PHP/CGI eingesetzt werden oder diese unterstützen.


Perl: mod_perl

Perl ist ursprünglich nicht als Internet-Programmiersprache geboren worden, hat sich dieses Feld aber doch sehr schnell erobert. Bei der „normalen“ Perl-Programmierung wird beim Ausführen eines Perl-Skriptes jedes Mal eine neue Instanz des Perl-Parsers gestartet. Der Perl-Parser arbeitet bei Internetprogrammen ganz ähnlich, was aber den Nachteil hat, dass Perl immer wieder neu gestartet werden muss. Abhilfe schafft mod_perl, welches die Perl-Instanz offen hält. Die Ausführgeschwindigkeit steigt somit.
Neben mod_perl gibt es noch andere Projekte wie FastCGI, die auf dem selben Prinzip beruhen.


Perl: Das Modul CGI

In der Regel ist die objektorientierte Programmierung etwas langsamer bzw. speicherlastiger als die prozeduale Programmierung. CGI ist ein extrem beliebtes und mächtiges Modul für die Manipulation und Ausgabe von Headern und HTML. Das Modul CGI ist komplett objektorientiert, bietet aber auch die Möglichkeit an, prozedual darauf zuzugreifen. Da aber CGI objektorientiert ist, müssen die prozedualen Zugriffe erst „umgebogen“ werden. Der objektorientierte Zugriff ist hier also schneller.


Perl vs. PHP

Perl ist im Allgemeinen etwas schneller als PHP. Es wäre also eine Überlegung wert, manche Dinge lieber in Perl umzusetzen. Benchmarks zwischen Perl und PHP gibt es unter http://shootout.alioth.debian.org.


Traffic verringern

Traffic verursacht ebenso Netzlast. Daher folgen jetzt auch ein paar Hinweise, wie man den Traffic eingrenzt.


Apache: Auf großzügigeres Caching setzen

Apache bietet mit dem Modul mod_file_cache die Möglichkeit, statische Inhalte (gilt also nicht für CGI/PHP) zwischen zu speichern. Die Daten (z.B. ein Logo) werden dann im Speicher belassen. Außerdem steigt dadurch die Geschwindigkeit. Für dynamische Inhalte gibt es das Modul mod_cache. Man kann hier beispielsweise angeben, wann eine Datei oder eine Seite ablaufen soll, wie lange sie im Cache gehalten werden soll, wie groß der Cache sein soll etc. pp.


Apache: HTTP 1.1 Kompression einschalten

Seit HTTP 1.1 gibt es die Möglichkeit, Daten komprimiert auszuliefern. Modem-Besitzer haben davon nicht viel, weil Modems eine eingebaute Kompression benutzen und außerdem steigt wegen der (De-)Komprimierung empfindlich die CPU-Belastung, aber die Traffic-Einsparungen sind so ennorm, dass sich die Kompression rentiert. Besonders (X)HTML-Seiten bestehen ASCII, also reinem Text, der sich sehr gut komprimieren lässt.


Apache: Weitere Webserver-Optimierungen

Zu dem Thema Webserver- und anderen Optimierungen gibt es noch einen hilfreichen Artikel auf SelfHTML: Häuptling Schnelles Wiesel: Wirksames Tuning für viel besuchte Webauftritte.

Formularprüfung mit JavaScript

JavaScript eignet sich hervorragend um Formulareingaben zu prüfen. So muss die Seite nicht neu geladen werden, was nicht nur Traffic spart, sondern auch deutlich schneller und komfortabler für den Benutzer ist. Man sollte aber berücksichtigen, dass die Benutzereingaben auch einer Prüfung unterzogen werden müssen, wenn JavaScript nicht vorhanden ist. Um die zusätzliche Prüfung per PHP/Perl/… kommt man also nicht herum.

Größe von Grafikdateien reduzieren

Grafiken sind sicherlich einer der größten Trafficverursacher. Häufig werden die Grafiken ohne zusätzliche Optimierung hochgeladen. Folgendes sollte man sich fragen:

  • Habe ich das richtige Grafikformat gewählt? GIF ist für Logos gut geeignet, JPG hingegen für Fotos und PNG für Fotos ohne Qualitätsverlust.

  • Kann ich die Anzahl der Farben noch reduzieren (gilt jetzt speziell für GIF)?

  • Kann ich noch Teile von der Grafik wegschneiden und gerade einfarbige Flächen am Rand ggf. durch HTML ersetzen?

  • Kann ich Grafiken zusammenführen? Dadurch entsteht weniger Overhead und es müssen weniger HTTP-Anfragen an den Server gestellt werden.

  • Kann ich Grafiken durch Text ersetzen?


CSS: Stylesheets auslagern

CSS lässt sich in Stylesheets auslagern, welche dann nur einmal vom Browser geladen werden müssen und dann im Cache des Besuchers liegen. Die Stylesheets müssen also nur genau einmal heruntergeladen werden. Dadurch fällt weniger Traffic an und die Ladezeit ist geringer. Darüber hinaus kann man auch signifikante Traffic-Einsparungen erzielen, wenn man Tabellen-Layouts (vor allen Dingen wenn noch Spacer-GIFs benutzt werden) durch CSS-Layouts ersetzt.


JavaScript: Skripte in externe Dateien auslagern

Auch JavaScripts müssen dadurch vom Besucher nur einmal herunterladen werden. JS-Dateien aus dem Cache zu holen geht schneller und spart Traffic.


HTML: Code „komprimieren“

HTML-Code stellt eher den Tropfen auf dem heißen Stein dar, weil man hier kaum Traffic einsparen kann. Wichtigste Voraussetzung ist natürlich, dass man auf seiner Seite ein sauberes, strukturiertes Markup verwendet (also am besten ein CSS-Layout auf XHTML-Basis). Darüber hinaus kann man HTML-Dokumente „komprimieren“, indem man Zeilenumbrüche und HTML-Kommentare entfernt. Am besten legt man sich eine Sicherungskopie der Originaldateien an. So kann man die Sicherungskopien weiterhin für die eigentliche „HTML-Programmierung“ verwenden, während die komprimierten HTML-Dateien auf dem Server hochgeladen werden. Darüber hinaus gibt es in HTML-Codes häufig überflüssige Tags und Attribute. Das Tool HTML Tidy kann hier wertvolle Arbeit leisten, um derlei Verschwendung auf die Schliche zu kommen.
Durchschnittlich ist zu erwarten, dass die Größe des HTML-Codes um 10% schrumpft. Selbiges gilt für CSS, denn auch hier kann man Absätze, Leerzeichen, Zeilenumbrüche usw. entfernen.


Geschrieben von
anna (anonym)
-- 07.Februar 2008 (#21)

Schöner Artikel .. würde mich freuen wenn davon hin und wieder mehr zu lesen ist hier. Leider fehlt es im Netzt an konkreten Beispielen für all diese einzelnen Optimierungsphasen. Es gibt zwar viele seiten wo dieses Thema behandelt wird, aber wenn dort Beispiele aufgeführt werden, dann sind diese schon aus längst vergangener Zeit.


Geschrieben von
Dirk
-- 06.Februar 2006 (#2)

Danke. OPTIMIZE TABLE ist doch eigentlich dazu gedacht, einmal die Tabellen aufzuräumen und nicht benötigten Speicherplatz (also Festplatte, nicht Arbeitsspeicher) freizugeben. Ich werde aber zu Hause nochmal nachgucken, ob dass auch geschwindigkeitsmäßig was ausmacht.


Geschrieben von
Cord (anonym)
-- 05.Februar 2006 (#1)

Mal wieder ein hilfreicher und gut geschriebener Artikel!
Zur MySQL-Optimierung könnte man vielleicht noch die OPTIMIZE TABLE-Syntax hinzufügen, die gerade nach umfangreichen Änderungen benutzt werden sollte/kann.
Funktioniert allerdings nicht mit InnoDB-Tabellen.

Link: http://dev.mysql.com/doc/refman/4.0/de/optimize-table.html

Grüße
Cord


Geschrieben von
me (anonym)
-- 12.Dezember 2006 (#11)

man kann auch am mysql server spielen.
dazu liegen sogar schon "default" konfigurationen in den meisten linux distributionen:

/usr/share/mysql/my-huge.cnf
/usr/share/mysql/my-large.cnf
/usr/share/mysql/my-medium.cnf
/usr/share/mysql/my-small.cnf


Geschrieben von
masterk (anonym)
-- 28.September 2006 (#4)

Danke, sind einige interessante Tipps bei.


Geschrieben von
Hanfgaertner (anonym)
-- 22.Oktober 2007 (#19)

Inwiefern bringt die HTTP Kompression nach heutigen Maßstäben noch etwas?
Ist vielleicht aber auch nur technische Arroganz meiner generation kleinere Optimierungen unter den Tisch fallen lassen zu wollen =P


Geschrieben von
Jens (anonym)
-- 02.November 2006 (#10)

zu:
"PHP/Andere Programmiersprachen: Referenzierung benutzten
…
Bei Call by reference hingegen wird die Reference auf eine Variable übergeben. Das Kopieren entfällt und daher ist der Funktionsaufruf per Referenz schneller. …"

Mal abgesehen davon, dass call-by-reference ab PHP 5.0 standard ist gilt das auch nicht wirklich für ältere PHP-Versionen.
PHP benutzt bei call-by-value einen Mechanismus, der die übergebenen Werte nur dann kopiert, wenn sie in dem Unterscope tatsächlich verändert werden (Stichwort: copy-on-write). Ändert man an dem übergebenen Skalar/Array/Objekt nichts, dann gibt es keinen (messbaren) Performanceunterschied zwischen call-by-value und call-by-reference.
Natürlich gilt grundsätzlich, dass es mit call-by-value nicht möglich ist den Wert der übergebenen Variable in dem übergeordneten Scope zu ändern, aber das ist ein anderes Thema.

mfg
Jens


Das Hinzufügen von Kommentaren wurden deaktiviert.


Menü
Beitragsübersicht
Kontakt & Impressum
Über mich
Linkempfehlungen/Linkpartner

Letzte Kommentare
»Vorstellungsgespräch: E…
»Die Krux mit den Hinter…
»Bewerbung: Standard-Abs…
»Glossar: Quirks-Modus (…
»Glossar: Quirks-Modus (…

Meta
RSS 2.0
XHTML-Validierung
CSS-Validierung

Backlinks
»Spam vermeiden
»Internetagentur Ulm
»Bruststraffung
»Fernstudium
»TuS Graf Kobbo Tecklenburg
»Interpretation