Datenbank richtig nutzen (2) – Partitionierung

Im letzten Artikel habe ich beschrieben, wie man mittels Indizes die Performanz einer Datenbank wesentlich erhöhen kann. Doch leider sind Fälle möglich, in denen dies nichts hilft. Stellen wir uns also vor, wir haben eine Tabelle mit 3 Feldern (zwei INTEGER-Felder und ein VARCHAR-Feld). Wir wollen nun alle Zeilen abfragen, benötigen aber nur die beiden ersten Felder. Da alle Zeilen abgefragt werden sollen, hilft ein Index hier nicht weiter. Trotzdem kann man die Abfrage performanter gestalten. Die Lösung lautet Partitionierung.

Man unterscheidet zwischen zwei Arten von Partitionierungen, welche nun genauer angeschaut werden sollen.

vertikale Partitionierung
Die vertikale Partitionierung schneidet Spalten einer Tabelle ab und speichert die Daten in einer extra Tabelle. Wir spalten die eigentlichen Daten also auf zwei Tabellen auf und müssen diese beiden Tabellen verknüpfen, wenn wir alle Daten haben möchten. Hört sich dumm an? Das kommt darauf an.

Gehen wir zum Eingangsbeispiel zurück. Hier haben wir zwei INTEGER-Felder und ein VARCHAR-Feld. Wichtig ist nun die Kenntnis, dass INTEGER-Felder eine feste Größe haben. Egal, welche Zahl im Feld nachher steht, es ist immer gleich groß. Anders sieht es beim VARCHAR-Feld aus. Dessen Größe ist abhängig vom Feldinhalt, es ist aber maximal so groß, wie es beim Anlegen definiert wurde (maximal 255 Zeichen). Diese flexible Größe ist nun ein Nachteil bei der Abfrage.

Um nun die ersten beiden Spalten abzufragen, würde man folgende Abfrage verwenden:


Select feld1, feld2 from myTable

Man würde also erwarten, dass mySQL das dritte Feld vom Typ VARCHAR gar nicht beachtet. Das stimmt aber nicht. MySQL speichert alle Daten nacheinander in das Dateisystem (stimmt für fast alle Feld-Typen – BLOBS machen z.B. eine Ausnahme, sie werden extra gespeichert). Die einzelnen Zeilen sind durch entsprechende Steuerzeichen voneinander getrennt. Sie werden bei der Verwendung von Indizes direkt angesprungen. Wenn jedoch alle Daten abgefragt werden müssen, macht die Verwendung des Indizes keinen Sinn. Statt dessen werden die Daten sequenziell von der Festplatte gelesen. Dies ist in der Regel wesentlich schneller als jedesmal die Index-Daten zu lesen und dann wieder an die entsprechende Stelle zu springen (Die Lesekopf-Positionierung ist hier das bremsende Element). Aufgrund der flexiblen Größe des VARCHAR-Feldes muss die Datenbankengine nun aber das Feld betrachten, um herauszufinden, wann die nächste Zeile anfängt. Im Gegensatz zu Feldern mit fester Größe steht dies nicht von vornherein fest.

Es existieren hier also zwei Möglichkeiten, die Abfrage zu beschleunigen:

  • Wir verwenden anstatt des VARCHAR-Feldes ein CHAR-Feld. Damit haben wir eine feste Spaltengröße und die Abfrage kann schnell abgearbeitet werden. Da die Feldgröße nicht mehr flexibel ist, geht damit natürlich unter Umständen erheblich Speicherplatz verloren.
  • Wir speichern das VARCHAR-Feld in einer extra Tabelle. Damit ist dei Abfrage schnell möglich und es geht kaum Speicherplatz verloren. Lediglich die Abfrage aller drei Spalten ist aufwendiger. Hier muss natürlich abgewogen werden, ob sich der Performanzgewinn bei der Abfrage der ersten beiden Spalten lohnt.

Leider bietet mySQL für die vertikale Partitionierung keinen Automatismus an, so dass hier von Hand gearbeitet werden kann.

horizontale Partitionierung
Anders sieht es bei der horizontalen Partitionierung aus. Sie findet ihren Einsatz vor allem dann, wenn die Datenbank mit hohen Zugriffszahlen zu arbeiten hat. Aber auch Abfragen können mit ihr beschleunigt werden. Die Grundidee ist dabei, die Daten anhand eines bestimmten Kriteriums aufzuspalten und getrennt zu speichern. Dieses Verfahren wird meines Wissens nach von Slashdot angwendet: alle Artikel mit gerader ID werden auf einem anderen Server wie Artikel mit ungerader ID gespeichert. Dadurch werden die Datenbankserver bei vielen parallelen Zugriffen entlastet.

Jedoch ist auch schon das Aufteilen der Daten in unterschiedliche Bereiche schneller. Hier bietet mySQL seit der Version 5.1 (noch Beta) einen Automatismus an. Durch die Aufteilung ist ein paralleles Abfragen der Daten gleichzeitig möglich. Erläutern wir dies an einem Beispiel:

Wir haben eine Tabelle mit mehreren Feldern, eines davon heißt “SuchFeld” und wir haben darüber einen Index erstellt. So ist die Abfrage – nach unserem bisherigen Wissen – schnellstmöglich. Nun gehen wir davon aus, dass wir sehr viele Daten zur Verfügung haben (> 1 Mio. Daten). Genau jetzt macht die Partitionierung Sinn. Dabei gibt es nun 2 Möglichkeiten, die Partition zu erstellen:

  1. anhand der ID (RANGE-Partition)
  2. anhand des Suchfeldes (z.B. mit HASH-Partition)

Wir erstellen im Beispiel nun 4 Partitionen. Die Folge ist, dass wir zusätzlich 4 Index-Tabellen haben. Wurde die Partition nach Methode 1 erstellt, so können diese 4 Index-Tabellen gleichzeitig abgefragt werden (sofern die Hardware das kann). Das ist eine klare Beschleunigung der Abfrage. Noch schneller geht es nach Methode 2. Denn hier weiß mySQL bereits vor der Abfrage, in welcher Partition die Datenbankengine zu suchen hat. Somit muss nur 1/4 der Daten betrachtet werden. Selbst ohne die Verwendung des Indizes ist dies schon ein klarer Geschwindigkeitsvorteil.

Das ganze kann man nun noch verstärken, indem man die einzelnen Partitionen auf unterschiedliche Festplatten verteilt. So kommen sich die einzelnen Lese- und Schreibvorgänge nicht selbst in die Quere, was wieder einen deutlichen Geschwindigkeitsvorteil darstellt.

2 Gedanken zu „Datenbank richtig nutzen (2) – Partitionierung

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.