6.1 Kampf der Abkürzungen: BASE vs. SÄURE

„In der Chemie misst der pH-Wert den relativen Säuregehalt einer wässrigen Lösung. Die pH-Skala reicht von 0 (stark saure Substanzen) bis 14 (stark alkalische Substanzen); reines Wasser bei 25 °C hat einen pH-Wert von 7 und ist neutral.

Dateningenieure haben diese Metapher genutzt, um Datenbanken hinsichtlich der Zuverlässigkeit von Transaktionen zu vergleichen.“

Wahrscheinlich war die Idee folgende: Je höher der pH-Wert, d. h. Je näher die Datenbank an „alkalisch“ („BASE“) liegt, desto weniger zuverlässig sind die Transaktionen.

Beliebte relationale Datenbanken wie MySQL entstanden gerade auf der Basis von ACID. Doch in den letzten zehn Jahren kamen die sogenannten NoSQL-Datenbanken, die unter diesem Namen mehrere sehr unterschiedliche Datenbanktypen vereinen, ganz gut ohne ACID aus. Tatsächlich gibt es eine große Anzahl von Entwicklern, die mit NoSQL-Datenbanken arbeiten und sich überhaupt nicht um Transaktionen und deren Zuverlässigkeit kümmern. Mal sehen, ob sie Recht haben.

Über die NoSQL-Datenbank kann man nicht allgemein sprechen, da es sich lediglich um eine gute Abstraktion handelt. NoSQL-Datenbanken unterscheiden sich voneinander im Design der Datenspeicher-Subsysteme und sogar in den Datenmodellen: NoSQL ist sowohl dokumentenorientiertes CouchDB als auch Graph Neo4J. Aber wenn wir im Kontext von Transaktionen über sie sprechen, ähneln sie sich in einem Punkt: Sie bieten begrenzte Versionen von Atomizität und Isolation und bieten daher keine ACID-Garantien. Um zu verstehen, was das bedeutet, beantworten wir die Frage: Was bieten sie, wenn nicht ACID? Nichts?

Nicht wirklich. Schließlich müssen sie sich ebenso wie relationale Datenbanken in einem schönen Paket verkaufen. Und sie haben ihre eigene „chemische“ Abkürzung erfunden – BASE.

6.2 BASIS als Antagonist

Und auch hier werde ich nicht in der Reihenfolge der Buchstaben vorgehen, sondern mit dem Grundbegriff beginnen – der Konsistenz. Ich muss Ihren Wiedererkennungseffekt nivellieren, da diese Konsistenz wenig mit der Konsistenz von ACID zu tun hat. Das Problem mit dem Begriff Konsistenz besteht darin, dass er in zu vielen Zusammenhängen verwendet wird. Diese Konsistenz hat jedoch einen viel breiteren Anwendungskontext, und tatsächlich ist dies genau die Konsistenz, die bei der Diskussion verteilter Systeme diskutiert wird.

Die oben erwähnten relationalen Datenbanken bieten unterschiedliche Ebenen der Transaktionsisolation, und die strengste davon stellt sicher, dass eine Transaktion keine ungültigen Änderungen erkennen kann, die von einer anderen Transaktion vorgenommen wurden. Wenn Sie in einem Geschäft an der Kasse stehen und in diesem Moment das Geld für die Miete von Ihrem Konto abgebucht wird, die Transaktion mit der Überweisung des Geldes für die Miete jedoch fehlschlägt und Ihr Konto wieder auf den vorherigen Wert (das Geld ist nicht abgebucht), dann wird Ihre Zahlungstransaktion an der Kasse nicht jedem diese Gesten auffallen – schließlich wurde diese Transaktion nie durchgeführt, und aufgrund der Anforderung der Transaktionsisolation können ihre vorübergehenden Änderungen von anderen Transaktionen nicht bemerkt werden.

Viele NoSQL-Datenbanken verzichten auf die Isolationsgarantie und bieten eine „eventuelle Konsistenz“, bei der Sie irgendwann zwar gültige Daten sehen, aber die Möglichkeit besteht, dass Ihre Transaktion ungültige Werte liest – also temporär, teilweise aktualisiert oder veraltet. Es ist möglich, dass die Daten im „Lazy“-Modus beim Lesen („lazily at read time“) konsistent werden.

NoSQL wurde als Datenbank für Echtzeitanalysen konzipiert und um eine höhere Geschwindigkeit zu erreichen, wurde auf Konsistenz verzichtet. Und Eric Brewer, derselbe, der den Begriff BASE geprägt hat, formulierte das sogenannte „CAP-Theorem“, wonach:

Für jede Implementierung des verteilten Rechnens ist es möglich, nicht mehr als zwei der folgenden drei Eigenschaften bereitzustellen:

  • Datenkonsistenz ( Konsistenz ) – Daten auf verschiedenen Knoten (Instanzen) widersprechen sich nicht;
  • Verfügbarkeit ( Verfügbarkeit ) – jede Anfrage an ein verteiltes System endet mit einer korrekten Antwort, jedoch ohne Garantie, dass die Antworten aller Systemknoten gleich sind;
  • Partitionstoleranz (Partitionstoleranz ) – Auch wenn zwischen den Knoten keine Verbindung besteht, arbeiten sie unabhängig voneinander weiter.

Wenn Sie eine sehr einfache Erklärung von CAP wünschen, dann sind Sie hier genau richtig.

Es gibt Meinungen, dass das CAP-Theorem nicht funktioniert und im Allgemeinen zu abstrakt formuliert ist. Auf die eine oder andere Weise verweigern NoSQL-Datenbanken häufig die Konsistenz im Kontext des CAP-Theorems, das folgende Situation beschreibt: Daten wurden in einem Cluster mit mehreren Instanzen aktualisiert, die Änderungen wurden jedoch noch nicht auf allen Instanzen synchronisiert. Denken Sie daran, ich habe oben das DynamoDB-Beispiel erwähnt, das mir sagte: Ihre Änderungen wurden dauerhaft – hier ist ein HTTP 200 für Sie – aber ich sah die Änderungen erst nach 10 Sekunden? Ein weiteres Beispiel aus dem Alltag eines Entwicklers ist DNS, das Domain Name System. Falls es jemand nicht weiß, dann ist dies genau das „Wörterbuch“, das http(s)-Adressen in IP-Adressen übersetzt.

Der aktualisierte DNS-Eintrag wird gemäß den Einstellungen für das Caching-Intervall an die Server weitergegeben, sodass Aktualisierungen nicht sofort erkennbar sind. Nun, eine ähnliche zeitliche Inkonsistenz (d. h. eventuelle Konsistenz) kann bei einem relationalen Datenbankcluster (z. B. MySQL) auftreten – schließlich hat diese Konsistenz nichts mit der Konsistenz von ACID zu tun. Daher ist es wichtig zu verstehen, dass sich SQL- und NoSQL-Datenbanken in diesem Sinne wahrscheinlich nicht sehr unterscheiden, wenn es um mehrere Instanzen in einem Cluster geht.

Darüber hinaus kann die End-to-End-Konsistenz dazu führen, dass Schreibanforderungen nicht in der richtigen Reihenfolge erfolgen: Das heißt, alle Daten werden geschrieben, aber der Wert, der letztendlich empfangen wird, ist nicht der letzte in der Schreibwarteschlange.

Nicht-ACID-NoSQL-Datenbanken verfügen aufgrund des End-to-End-Konsistenzmodells über einen sogenannten „Soft State“, was bedeutet, dass sich der Zustand des Systems im Laufe der Zeit auch ohne Eingabe ändern kann. Solche Systeme streben jedoch danach, eine bessere Zugänglichkeit zu ermöglichen. Die Bereitstellung einer 100-prozentigen Verfügbarkeit ist keine triviale Aufgabe, daher sprechen wir von einer „Grundverfügbarkeit“. Und zusammen bilden diese drei Konzepte: „grundsätzlich verfügbar“, „weicher Zustand“ („weicher Zustand“) und „eventuelle Konsistenz“ das Akronym BASE.

Ehrlich gesagt scheint mir das Konzept von BASE eine leerere Marketinghülle zu sein als ACID – weil es nichts Neues bringt und die Datenbank in keiner Weise charakterisiert. Und das Anbringen von Labels (ACID, BASE, CAP) an bestimmten Datenbanken kann Entwickler nur verwirren. Ich habe beschlossen, Ihnen diesen Begriff trotzdem vorzustellen, da es beim Studium der Datenbank schwierig ist, ihn zu umgehen, aber jetzt, da Sie wissen, was er ist, möchte ich, dass Sie ihn so schnell wie möglich vergessen. Und zurück zum Konzept der Isolation.

6.3 Die BASE-Datenbanken erfüllen also überhaupt nicht die ACID-Kriterien?

Der wesentliche Unterschied zwischen ACID-Datenbanken und Nicht-ACID-Datenbanken besteht darin, dass Nicht-ACIDs tatsächlich auf Isolation verzichten. Das ist wichtig zu verstehen. Aber es ist noch wichtiger, die Datenbankdokumentation zu lesen und sie zu testen, so wie es die Leute vom Hermitage-Projekt tun. Es ist nicht so wichtig, wie genau die Ersteller dieser oder jener Datenbank ihre Idee nennen – ACID oder BASE, CAP oder nicht CAP. Wichtig ist, was genau diese oder jene Datenbank bereitstellt.

Wenn die Ersteller der Datenbank behaupten, dass sie ACID-Garantien bietet, dann hat das wahrscheinlich einen Grund, aber es ist ratsam, es selbst zu testen, um herauszufinden, ob und in welchem ​​Umfang dies der Fall ist. Wenn sie erklären, dass ihre Datenbank solche Garantien nicht bietet, kann dies Folgendes bedeuten:

  • Die DB bietet keine Garantie für die Atomizität. Während einige NoSQL-Datenbanken eine separate API für atomare Operationen bieten (z. B. DynamoDB);

  • Die DB bietet keine Isolationsgarantie. Dies kann beispielsweise bedeuten, dass die Datenbank die Daten nicht in der Reihenfolge schreibt, in der sie geschrieben wurden.

Was die Haltbarkeitsgarantie angeht, gehen viele Datenbanken aus Leistungsgründen in diesem Punkt Kompromisse ein. Das Schreiben auf die Festplatte ist ein zu langer Vorgang und es gibt mehrere Möglichkeiten, dieses Problem zu lösen. Ich möchte nicht viel auf die Datenbanktheorie eingehen, aber damit Sie ungefähr verstehen, wie Sie aussehen müssen, werde ich allgemein beschreiben, wie verschiedene Datenbanken das Problem der Haltbarkeit lösen.

Um verschiedene Datenbanken vergleichen zu können, müssen Sie unter anderem wissen, welche Datenstrukturen dem Datenspeicher- und -abruf-Subsystem einer bestimmten Datenbank zugrunde liegen. Kurz gesagt: Verschiedene Datenbanken verfügen über unterschiedliche Implementierungen der Indizierung – also der Organisation des Zugriffs auf Daten. Mit einigen von ihnen können Sie Daten schneller schreiben, mit anderen können Sie sie schneller lesen. Man kann jedoch nicht pauschal sagen, dass bestimmte Datenstrukturen die Haltbarkeit erhöhen oder verringern.

6.4 wie unterschiedliche Datenbanken Daten indizieren und wie sich dies auf die Haltbarkeit auswirkt und mehr

Es gibt zwei Hauptansätze zum Speichern und Abrufen von Daten.

Der einfachste Weg, Daten zu speichern, besteht darin, Vorgänge protokollartig am Ende der Datei hinzuzufügen (d. h. es findet immer ein Anhängevorgang statt): Es spielt keine Rolle, ob wir Daten hinzufügen, ändern oder löschen möchten – alles CRUD-Operationen werden einfach in das Protokoll geschrieben. Das Durchsuchen des Protokolls ist ineffizient, und hier kommt der Index ins Spiel – eine spezielle Datenstruktur, die Metadaten darüber speichert, wo genau die Daten gespeichert sind. Die einfachste Indexierungsstrategie für Protokolle ist eine Hash-Map, die Schlüssel und Werte verfolgt. Die Werte sind Verweise auf den Byte-Offset für die in die Datei geschriebenen Daten, die das Protokoll (log) darstellt und auf der Festplatte gespeichert wird. Diese Datenstruktur wird vollständig im Speicher gespeichert, während sich die Daten selbst auf der Festplatte befinden, und wird als LSM-Baum (Log Structured Merge) bezeichnet.

Sie haben sich wahrscheinlich gefragt: Wenn wir unsere Operationen ständig in das Journal schreiben, wird es dann exorbitant wachsen? Ja, und deshalb wurde die Komprimierungstechnik erfunden, die die Daten mit einer gewissen Periodizität „bereinigt“, nämlich nur den relevantesten Wert für jeden Schlüssel übrig lässt oder ihn löscht. Und wenn wir mehr als ein Protokoll auf der Festplatte haben, sondern mehrere, und alle sind sortiert, dann erhalten wir eine neue Datenstruktur namens SSTable („sortierte Zeichenfolgentabelle“), und dies wird zweifellos unsere Leistung verbessern. Wenn wir im Speicher sortieren wollen, erhalten wir eine ähnliche Struktur – die sogenannte MemTable, aber damit besteht das Problem, dass bei einem schwerwiegenden Datenbankabsturz die zuletzt geschriebenen Daten (die sich in MemTable befinden, aber noch nicht geschrieben wurden) gelöscht werden Festplatte) gehen verloren. Eigentlich,

Ein weiterer Indexierungsansatz basiert auf B-Bäumen („B-Bäumen“). In einem B-Baum werden Daten in Seiten fester Größe auf die Festplatte geschrieben. Diese Datenblöcke sind oft etwa 4 KB groß und verfügen über nach Schlüssel sortierte Schlüssel-Wert-Paare. Ein B-Tree-Knoten ist wie ein Array mit Links zu einer Reihe von Seiten. Max. Die Anzahl der Links in einem Array wird als Verzweigungsfaktor bezeichnet. Jeder Seitenbereich ist ein weiterer B-Tree-Knoten mit Links zu anderen Seitenbereichen.

Auf Blattebene finden Sie schließlich einzelne Seiten. Diese Idee ähnelt Zeigern in Low-Level-Programmiersprachen, mit der Ausnahme, dass diese Seitenverweise auf der Festplatte und nicht im Speicher gespeichert werden. Wenn INSERTs und DELETEs in der Datenbank auftreten, kann sich ein Knoten entsprechend dem Verzweigungsfaktor in zwei Teilbäume aufteilen. Wenn die Datenbank aus irgendeinem Grund mitten im Prozess ausfällt, kann die Integrität der Daten gefährdet sein. Um dies zu verhindern, führen Datenbanken, die B-Bäume verwenden, ein „Write-Ahead-Protokoll“ oder WAL, in dem jede einzelne Transaktion aufgezeichnet wird. Diese WAL wird verwendet, um den Zustand des B-Baums wiederherzustellen, wenn dieser beschädigt ist. Und es scheint, dass dies der Grund dafür ist, dass Datenbanken, die B-Bäume verwenden, hinsichtlich der Haltbarkeit besser sind. LSM-basierte Datenbanken können jedoch auch eine Datei verwalten, die im Wesentlichen dieselbe Funktion wie WAL ausführt. Deshalb wiederhole ich, was ich bereits gesagt habe, und vielleicht mehr als einmal: Verstehen Sie die Funktionsweise der von Ihnen gewählten Datenbank.

Sicher ist jedoch, dass B-Bäume gut für die Transaktionalität sind: Jeder Schlüssel kommt nur an einer Stelle im Index vor, während Journaled-Storage-Subsysteme mehrere Kopien desselben Schlüssels in verschiedenen Shards haben können (z. B. bis zum nächste Verdichtung wird durchgeführt).

Das Design des Index wirkt sich jedoch direkt auf die Leistung der Datenbank aus. Bei einem LSM-Baum erfolgen Schreibvorgänge auf die Festplatte sequentiell und B-Bäume verursachen mehrere zufällige Festplattenzugriffe, sodass Schreibvorgänge mit LSM schneller sind als mit B-Bäumen. Der Unterschied ist besonders deutlich bei magnetischen Festplatten (HDDs), wo sequentielle Schreibvorgänge viel schneller sind als zufällige Schreibvorgänge. Das Lesen ist bei LSM-Bäumen langsamer, da Sie mehrere verschiedene Datenstrukturen und SS-Tabellen durchsuchen müssen, die sich in unterschiedlichen Komprimierungsstadien befinden. Im Detail sieht es so aus. Wenn wir mit LSM eine einfache Datenbankabfrage durchführen, suchen wir zunächst den Schlüssel in der MemTable. Wenn es nicht vorhanden ist, schauen wir uns die aktuellste SSTable an; Wenn nicht, schauen wir uns die vorletzte SSTable an und so weiter. Wenn der angeforderte Schlüssel nicht existiert, erfahren wir dies mit LSM zuletzt. LSM-Bäume werden beispielsweise verwendet in: LevelDB, RocksDB, Cassandra und HBase.

Ich beschreibe alles so ausführlich, damit Sie verstehen, dass Sie bei der Auswahl einer Datenbank viele verschiedene Dinge berücksichtigen müssen: Erwarten Sie beispielsweise, mehr Daten zu schreiben oder zu lesen? Und ich habe den Unterschied in den Datenmodellen noch nicht erwähnt (Müssen Sie die Daten durchlaufen, wie es das Diagrammmodell zulässt? Gibt es überhaupt Beziehungen zwischen verschiedenen Einheiten in Ihren Daten – dann werden relationale Datenbanken Abhilfe schaffen?), und 2 Arten von Datenschemata – beim Schreiben (wie in vielen NoSQL-Datenbanken) und beim Lesen (wie in relationalen Datenbanken).

Wenn wir zum Aspekt der Haltbarkeit zurückkehren, lautet die Schlussfolgerung wie folgt: Jede Datenbank, die auf die Festplatte schreibt, unabhängig von den Indizierungsmechanismen, kann gute Garantien für die Haltbarkeit Ihrer Daten bieten, Sie müssen sich jedoch mit jeder einzelnen Datenbank befassen , was genau es bietet.

6.5 Funktionsweise von In-Memory-DBs

Übrigens gibt es neben Datenbanken, die auf Festplatte schreiben, auch sogenannte „In-Memory“-Datenbanken, die hauptsächlich mit RAM arbeiten. Kurz gesagt: In-Memory-Datenbanken bieten aufgrund schnellerer Schreib- und Lesegeschwindigkeiten in der Regel eine geringere Haltbarkeit. Für einige Anwendungen kann dies jedoch angemessen sein.

Tatsache ist, dass RAM-Speicher seit langem teurer sind als Festplatten, aber in letzter Zeit wird er rapide billiger, was zur Entstehung eines neuen Datenbanktyps geführt hat – was angesichts der Geschwindigkeit, mit der Daten aus dem RAM gelesen und geschrieben werden, logisch ist. Aber Sie werden sich zu Recht fragen: Wie steht es mit der Datensicherheit dieser Datenbanken? Auch hier müssen Sie sich die Details der Implementierung ansehen. Generell bieten die Entwickler solcher Datenbanken folgende Mechanismen an:

  • Sie können batteriebetriebenes RAM verwenden;
  • Es ist möglich, Änderungsprotokolle auf die Festplatte zu schreiben (so etwas wie die oben erwähnten WALs), aber nicht die Daten selbst;
  • Sie können regelmäßig Kopien des Datenbankstatus auf die Festplatte schreiben (was ohne Verwendung anderer Optionen keine Garantie darstellt, sondern nur die Haltbarkeit verbessert);
  • Sie können den RAM-Status auf andere Maschinen replizieren.

Beispielsweise mangelt es der In-Memory-Redis-Datenbank, die hauptsächlich als Nachrichtenwarteschlange oder Cache verwendet wird, an der Haltbarkeit von ACID: Es garantiert nicht, dass ein erfolgreich ausgeführter Befehl auf der Festplatte gespeichert wird, da Redis Daten auf die Festplatte schreibt (wenn Sie Persistenz aktiviert haben) nur asynchron und in regelmäßigen Abständen.

Dies ist jedoch nicht für alle Anwendungen von entscheidender Bedeutung: Ich habe ein Beispiel für den kooperativen Online-Editor EtherPad gefunden, der alle 1–2 Sekunden geleert wurde und der Benutzer möglicherweise ein paar Buchstaben oder ein Wort verlieren konnte, was kaum kritisch war. Ansonsten kann Redis zum Implementieren von Transaktionen verwendet werden, da In-Memory-Datenbanken insofern gut sind, als sie Datenmodelle bereitstellen, die mit Festplattenindizes nur schwer zu implementieren wären – die Prioritätswarteschlange ermöglicht Ihnen dies.