Das folgende wird ein etwas weniger humoristischer Beitrag über ein Thema, das sich inzwischen zu einem meiner Hauptprobleme als Datenbankadministrator entwickelt hat: BLOBs in Datenbanken. Ich werde versuchen, das Ganze etwas ausführlicher zu beschreiben, damit ich später BLOB-geneigte Datenbanknutzer für einen Einstieg in die Problematik auf diese Seite verweisen kann.
Fangen wir also ganz harmlos an: was bedeutet BLOB? Das Wort ist überraschenderweise kein echtes Akronym, sondern wurde laut Wikipedia von einem Mitarbeiter bei DEC erfunden, um einen “Haufen unstrukturierter Daten” (freie Übersetzung von mir) zu bezeichnen:
Blobs were originally just amorphous chunks of data invented by Jim Starkey at DEC, who describes them as “the thing that ate Cincinnati, Cleveland, or whatever”.
Erst später tauchten die Backronyme “Basic Large Object” bzw. “Binary Large Object” auf.
Doch was sind BLOBs nun genau? Grob gesagt stellen jegliche Daten, die im jeweiligen Kontext unstrukturiert erscheinen, BLOBs dar. Damit sind sowohl tatsächlich unstrukturierte Daten gemeint als auch, was der weitaus häufigere Fall ist, strukturierte Daten, deren innere Struktur im Kontext jedoch nicht erfasst und somit auch nicht genutzt werden kann.
Am Beispiel etwas konkreter: eine MP3-Datei hat zwar durchaus einen wohldefinierten, strukturierten Aufbau (ein Dateiheader gefolgt von Musikdaten, die in exakt spezifizierten Frames vorliegen, letztere widerum mit jeweils einem eigenen Header versehen). Dennoch stellt eine MP3-Datei im Kontext von Datenbanken einen BLOB dar, da die Datenbank mit dieser inneren Struktur überhaupt nichts anfangen kann. Sie kann lediglich den gesamten “Datenhaufen” 1:1 in ein BLOB-Feld kopieren. Dasselbe gilt für alle in Dateien gekapselten Daten (Graphiken, Videos, Office-Dokumente, usw. aber auch reine Textdateien), wenn keine Anwendung vor die Datenbank geschaltet wird, die die Daten “auseinandernimmt” und eventuell auch noch weiter aufbereitet.
Eine zentrale Eigenschaft, durch die sich BLOBs von den restlichen Daten in einer Datenbank unterscheiden, liegt darin, dass die einzigen Datenbankoperationen im jeweils vollständigen Lesen oder Schreiben des gesamten BLOB-Datenfeldes bestehen. Andere Operationen, die eigentlich den Ausschlag für den Einsatz von Datenbanken darstellen (sortieren, suchen, filtern, usw.) sind nicht möglich. Auch in komplexeren Datenbankabfragen können BLOB-Felder maximal in der Form von “… und dann hängen wir in der Ergebnistabelle noch den BLOB dran” auftreten.
Nachdem nun also feststeht, was BLOBs sind, wird sich der geneigte Leser und insbesondere der unbedarfte Datenbanknutzer fragen, was denn nun das Problem sei. Es gibt doch in jeder besseren Datenbank (und auch bei MySQL) entsprechende Datentypen, also rein damit und gut ist…
Fassen wir also zunächst zusammen, warum Datenbanknutzer (verständlicherweise) von BLOBs so begeistert sind:
- die Daten sind im Datenbankschema sauber eingebunden und (hoffentlich durch echte Schlüsselbeziehungen – bei MySQL/MyISAM nicht möglich) referenziert – die Daten können weder verwaisen, noch können die Referenzen auf die Daten auf einmal ins Leere zeigen
- in einem Datenbank-Dump sind sämtliche Daten enthalten: die strukturierten Daten und die BLOBs – dies erleichtert sowohl die Portierung als auch das komplette Zurücksetzen in einen früheren Zustand (Stichwort Backups) ungemein
- je nach Datenbanksystem ist es möglich, die BLOB-Daten durch programmierbare Operationen auf dem Datenbankserver (Trigger, Rules, …) zu bearbeiten
- die Rechteverwaltung (welcher Nutzer darf was?) ist deutlich leichter zu implementieren, wenn die BLOBs mit in der Datenbank liegen
Die Probleme, mit denen der oben beschriebene Komfort (und es geht hier ausschließlich um Komfort) erkauft werden, sieht der Nutzer nicht – ganz im Gegensatz zum Datenbankadministrator.
Grob kann man festhalten, dass BLOBs in Datenbanken immer ineffizient sind und zwar sowohl was die Zugriffszeit als auch den letztendlich verbrauchten Speicher angeht. Begründet liegt dies darin, dass bei allen Datenbanksystemen (und in Teilen auch bei MySQL) Datenkonsistenz die oberste Priorität besitzt.
So kann ein Datenbankserver z.B. nicht wie ein Fileserver dem Nutzer den Dateiinhalt einfach an den Kopf werfen (also lediglich: Datei lesen und den Inhalt an den Nutzer weiterleiten). Nein, ein Datenbankserver wird bei einem Zugriff auf einen BLOB (mindestens) folgende Operationen durchführen:
- prüfen, ob der Datenbanknutzer die Berechtigung zum Zugriff auf die Datei hat (über mehrere Hierarchieebenen hinweg – Datenbankrechte sind in der Regel umfangreicher als Filesystemrechte aufgebaut)
- prüfen, ob der BLOB gerade anderweitig gelesen/geschrieben wird oder sonstwie temporär gesperrt ist (dies dient der Sicherung der Datenkonsistenz – es soll verhindert werden, dass sich die Daten während des Zugriffs ändern könnten); falls ja, die Anfrage in einer Warteschlange eintragen
- sobald ein Zugriff möglich ist, diesen in den entsprechenden Systemtabellen eintragen, damit die folgenden Zugriffe bei Bedarf hingehalten werden können
- (nur zur Erinnerung: bis hierher wurde noch kein Byte des BLOBs gelesen!)
- die Datei aus dem datenbankinternen Format in das ursprüngliche Format umwandeln (eventuell sind je nach Datenbankconnector noch weitere Umwandlungen nötig)
- die Datei ausliefern
- die eingetragenen Sperren wieder entfernen
… und je nach Datenbanksystem wird der gesamte Zugriff auch noch parallel in einer separaten Textdatei protokolliert. Spätestens an dieser Stelle sollte einleuchtend sein, warum es aus Effizienzgründen eine blöde Idee ist, einen Datenbankserver als Fileserver zu missbrauchen – es wird aber noch schlimmer…
Stichwort Backups. Jeder, der sich schon einmal ernsthaft mit Backups beschäftigt hat, kennt die goldene Methode, um die drei gegeneinander konkurrierenden Größen Backuplaufzeit, Rückspielzeit und Backupgröße miteinander in Einklang zu bringen: inkrementelle Backups.
Als Datenbankadministrator muss man lernen, mit einer unbequemen Wahrheit zu leben: es gibt keine inkrementellen Backups! Zur Sicherung der Datenkonsistenz (Erinnerung: oberste Priorität) besteht jedes Backup zwangsläufig aus einem vollständigen Datenbankdump.
(Kleine Fußnote der Vollständigkeit halber: man kann bei echten Datenbanksystemen durch eine explizite, separate Sicherung der Write-Logs eine Art inkrementelles Backup simulieren, was auch durchaus nicht selten praktiziert wird, aber im Falle der BLOBs die Probleme auch nur verlagert – davon abgesehen, dass eine Backup-Strategie immer so trivial und unkompliziert wie möglich sein sollte.)
Dazu ein Beispiel aus unserer Praxis zur Verdeutlichung. Bei unseren PostgreSQL-Datenbankservern erfolgt einmal täglich ein Datenbankdump aller Datenbanken, wobei die Datenbanken jeweils erst einmal einzeln und dann noch einmal die gesamte Datenbankinstanz in einem eigenen Dump abgezogen werden.
Diese Datenbankdumps heben wir derzeit für 100 Tage auf (darüber hinaus heben wir noch die monatsersten Dumps ein Jahr lang auf und die jahresersten Dumps bisher ohne Begrenzung, des Weiteren machen wir noch ein zweites Backup pro Tag, das wir kürzer aufheben, aber für das folgende Rechenbeispiel beschränken wir uns auf die 100 Tage). Daraus folgt, dass jedes Byte in unserer Datenbank auf dem Backupserver das 200fache als Speicherplatz benötigt.
Dazu ein paar Rechenbeispiele: eine 100kB-Graphikdatei benötigt auf dem Backupserver langfristig nicht weniger als 19MB (in Wirklichkeit sogar noch etwas mehr, da die BLOBs im Datenbankdumpformat auch nochmal aufgebläht werden). Eine 578kB-Worddatei (Beispieldatei eines BLOB-geneigten Datenbanknutzers) belegt 112MB. Was mit Daten im MB-Bereich passiert, kann sich der geneigte Leser selbst ausrechnen.
Und nun haben wir Anfragen im zweistelligen GB-Bereich (und einen Ausreißer im dreistelligen GB-Bereich) erhalten…
Von unseren PostgreSQL-Servern konnten wir BLOBs bisher weitestgehend fernhalten, bei unseren MySQL-Servern ist das Kind schon in den Brunnen gefallen (was unter anderem dazu geführt hat, dass wir die obige Backup-Strategie zum Leidwesen der mehrheitlich BLOB-losen Nutzer dort nicht fahren können). Dadurch wurden wir aber auch schon mit einem weiteren, echten Problem konfrontiert: das Abziehen von riesigen, BLOB-lastigen Datenbanken kostet Zeit – viel Zeit – zuviel Zeit.
Denn zusätzlich besteht bei MySQL (auf Basis von MyISAM) das Problem, dass man Datenbanken nicht im laufenden Betrieb abziehen kann. Die Datenbank muss also für die Zeit des Datenbankdumps gesperrt werden. Was dies bedeutet, wenn die Dauer eines Dumps im Stundenbereich liegt, kann sich der geneigte Leser selbst ausmalen. Erfreulicherweise wurden wir bisher von dem Erlebnis von Kollegen verschont, bei denen BLOB-bedingt Backupzeiten von mehr als 24h auftraten – was bei täglichen Backups ziemlich unpraktisch ist und wo dann auch “echte” Datenbanksysteme nicht mehr weiterhelfen könnten.
Bleibt also die Frage, welche sinnvollen Optionen nun für einen Datenbanknutzer bestehen, der neben strukturierten Daten auch BLOBs sinnvoll verwalten möchte. Nun, es gibt eine Lösung, die sogar älter ist als BLOBs selbst (und zwar schlichtweg deshalb, da sie aus einer Zeit kommt, als Datenbanken noch keine BLOBs aufnehmen konnten): Referenzen.
Gemeint sind damit Referenzen, die man in der Datenbank (in Textfeldern) hinterlässt und die auf BLOB-Daten verweisen. Dabei kann es sich beispielsweise um vollqualifizierte Verzeichnis- und Dateinamen auf einem Fileserver oder URLs, die sonstwie auf den eigentlichen Speicherort des BLOBs verweisen, handeln. Der einzige, wirklich kritische Nachteil dieser Lösung besteht darin, dass die Sicherstellung der Konsistenz zwischen Datenbank- und Fileserverinhalt der zugreifenden Applikation aufgebürdet wird. Auch beim Backup muss man nun aufpassen, dass man Datenbank und Fileserver möglichst synchron sichert. Ansonsten löst dieser Ansatz aber schlagartig alle oben genannten Probleme.
Zu guter letzt noch, Ehre wem Ehre gebührt: dieser Artikel hat mir beim Schreiben sehr geholfen. In dem Artikel, der die gesamte Problematik vor allem aus der Sicht per PHP angebundener Webdatenbanken betrachtet, werden auch noch ein paar weitere Gründe gegen BLOBs genannt, die in diesem speziellen Kontext zusätzlich auftreten.
Auch diesen Artikel zu dem Thema fand ich sehr lesenswert. Der Autor ist ein DB-Admin, der für sehr große Datenmengen zuständig ist. Neben interessanten Erfahrungsberichten aus seiner Praxis und den oben genannten Gründen greift er unter anderem auch noch den Kostenaspekt auf (den er sehr zugespitzt formuliert: “If you’re storing your BLOBs, especially large media files in database, aren’t you choosing the most expensive file system available?”).
Kurzzusammenfassung all des Gesagten (siehe auch Subject): Du sollst nicht BLOBben!