Denormalisierung

Unter Denormalisierung versteht man die bewusste Rücknahme einer Normalisierung zum Zweck der Verbesserung des Laufzeitverhaltens einer Datenbankanwendung. Aus Sicht der ANSI-SPARC-Architektur wird die konzeptionelle Ebene eines Datenmodells vollständig normalisiert entworfen. Unabhängig davon kann die interne Ebene gezielt denormalisiert entworfen werden. Denormalisierung findet also ausschließlich auf der internen Ebene statt und entbindet nicht von der Forderung, zuvor die konzeptionelle Ebene zu normalisieren.

Ein logisch ideales („normalisiertes“) Datenmodell ist vollkommen redundanzfrei – abgesehen von der technisch notwendigen Mehrfachspeicherung von Fremdschlüsseln bei Primärschlüssel-Fremdschlüssel-Beziehungen.

Mit Denormalisierungen lassen sich oftmals wesentlich größere Performance-Verbesserungen erreichen als mit einem Tuning der Datenbankinstallation.

Neben der Verbesserung des Laufzeitverhaltens wird Denormalisierung auch angewandt, um die Komplexität eines Systems zu verringern oder um die Administrierbarkeit der gespeicherten Daten zu erleichtern.

Bereiche der Denormalisierung

Rücknahme der ersten Normalform

Verstöße gegen die erste Normalform werden meistens zur Vermeidung einer unnötigen Komplizierung des Datenhaushalts vorgenommen.

Die erste Normalform fordert eine atomare Speicherung von Daten, das bedeutet, dass in einem Attribut (= einem Datenbankfeld) nur atomare (=unzerteilbare) Informationen abgelegt werden dürfen. Beispiel: Die Definition eines 100 Zeichen langen Datenfeldes zur Aufnahme von einer oder mehrerer Telefonnummern verstößt gegen die Forderung der ersten Normalform. Um die erste Normalform zu erfüllen, müsste für die Speicherung mehrerer Telefonnummern eine eigene Tabelle geschaffen werden. So könnten zu einer Person beliebig viele Telefonnummern gespeichert werden. Die Unterbringung einer oder mehrerer Telefonnummern in einem einzigen Datenfeld ist jedoch oft völlig ausreichend und die Komplexität des Systems wird dadurch reduziert.

Ein weiteres Beispiel für eine aus praktischen Gründen sinnvolle Verletzung der ersten Normalform ist die Speicherung von Titel, Vorname und Nachname in einem einzigen Datenfeld. Solange in dem System nicht auf die Einzelkomponenten des Namens zugegriffen werden muss, ist eine Speicherung der einzelnen Namenskomponenten in einem einzigen Textfeld ebenfalls eine gute Möglichkeit zur Vereinfachung des Systems.

In den meisten Datenbanksystemen wird die Straße und die Hausnummer (mit evtl. noch ergänzenden Buchstaben) in einem einzigen Datenfeld gespeichert, obwohl diese Vorgehensweise strenggenommen gegen die erste Normalform verstößt.

Rücknahme der zweiten oder dritten Normalform

Die zweite und die dritte Normalform fordern, dass alle abhängigen Attribute allein von den Schlüsselkandidaten abhängig sein dürfen. Alle Relationen, die diese Forderungen nicht erfüllen, müssen aufgespalten werden. Dadurch entstehen viele neue kleinere Tabellen. Um auf diese Daten zuzugreifen, müssen die Daten dieser Einzeltabellen wieder zusammengeführt werden durch Verwenden von SQL-Statements mit Joins. Die Ausführung eines Joins ist für das DBMS meistens zeitaufwändiger als der Zugriff auf eine einzige Tabelle.

Die zweite oder dritte Normalform wird meistens zurückgenommen mit dem Ziel, einen Join zu vermeiden. Es betrifft typischerweise zwei Tabellen, die in einer N:1-Beziehung zueinander stehen. Beispiel: Mitarbeiter und Abteilung. Wenn viele performance-kritische Lesezugriffe die Daten der Mitarbeiter und zusätzlich den Abteilungsnamen benötigen, dann kann die zusätzliche Speicherung des Abteilungsnamens in jedem Satz der Mitarbeitertabelle sinnvoll sein. Diese Zugriffe können dann alleine aus den Daten in der Mitarbeitertabelle bedient werden. Der zusätzliche Zugriff auf die Abteilungstabelle ist nicht mehr erforderlich.

Diese Art der Denormalisierung wird perfektioniert bei der dimensionalen Modellierung eines Data-Marts für ein Data-Warehouse. Wenn die Dimensionstabellen vollständig normalisiert gestaltet werden, dann gibt es eine Vielzahl von Einzeltabellen, die untereinander durch Fremdschlüssel-Beziehungen verbunden sind. Das Datenmodell sieht aus wie eine Schneeflocke, daher die Bezeichnung Schneeflockenschema. Für den Zugriff auf die Daten sind oft Joins auf die vielen durch die Normalisierung herausgelösten Einzel-Tabellen erforderlich. Im Gegensatz dazu steht das Sternschema, bei dem die Dimensionstabellen denormalisiert gestaltet sind. Die Faktentabelle ist nur unmittelbar von den einzelnen Dimensionstabellen abhängig. Es gibt keine Abhängigkeiten, die über mehrere Fremdschlüssel-Beziehungen vollzogen werden müssen. Die Anzahl der Dimensionstabellen ist geringer und für Zugriffe auf die Tabellen sind weniger Joins erforderlich. Allerdings gibt es bei den Daten in den Dimensionstabellen Redundanz. Die Performance der Datenzugriffe ist bei dem Sternschema meistens besser, daher wird in der Praxis meistens dieses Schema gewählt.

Vorweggenommene Aggregation

Zur Ausführung von Abfragen müssen oft umfangreiche Aggregationen ausgeführt werden. Das ist besonders bei OLAP-Systemen der Fall. Wenn die Antwortzeit der Abfragen in nicht mehr akzeptable Bereiche kommt, dann können die Aggregationen auch vorweg berechnet und gespeichert werden. Ideal ist das bei Systemen, die nur in der Nacht aktualisiert werden. Dann werden nach der eigentlichen Aktualisierung der Daten auch alle möglichen Aggregationen berechnet und gespeichert. Wenn dann ein Anwender während des Tages eine Kennzahl (KPI) anfordert, dann sind alle dafür erforderlichen Aggregationen bereits vorhanden und die Kennzahl kann sekundenschnell ausgegeben werden.

Fragmentierung

Man unterscheidet horizontale und vertikale Fragmentierung.

Bei der horizontalen Fragmentierung (englisch sharding) wird die Gesamtheit aller Datensätze einer Relation auf mehrere Tabellen aufgeteilt. Wenn diese Tabellen auf demselben Server liegen, handelt es sich meistens um Partitionierung. Die einzelnen Tabellen können aber auch auf unterschiedlichen Servern liegen. So können z. B. die Daten für die Geschäfte in den USA auf einem Server in den USA gespeichert werden und die Daten für die Geschäfte mit Europa liegen auf einem Server in Deutschland. Diese Aufteilung wird auch als Regionalisierung bezeichnet.

Horizontale Fragmentierung schafft keine Redundanz der gespeicherten Daten, sondern der Strukturen. Wenn eine Relation geändert werden muss, dann muss nicht nur eine Tabelle geändert werden, sondern es müssen alle Tabellen geändert werden, über die die Daten aus der betreffenden Relation verteilt sind. Hier besteht die Gefahr von Anomalien in den Datenstrukturen.

Bei der vertikalen Fragmentierung werden die abhängigen Attribute (Nicht-Schlüssel-Attribute) einer Tabelle in zwei oder mehrere Gruppen aufgeteilt. Aus jeder Gruppe wird eine eigene Tabelle, die noch um alle Schlüssel-Attribute der Ursprungstabelle ergänzt werden. Das kann dann sinnvoll sein, wenn die Attribute einer Relation Datensätze mit einer sehr großen Satzlänge ergeben. Wenn zusätzlich noch die Zugriffe meistens nur einige wenige Attribute betreffen, dann kann man die wenigen häufig zugegriffenen Attribute in eine Gruppe zusammenfassen und den Rest in eine zweite Gruppe zusammenfassen. Die häufig auszuführenden Zugriffe werden dadurch schneller, weil eine geringere Menge an Daten von der Festplatte gelesen werden muss. Die selten auszuführenden Zugriffe auf die restlichen Attribute werden dadurch nicht schneller, aber auch nicht langsamer.

Ab welcher Satzlänge eine Aufspaltung in mehrere kleinere Tabellen sinnvoll ist, hängt auch von dem Datenbanksystem ab. Viele Datenbanksysteme speichern die Daten in Form von Blöcken mit einer Größe von 4 KiB, 8 KiB oder 16 KiB ab. Wenn die durchschnittliche Satzlänge wenig größer als 50 % eines Datenblocks ist, dann bleibt viel Speicherplatz ungenutzt. Wenn die durchschnittliche Satzlänge größer als die verwendete Blockgröße ist, dann werden die Datenzugriffe aufwändiger. Wenn BLOBs zusammen mit anderen Attributen in einer Relation vorkommen, ist vertikale Fragmentierung fast immer von Vorteil.

Partitionierung

Partitionierung ist ein Spezialfall der horizontalen Fragmentierung.

Große Datenbestände lassen sich leichter administrieren, wenn die Daten einer Relation in mehrere kleine Teile (= Partitionen) aufgeteilt und diese separat gespeichert werden. Wenn eine Partition einer Tabelle gerade aktualisiert wird, dann können andere Partitionen der Tabelle zur selben Zeit reorganisiert werden. Wenn in einer Partition ein Fehler entdeckt wird, dann kann diese einzelne Partition aus einer Datensicherung wiederhergestellt werden, während Programme auf die anderen Partitionen weiter zugreifen können. Die meisten etablierten Datenbankhersteller bieten Partitionierung an, siehe z. B. Partitionierung bei DB2 und Partitionierung bei MySQL.

Die meisten Datenbanksysteme bieten die Möglichkeit, entweder einzelne Partitionen anzusprechen oder alle Partitionen unter einem einheitlichen Tabellennamen anzusprechen.

Durch Partitionierung können die Datenzugriffe beschleunigt werden. Der wesentliche Vorteil ist jedoch die leichtere Administrierbarkeit der gesamten Tabelle.

Index

Die Erstellung eines Index ist auch eine redundante Datenspeicherung und damit – genau genommen – eine Denormalisierung. Die meisten Datenbankmanagementsysteme sind in der Lage, einen Index automatisch zu aktualisieren, wenn die Daten in der Basistabelle verändert werden. Indizes können eine Leistungssteigerung bei Lesezugriffen und bei Schreibzugriffen bewirken, da eine gezielte Suche nach bestimmten Sätzen unterstützt wird. Oft werden Schreibzugriffe jedoch umso langsamer, je mehr Indices vorhanden sind, da sowohl die Daten in der Tabelle, als auch die Daten in den Indices aktualisiert werden müssen.

Nachteile

Nachteilig ist oft der zusätzliche Aufwand, der getrieben werden muss, um die redundanten Daten konsistent zu halten. Es besteht die Gefahr von Datenanomalien auf Grund der redundanten Speicherung.

Diese Gefahr kann aufgehoben werden, wenn es gelingt, die Aktualisierung der redundant gespeicherten Daten an das Datenbankmanagementsystem zu delegieren.

Die Datenbankhersteller bieten verschiedene Funktionen, um redundant gespeicherte Daten automatisch abzugleichen.

  • Dass die Aktualisierung eines Index automatisch erfolgt, ist schon so selbstverständlich, dass man es gar nicht anders erwartet.
  • Die Vorwegnahme von Aggregationen wird durch Materialized Views unterstützt, die diese Aggregationen automatisch im erforderlichen Umfang aktualisieren.
  • Ferner gibt es Trigger, mit denen redundant gespeicherte Daten automatisch aktualisiert werden können.

Wenn das Datenbankmanagementsystem solche Aufgaben übernimmt, dann mag die Aktualisierung eines einzelnen Datensatzes dadurch nur unmerklich verlangsamt werden. Massendatenverarbeitungen können durch die Nutzung solcher Funktionen allerdings deutlich langsamer werden.

Meistens hat eine Denormalisierung einen zusätzlichen Speicherbedarf zur Folge. Oft ist man jedoch bereit, für eine Verbesserung der Leistung die Kosten für zusätzlichen Speicherplatz zu tragen. Im Einzelfall muss abgewogen werden, ob die Vorteile es wert sind, die damit verbundenen Nachteile in Kauf zu nehmen.