Denormalisieren in NoSQL oder wie wir lernten, Tabellen zu skalieren
Der Begriff NoSQL wurde erstmals 1998 für eine alternative Abfragesprache von relationalen Datenbanken verwendet [1]. In seiner heutigen Bedeutung von "Not only SQL" als Sammelbegriff für alternative Datenbankkonzepte, kam er erst gegen Ende der Nullerjahre nach einem Meetup in San Francisco in Mode [2]. Seither ist viel darüber geschrieben worden, doch nicht selten kommen dabei wichtige Aspekte nicht zum Ausdruck oder es werden gar falsche Interpretationen in die Welt gesetzt. Die Palette reicht von einer versuchten relationalen Nutzung alternativer Datenbankansätze bis hin zur Verwechslung von spaltenorientierten (relationalen) Datenbanken mit den sogenannten Wide Column Stores aus der NoSQL-Welt. In diesem Artikel möchte ich daher die wichtigsten konzeptuellen Unterschiede von relationalen Datenbanken und ihren NoSQL-Verwandten, vor allem im Bereich der Normalisierung, herausarbeiten und die damit einhergehenden Konsequenzen für Skalierbarkeit, Flexibilität und Abfragemöglichkeiten verdeutlichen.
Wie das folgende Zitat des Turing-Preisträgers Michael Stonebraker [3] nämlich wunderbar beschreibt, hatten die Anfang der 2000er Jahre vermehrt aufgetretenen Performanz-Probleme relationaler Datenbanken im Umgang mit großen Datenmengen (Big Data [4]) nichts mit der Abfragesprache SQL an sich zu tun, sondern stammten in erster Linie von den relationalen Eigenschaften dieser Systeme sowie den dort propagierten ACID-Garantien [5]:
“… blinding performance depends on removing overhead. Such overhead has nothing to do with SQL, but instead revolves around traditional implementations of ACID transactions, multi-threading, and disk management.”
Die SQL (Structured Query Language) ist entsprechend auch in den 2020er Jahren als Abfragesprache weiterhin so populär, dass sich selbst verschiedene NoSQL-Datenbanken (wie bspw. der Wide Column Store Cassandra) mit ihrer Abfragesprache stark an ihr orientieren und gängige Big-Data-Frameworks (wie Hadoop, Spark oder Flink) oder die Streaming-Plattform Kafka Möglichkeiten bieten, um SQL-basiert auf Daten zugreifen zu können [6].
Hintergründe und Grundlagen
Doch springen wir zunächst zurück in die Zeit um die Jahrtausendwende: Diejenigen unter uns, die sich bereits damals mit Personal Computern beschäftigten, kennen sicher noch das Phänomen, das gemeinhin auf Moore’s Law zurückgeführt wird: Man musste alle zwei bis drei Jahre ein neues Gerät erwerben, weil das Nachfolgemodell über mindestens die doppelte Taktrate und den doppelten Speicherplatz verfügte und nur so gewährleistet war, dass auch die neuesten Applikationen (und Spiele) darauf liefen. Während der Speicherplatz in Neugeräten noch heute mit einer ähnlichen Geschwindigkeit wächst, stagnieren die Prozessortaktraten jedoch seit etwa 2005 in einem Bereich um 4 GHz.
Just zu dieser Zeit führten der Boom des Internets sowie die damals aufkommenden Online-Shops und Social-Media-Anwendungen zu einem rasanten Anstieg der Datenmengen, den wir heute gern mit dem Begriff "Big Data" bezeichnen [4]. Entsprechend war damals ein Punkt erreicht, an dem oft alle Daten weder auf einen einzelnen Rechner passten, noch dort mit ausreichender Geschwindigkeit hätten verarbeitet werden können. Eine offensichtliche Lösung für dieses Dilemma sind Multi-Core-Rechner mit mehreren Prozessorkernen, die heute ebenso zum Standard gehören wie Cloud-Systeme, die ihre Daten und Prozessorlast für Entwickler und Benutzer nicht erkennbar auf verschiedene (ggf. virtuelle) Maschinen (oder sog. Container) verteilen und dazu eben oft NoSQL-Systeme verwenden. Doch warum sind NoSQL-Systeme so viel besser skalierbar als "herkömmliche" relationale Datenbanksysteme (RDBMS)?
Beginnen wir die Beantwortung dieser Frage mit einem einfachen, beispielhaften Datensatz, der uns auch im weiteren Verlauf dienlich sein soll, um die Unterschiede zwischen relationalen und nicht-relationalen Datenbanken herauszuarbeiten. Ein wichtiges Merkmal der RDBMS ist ihre große Flexibilität in der Speicherung durch Normalisierung der Datenmodelle und die Nutzung von Fremdschlüsseln zum Verknüpfen von Entitäten (vgl. [5]). Das folgende Beispiel illustriert das anhand von Daten für Hotels und Bewertungen für einen Reiseratgeber à la Tripadvisor:
Im Beispiel sind die Bewertungen (Ratings) jeweils über einen Fremdschlüssel (Spalten Hotel und User) mit den Hotels und den bewertenden Usern verknüpft, um abzubilden, welcher User welches Hotel bewertet hat. Dazu werden relationale Normalisierungsprinzipien umgesetzt, was wiederum eine maximale Flexibilität in der Abfragegestaltung ermöglicht und die bekannten Anomalien (bei Insert, Update und Delete) vermeidet [6].
Solche relationalen Datenbanken, die auf eine vor über 50 Jahren veröffentlichte Idee von "Ted" Codd zurückgehen [7], der dafür ebenfalls mit dem Turing-Preis ausgezeichnet wurde, sind primär für OLTP (Online Transaction Processing), also den individuellen Zugriff auf einzelne Datensätze, ausgelegt. Entsprechend werden einzelne Datensätze (die Tabellenzeilen) zeilenweise hintereinander weg abgespeichert (s. Abb. 2, Mitte), so dass sie zur Bearbeitung schnell en bloc geladen und bei Veränderung an der gleichen Stelle gesichert werden können.
Data Warehouses, die für Datenanalysen (Stichwort: OLAP = Online Analytical Processing) großer, sich nicht andauernd ändernder Datensätze ausgelegt sind, speichern die Daten hingegen meist spaltenorientiert, um auf bestimmte Attribute aller Datensätze schnell zugreifen zu können (s. Abb. 2, unten). So können darauf etwa Aggregationen wie Summen oder Durchschnittswerte performanter berechnet werden.
Für das hypothetische Berechnen der mittleren Sterneanzahl (nicht zu verwechseln mit den Sternen bzw. Punkten aus den Bewertungen) aller Hotels, könnten etwa die rot markierten Daten am Ende der unteren Tabelle in Abb. 2 in einem Rutsch und somit deutlich schneller von der Festplatte geladen werden, als ihre über die Datensätze verstreuten Pendants im mittleren Teil der Abbildung. Das bringt natürlich bei einer rotierenden Magnetplatte (HDD) mit hohen Latenzzeiten durch das Positionieren des Lesekopfs deutlich größere Vorteile, als es bei einem modernen Flashspeicher (SSD) der Fall sein wird. Erstere sind allerdings gerade für sehr große Datenmengen nach wie vor deutlich kostengünstiger zu bekommen.
Ein einfacher Weg, um Datenbanken für eine große Leselast zu wappnen, ist die Verwendung eines Master-Slave-Ansatzes, bei dem alle Daten des Master-Rechners auf den Slaves repliziert werden. Der Vorteil für Leseabfragen ist, dass diese über ein Load-Balancing verteilt abgearbeitet werden können und somit eine höhere Leselast ermöglicht wird. Das fördert die Performanz beim Schreiben jedoch nicht, sondern beeinflusst sie negativ, da sich der Master mit seinen Slaves abstimmen und Schreibkommandos von ihnen bestätigen lassen muss. Kurzum, diese Idee lohnt sich vor allem dann, wenn in einer Datenbank viel gelesen und eher wenig geschrieben wird, für Datenbanken mit einer hohen Schreiblast ist sie nicht zu empfehlen.
Relationale Datenbanken unterstützen diesen Ansatz zwar oft, tun sich jedoch nach wie vor schwer, was die echte Verteilbarkeit auf verschiedene Rechner betrifft. Beispielsweise können die RAC-Cluster der Firma Oracle zwar mehrere Server zur Anfragebearbeitung verwenden, diese legen ihre Daten jedoch auf einem darunterliegenden, verteilten Dateisystem ab. So müssen Abfragen zwar nicht auf Datenbankebene über Rechnergrenzen hinweg koordiniert erfolgen, die Systemkomplexität bleibt aber nichtsdestotrotz hoch, so dass Aufsetzen und Betreiben eines solchen Clusters – gerade im Vergleich zu modernen NoSQL-Lösungen – als sehr komplex einzustufen sind. Interessanterweise nutzen auch frühe NoSQL-Systeme wie Googles BigTable oder HBase aus dem Hadoop-Ökosystem genau dieses Vorgehen, um die Komplexität in der Datenbank über ein verteiltes Dateisystem (Google File System bzw. Hadoop File System) zu verringern. Leider verringert das auch die Performanz und zumindest das Aufsetzen eines Hadoop-Clusters ist ebenfalls alles andere als trivial.
NoSQL
Modernere, auf Durchsatz ausgelegte NoSQL-Datenbanken hingegen wurden von Anfang an konsequent für die Verteilbarkeit auf nicht spezialisierter Commodity-Hardware, mit gegebenenfalls "wild zusammengewürfelten" Clustern aus verschiedenen Rechnertypen entwickelt. Sie finden sich meist mit einigen wenigen Konfigurationsparametern, wie der Angabe von Seed-IP-Adressen, selbständig zu einem Cluster zusammen. Lese- und Schreibanfragen können üblicherweise an jedes beliebige Gerät im Cluster gestellt und dort automatisch koordiniert werden. Zwar gibt es auch hier Systeme, bei denen die Installation einfacher funktioniert (bspw. Cassandra) und andere, die komplexer zu konfigurieren sind (bspw. MongoDB), aber grundsätzlich sind solche Systeme deutlich leichter und performanter in Clustern zu betreiben als ihre relationalen und nicht-relationalen Vorgänger.
Wie bereits angedeutet, ist NoSQL ein breites Feld mit einer Reihe von verschiedenen Datenbanktypen. Allen gemeinsam ist die in modernen Systemen von Anfang an eingeplante Verteilbarkeit und redundante Speicherung von Daten. Weitere Details dazu sind allerdings eine andere Geschichte, für die wir jedoch an dieser Stelle in zu viele weitere Themen wie ACIDvs. BASE oder auch das CAP-Theorem eintauchen müssten [8]. In der Literatur (bspw. in [9]) werden meist die in der folgenden Tabelle gezeigten NoSQL-Subtypen unterschieden.
Tabelle 1: Übersicht über verschiedene NoSQL-Unterarten
Datenmodell | Skalierbarkeit | Latenz | Schema | (Betriebs-)Komplexität |
Key-Value Stores | Sehr hoch | Sehr gering | Applikationsseitig umzusetzen | Gering |
Document Stores | Hoch | Gering | Meist schemalos | Mittel bis hoch |
Wide-Column Stores | Sehr hoch | Gering (ältere Systeme: hoch) | Meist vorgeben | Gering bis mittel |
Graph-Datenbanken | Eher begrenzt | Gering bis mittel | Key-Value-Paare an Knoten und Kanten | Mittel bis hoch |
Im weiteren Verlauf des Artikels möchte ich meine Erläuterungen primär auf die Wide Column Stores fokussieren, da diese ähnlich zu den Tabellen klassischer RDBMS aufgebaut sind und daher entscheidende Unterschiede gerne übersehen werden. Document Stores, die ihre Daten meist über JSON-Dokumente speichern, sind zwar im Aufbau noch etwas flexibler, die prinzipiellen Gedanken zur Denormalisierung, die ich im Folgenden beschreibe, gelten aber auch dort.
Verteilte Tabellen
Wie gut lassen sich relational organisierte Daten mit einer Notwendigkeit für Joins und ACID-Garantien theoretisch überhaupt verteilen? Ein Join über eine Netzwerkstrecke zwischen mehreren Servern ist zwar ebenso vorstellbar wie verteilte ACID-Transaktionen mit zahlreichen notwendigen Lock-Vorgängen und Schreibbestätigungen, in der Praxis jedoch werden sich alle Hoffnungen auf "blinding performance" damit schnell zerschlagen. Das liegt insbesondere daran, dass sich Tabellen am besten über ein Modulo auf dem Primärschlüssel (oder einem Hash-Wert davon) auf verschiedene Clusterknoten (oft Partitionen genannt) verteilen lassen. Zugriffe auf einzelne Werte sind bei einem solchen Verfahren sehr gut möglich, Bereichsabfragen auf dem Primärschlüssel oder Abfragen auf anderen Attributen erzeugen allerdings einen erhöhten Suchaufwand, da diese auf allen Servern im Cluster ausgeführt werden müssen. Hier können immerhin clevere probabilistische Datenstrukturen helfen, um unnötige Suchaufwände in aussichtslosen Fällen zu vermeiden [10].
Da Daten bei Joins in relationalen Datenbanken eben gerade nicht nach dem Primärschlüssel, sondern nach dem Fremdschlüssel von mehreren Servern zusammengesucht werden müssen, wären Joins in verteilten Datenbanken nur mit deutlichen Einschränkungen der Performanz vorstellbar. Abb. 3 verdeutlicht das am Beispiel unseres Reiseratgebers.
In einem Cluster mit vier Maschinen (daher modulo 4) würden die Daten der User aheintz und emiller auf Server 1 und die von csmith auf Server 0 gespeichert werden. Die beiden von csmith gemachten Bewertungen 18 und 19 lägen auf den Servern 2 und 3, so dass für einen entsprechenden Join die Datensätze zunächst auf Server 0 übertragen und bei großen Datenmengen in der Praxis ggf. noch auf einer Festplatte zwischengespeichert werden müssten. Entsprechend ermöglichen die allermeisten NoSQL-Systeme keine Joins, womit im Umkehrschluss auch die sonst übliche Normalisierung von Datenmodellen nicht sinnvoll ist.
Denormalisierung
Auf Grund der immensen Bedeutung für das weitere Verständnis möchte ich die vorige Aussage an dieser Stelle noch einmal wiederholen: Verteilte NoSQL-Datenbanken, bzw. genauer Wide Column und auch Document Stores, erfordern eine Denormalisierung von Datenmodellen ausgehend von den benötigten Abfragen. Entsprechend verlieren sie im Vergleich zu normalisierten relationalen Datenmodellen an Flexibilität, sind dafür aber in der Datenspeicherung und bei den entsprechenden Abfragen deutlich performanter und eben auch skalierbarer.
Was das genau bedeutet, möchte ich im Folgenden am Beispiel von Wide Colum Stores wie Cassandra oder HBase herausarbeiten, da um deren Speichermodell auch heute oft noch eine nicht zu unterschätzende Verwirrung herrscht. Nicht zuletzt deshalb, weil sie gerne mit der zuvor erläuterten spaltenorientierten (engl. column-oriented) Speicherung von Daten in relationalen Systemen in einen Topf geworfen werden. Zwar nutzen auch die Wide Column Stores ein tabellenförmiges Format, darüber hinaus haben sie aber einige sehr spezielle Eigenarten, um eine bessere Performanz und Skalierbarkeit für den Umgang mit großen Datenmengen zu erreichen.
Ein Weg, um auf eine für einen Wide Column Store (oder analog auch einen Document Store) sinnvolle Tabellenstruktur zu kommen, ist, diese bewusst von der Abfrageseite her zu denken. Das bedeutet, dass wir uns zunächst klar machen müssen, in welchem Kontext wir welche Daten benötigen, um diese gemeinsam abspeichern zu können. Unser Ausgangspunkt soll daher eine Abfrage sein, die uns alle nötigen Daten für die Übersichtsseite eines Hotels unseres gedachten Reiseratgebers liefert. Diese könnte auch in der Praxis so ähnlich aussehen, wie in Abb. 4 illustriert.
Aus dieser Darstellung können wir die benötigten Informationen für eine passende Abfrage folgendermaßen ableiten: Name und Ort eines Hotels liegen natürlich klar auf der Hand (sowie eine im Beispiel bisher ausgelassene Hotel-Beschreibung). Notwendig ist ferner der Durchschnittswert aller Bewertungen für dieses Hotel, deren Anzahl sowie nicht zuletzt die Darstellung der zwei aktuellsten Bewertungen mit ihrer Bewertungszahl, dem Autor sowie dem abgegebenen Kommentar.
Diese Informationen müssten in einer relationalen Datenbank mindestens aus den Tabellen Hotels und Ratings zusammengetragen werden. In einem Wide Column Store hingegen speichern wir alle benötigten Daten denormalisiert in einer einzigen Tabelle, so dass diese für jedes Hotel gemeinsam auf einem Server abgelegt werden können. Abb. 5 gibt eine ungefähre Vorstellung davon, wie das aussehen könnte.
Alle für das in Abb. 4 dargestellte Szenario benötigten Daten können somit zusammenhängend mit einem einzigen Zugriff auf die in Abb. 5 gezeigte Row abgerufen werden. In einem Document Store funktioniert das analog durch das Einbetten von "Sub-Dokumenten" für die Bewertungen im Dokument jedes Hotels. Da es sich im gezeigten Beispiel um eine Komposition von Daten handelt, ist praktischerweise auch das Auftreten von Anomalien nicht zu befürchten. Schlimmstenfalls würde das Löschen eines User-Accounts dazu führen, dass diese in Abb. 4 gezeigte Verlinkung nicht mehr funktionieren würde.
Andere Szenarien, wie die Abfrage aller Bewertungen eines Users, sind allein mit der Tabellenstruktur von Abb. 5 offensichtlich nicht performant zu machen. Die sinnvollste Lösung für eine solcherart gelagerte Anforderung wäre es, die Daten ein weiteres Mal in einer zur Abfrage passenden Anordnung zu speichern und sie entsprechend mehrfach vorzuhalten. Cassandra kennt dazu übrigens das Konzept der "Materialized View". Mit einer solchen können in einem Format abzulegende Daten beim Speichern datenbankseitig in einem weiteren Format (also in einer weiteren Tabelle) gespeichert und damit für weitere performante Abfragen verfügbar gemacht werden. Aber Achtung, eine ohnehin schon große Datenmenge wird damit noch einmal umfangreicher gemacht! Wie so oft in der Informatik gilt es schlicht unter der Prämisse "there’s no free lunch" einen vertretbaren Trade-off zu finden.
Betrachten wir uns abschließend die Mechanik der Tabelle in Abb. 5 noch etwas genauer: Wide Column Stores verwenden üblicherweise zusammengesetzte Primärschlüssel (wieder kursiv gesetzt), in der Abbildung besteht dieser aus dem Ort und dem Namen sowie zusätzlich noch aus dem Datum der Bewertung. Cassandra nennt den ersten Teil "Partition Key", mit dessen Hash-Wert die Zielpartition (also der Server) zum Speichern zugeordnet wird. Der zweite Teil wird "Clustering Key" genannt und legt die Sortierreihenfolge der Columns fest. Im Beispiel wäre diese also nach dem Datum (in der Praxis natürlich besser nach einem Timestamp) absteigend definiert.
Übrigens spielt auch die Reihenfolge der Elemente des Partition Key bei Abfragen von Wide Column Stores eine entscheidende Rolle: Suchen nach Hotels können mit dem Schema aus Abb. 5 nur dann performant ausgeführt werden, wenn zuerst (oder nur) nach dem Ort und dann nach dem Namen gesucht wird, weil die Daten dafür optimiert angeordnet gespeichert werden. Eine Suche nach allen 5-Sterne-Hotels würde in der obigen Tabelle entsprechend nur sehr langsam laufen und obgleich Cassandra auch eine Indexierung unterstützt, lautet die Empfehlung für eine optimale Performanz dort sehr klar, die benötigten Daten in einer weiteren Tabelle redundant abzulegen.
Weitere Überlegungen
Nach den zuvor genannten Punkten mögen sich aufmerksame Leserinnen und Leser fragen, warum es einige Systeme, wie etwa SAPs HANA-Datenbank schaffen, die genannten "Naturgesetze" scheinbar zu überwinden und performante relationale Speicherzugriffe in einem verteilten System anzubieten [11]?
In-Memory
Eine vielleicht etwas vorschnelle Antwort dazu könnte lauten: Durch das Halten der Daten im Arbeitsspeicher. Natürlich macht "in memory" Datenhaltung Systeme deutlich schneller – und viele moderne Datenbanksysteme nutzen es entsprechend für geschickte Caching-Strategien – letztlich kommen jedoch auch im Arbeitsspeicher laufende Systeme bei umfangreichen Datenmengen nicht um eine Verteilung herum. Auf Grund der im Vergleich zum Festplattenplatz meist geringeren Größe des Arbeitsspeichers stoßen sie sogar viel eher an diese Grenze. So gesehen ist In-Memory also kein eigenes Paradigma, wie es gelegentlich in der Literatur dargestellt wird, sondern schlicht eine auf jedes Paradigma anwendbare Caching-Strategie.
Kehren wir noch einmal zur SAP-HANA zurück. Wie schafft es diese, Joins über verschiedene Rechner performant anzubieten? Mit einem ganz einfachen Trick, sie hat nämlich die Möglichkeit, zusammenhängende Tabellen per Konfigurationsoption auf der gleichen Maschine zu halten, damit sich die Frage nach einer Verteilung erst gar nicht stellt. HANA verfügt zudem als sogenannte Multi-Model-Datenbank, die verschiedene Paradigmen in sich vereint, über viele weitere Aspekte, die es wert wären, sich genauer damit auseinanderzusetzen. Das würde allerdings den Rahmen dieses Artikels bei Weitem sprengen.
Schemalosigkeit
Die sogenannte Schemalosigkeit ist ein weiterer Aspekt, der einige NoSQL-Datenbanken in speziellen Situationen flexibler und auch performanter machen kann. Während bei relationalen Datenbanken das Schema, also die Tabellenstruktur und damit auch die Speicherstruktur auf der Festplatte, vorab festgelegt sein und beispielsweise beim Hinzufügen einer neuen Spalte explizit geändert werden muss, können manche NoSQL-Datenbanken, wie etwa Dokumentdatenbanken neue Attribute (Spalten) direkt beim Speichern von Daten hinzufügen. Cassandra unterstützt das übrigens nicht, sondern arbeitet mit einem festgelegten Schema.
Was zunächst recht trivial klingen mag, war gerade in der Anfangszeit von Big Data ein sehr hilfreiches Feature. Angenommen, in einer über lange Zeit gewachsenen Datenbank muss die Möglichkeit aufgenommen werden, statt einer nun mehrere E-Mail-Adressen zu Kundeneinträgen zu speichern. Es liegt jedoch zunächst nur von wenigen Kunden mehr als eine Mail-Adresse vor. In einem klassischen relationalen Speicherraster führt diese Situation zunächst zu einer Schemaänderung sowie zu vielen leeren Einträgen, die unnötig Speicherplatz verbrauchen und auch Datenzugriffe langsamer machen. In einer schemalosen Datenbank werden jeweils nur vorhandene Attribute bei einem Datensatz gespeichert, so dass keinerlei Speicher verschwendet wird. Auch das Schema eines Wide Column Stores muss in diesem Fall natürlich angepasst werden, hier würde durch eine Nutzung von Wide Columns aber immerhin kein Speicherplatz verschwendet [12].
Zusammenfassung
Auf Grund der Komplexität des Themas NoSQL habe ich in diesem Artikel nur einige spezielle Aspekte, die besonders bei Wide Column und Document Stores zum Tragen kommen, herausgegriffen. Andere Paradigmen wie Key-Value-, Zeitreihen- oder Graphdatenbanken konnte ich nicht näher in Augenschein nehmen. Alleine die hier angedeutete Vielfalt von NoSQL-Systemen verdeutlicht jedoch nochmals einen entscheidenden Punkt: Es gibt bei NoSQL nicht mehr das eine (relationale) Datenbank-Paradigma für alle Fälle, wie das früher mit dem Quasi-Monopol von RDBMS der Fall war. Vielmehr sind die Herausforderungen großer Datenmengen häufig so umfassend, dass Daten mehrfach (Stichwort "Materialized Views") oder gar mehrfach in verschiedenen Systemen (Stichwort: "Polyglot Persistence") abgespeichert werden müssen, um ausreichende Perfomanz für alle benötigten Anwendungsszenarien zu erreichen. Das treibt Finanzverantwortlichen wegen hoher Hardwarekosten zwar oft die Sorgenfalten auf die Stirn, ist aber bei anspruchsvollen Aufgaben an der Grenze der verfügbaren Technologie oft der einzige Weg, überhaupt eine praxistaugliche Lösung zum Laufen zu bekommen.
Entsprechend lohnt es sich schon beim Erstellen von Datenmodellen die Anforderungen beim Speichern und vor allem beim Auslesen großer Datenmengen genau im Auge zu behalten, um sie mit dem passenden Paradigma optimal umsetzen zu können. Es liegt auf der Hand, dass das oft nicht ohne das Eingehen von Kompromissen funktionieren wird. Ein spannendes Beispiel dafür habe ich in diesem Artikel ausgeführt und dabei erläutert, wie und warum verteilte Wide Column bzw. auch Document Stores zur Vermeidung aufwändiger Join-Operationen Daten lieber denormalisieren und gegebenenfalls sogar duplizieren, um durchgängig eine "blinding performance" zu erreichen.
- C. Strozzi: NoSQL: A relational database management system. In Lainattu, Vol. 5, 1998.
- J. Oskarsson: NOSQL Meetup, 2009
- M. Stonebraker: The 'No SQL' Discussion Has Nothing to Do With SQL. In Communications of the ACM Blog
- O. Hummel: Big Data & Big-Data-Analytics. In Michael Lang (Hrsg.): Datenkompetenz – Die entscheidenden Fähigkeiten für die erfolgreiche Nutzung von Daten, Hanser, 2023.
- A. Kemper & A. Eickler: Datenbanksysteme. Eine Einführung (10. Auflage). Oldenbourg Verlag, 2015.
- Cassandra, Hadoop, Spark oder Flink
- E. F. Codd: A Relational Model of Data for Large Shared Data Banks. In Communications of the ACM. ACM Press, 1970.
- A. Kemper & A. Eickler: Datenbanksysteme. Eine Einführung (10. Auflage). Oldenbourg Verlag, 2015.
BASE: D. Pritchett: BASE: An ACID Alternative: In partitioned databases, trading some consistency for availability can lead to dramatic improvements in scalability. In ACM Queue, Vol. 6, Iss. 3, 2008.
Cap-Theorem: E. Brewer: Towards robust distributed systems. In ACM Symposium on Principles of Distributed Computing, 2000. - F. Gessert, W. Wingerath, S. Friedrich, N. Ritter: NoSQL database systems: a survey and decision guidance. In Computer Science Research & Development Vol. 32, 2017.
- O. Hummel: Auch mal Fünfe gerade sein lassen - Wie probabilistische Datenstrukturen daten-intensive Systeme besser skalierbar machen, Informatik Aktuell, 2023.
- SAP: Was ist SAP HANA?
- J. Page: Debunking the Myth of Going Schemaless, 2022