In letzter Zeit bekomme ich immer wieder mal die Frage, warum denn eine Webanwendung so langsam ist, bzw. was man denn tun könnte, um sie schneller zu machen. Nach meiner Erfahrung sind Performance Probleme bei Webanwendungen zu über 50% Datenbankprobleme. Besonders unerfahrene Entwickler haben im Rahmen ihrer Ausbildung zwar häufig den SQL Syntax und Regeln zur normalisierung gelernt, aber selten wird auf die Auswirkungen eines bestimmten Statements auf die Ausführungsgeschwindigkeit eingegeben.
Der folgende Artikel soll daher mal in grob vereinfachter und abstrakter Weise die Vorgänge und die unterschiedlichen Auswirkungen in einer relationalen Datenbank erklären. Der Artikel soll dabei weder alles wissenschaftlich exakt sein, noch geht er auf Datenbank spezifische Details ein, sondern soll als Grundlage dienen um einen ersten Eindruck einer Datenbank zu bekommen.
Zugrifssmöglichkeiten
Versucht man mit Hilfe eines einfachen SQL wie zum Beispiel:
select customerno, firstname, lastname from customer where lastname='Ma%'
auf eine Datenbank zuzugreifen, hat die Datenbank grundsätzlich drei verschiedene Möglichkeiten:
- Die komplette Tabelle wird nach Zeilen durchsucht, für die die angegebene Bedingung zutrifft. (FULL TABLE SCAN)
- Ein Index kann verwendet werden, um die betreffenden Zeilen anzusprechen. (INDEX ACCESS)
- Ein Zugriff über einen Eindeutigen Schlüssel ermöglichkeit den Zugriff auf die Daten. (UNIQUE INDEX ACCESS)
Im folgenden gehe ich davon aus, dass der Zugriff auf alle Zeilen in der Datenbank gleich schnell erfolgt, und alle Vergleichsoperationen gleich viel Rechnezeit benötigen. Damit hängt die Verarbeitungsgeschwindigkeit nur noch von der Anzahl der zu prüfenden Zeilen ab. Wenn ein Statement schneller sein soll, muss es also gelingen die Anzahl der Vergleiche zu reduzieren:
- Beim Abgleich aller Zeilen muss die Datenbank grundsätzlich jede Zeile der Tabelle prüfen. Der Aufwand eines Zugriffs ohne Index steigt also proportional mit der Anzahl der in der Tabelle.
- Nicht eindeutige Index Daten werden intern als Baum Struktur verwaltet. Dieser Baum ist sortiert so dass die Datenbank in der Wurzel mit der Suche beginnt und in jedem Knoten nur den Ast weiterverfolgen muss, der Zeilen mit der zutreffenden Bedingung beinhaltet. Der Aufwand einen solchen Baum zu durchlaufen ist proportional zur Tiefe des Baumes und diese Ergebit sich aus dem Logarithmus der Anzahl der Zeilen der Tabelle. Der Aufwand beim Zugriff über einen nicht eindeutigen Index steigt also logarithmisch mit der der Anzahl der Zeilen.
- Für eindeutige Index Daten berechnet die Datenbank eine Hash Funktion. Mit Hilfe dieser Funktion kann Sie aus dem übergebenen Parameter genau die Adresse im Speicher berechnen an der sich der gesuchte Datensatz befindet. Dabei ist es unabhängig ob es sich im eine sehr große oder sehr kleine Tabelle handelt. Der Aufwand beim Zugriff über einen eindeutigen Index ist unabhängig von der Anzahl der Zeilen in der Tabelle.
Was bedeutet das nun in der Praxis? Wenn meine Kundentabelle 20 Millionen Einträge hat und das Vergleichen einer Tabellenzeile 0,1 Millisekunden dauert, ergeben sich folgende Aufwände:
- Zum Auslesen aller Tabellenzeilen muss ich zwanzig Million Zeilen lesen und vergleichen. Das dauert zwei Million Millisekunden, oder 2000 Sekunden oder ca. 30 Minuten.
- Wenn ich einen nicht eindeutigen Index benutzen kann muss ln(20.000.000)= 17 Vergleiche durchführen, das dauert ca. 1,7 Millisekunden
- Wenn ich einen eindeutigen Index habe, muss ich nur einen Vergleich durchführen, das dauert ca. 0,1 Millisekunden
Das ist natürlich eine extrem vereinfachte Bedtrachtung die nur zur Kennzeichnung der Dimensionen dienen soll. Ich habe hier weder Zeiten für die Wahl des korrekten Index, für die Auswertung der Index Inhalte oder den Hash Algorithmus berücksichtigt. Ausserdem legt eine Datebank oft auch schon interne temporäre Index oder Tabellendaten an, die in der Praxis definitiv zu anderen Zeiten führen werden.
Man kann also mit Hilfe eines Index bei großen Datenmengen Zugriffszeiten um gewaltige Dimensionen beschleunigen. Die Kunst ist es also Index Daten und SQL Statements so aufeinander abzustimmen, das beide optimal zusammenspielen. Tipps man SQL Statements in dieser Richtung optimiert, gibt es im zweiten Teil.