Kassandra. Wie man nicht stirbt, wenn man nur Oracle kennt

Hallo Habr.

Mein Name ist Misha Butrimov, ich möchte Ihnen etwas über Cassandra erzählen. Meine Geschichte wird für diejenigen nützlich sein, die noch nie mit NoSQL-Datenbanken in Berührung gekommen sind – sie enthält viele Implementierungsfunktionen und Fallstricke, über die Sie Bescheid wissen müssen. Und wenn Sie nichts anderes als Oracle oder eine andere relationale Datenbank gesehen haben, werden diese Dinge Ihr Leben retten.

Was ist so gut an Cassandra? Es handelt sich um eine NoSQL-Datenbank, die ohne einen Single Point of Failure konzipiert ist und sich gut skalieren lässt. Wenn Sie für eine Datenbank ein paar Terabyte hinzufügen müssen, fügen Sie dem Ring einfach Knoten hinzu. Auf ein anderes Rechenzentrum erweitern? Fügen Sie dem Cluster Knoten hinzu. Verarbeitete RPS erhöhen? Fügen Sie dem Cluster Knoten hinzu. Es funktioniert auch in die entgegengesetzte Richtung.

Kassandra. Wie man nicht stirbt, wenn man nur Oracle kennt

Was kann sie sonst noch gut? Es geht darum, viele Anfragen zu bearbeiten. Aber wie viel ist viel? 10, 20, 30, 40 Anfragen pro Sekunde sind nicht viel. 100 Anfragen pro Sekunde für die Aufnahme – auch. Es gibt Unternehmen, die sagen, dass sie 2 Millionen Anfragen pro Sekunde behalten. Sie werden es wahrscheinlich glauben müssen.

Und im Prinzip hat Cassandra einen großen Unterschied zu relationalen Daten – es ist ihnen überhaupt nicht ähnlich. Und es ist sehr wichtig, sich daran zu erinnern.

Nicht alles, was gleich aussieht, funktioniert auch gleich

Einmal kam ein Kollege zu mir und fragte: „Hier ist eine CQL-Cassandra-Abfragesprache, und sie hat eine SELECT-Anweisung, sie hat wo, sie hat und.“ Ich schreibe Briefe und es funktioniert nicht. Warum?". Cassandra wie eine relationale Datenbank zu behandeln, ist der perfekte Weg, gewaltsamen Selbstmord zu begehen. Und ich bewerbe es nicht, es ist in Russland verboten. Du entwirfst einfach etwas Falsches.

Zum Beispiel kommt ein Kunde zu uns und sagt: „Lass uns eine Datenbank für TV-Serien aufbauen, oder eine Datenbank für ein Rezeptverzeichnis.“ Wir werden dort Essensgerichte oder eine Liste der Fernsehserien und Schauspieler darin haben.“ Wir sagen freudig: „Auf geht’s!“ Einfach zwei Bytes, ein paar Zeichen schicken und fertig, alles klappt sehr schnell und zuverlässig. Und alles ist gut, bis die Kunden kommen und sagen, dass Hausfrauen auch das gegenteilige Problem lösen: Sie haben eine Produktliste und wollen wissen, welches Gericht sie kochen möchten. Du bist tot.

Dies liegt daran, dass es sich bei Cassandra um eine Hybriddatenbank handelt: Sie stellt gleichzeitig einen Schlüsselwert bereit und speichert Daten in breiten Spalten. In Java oder Kotlin könnte es so beschrieben werden:

Map<RowKey, SortedMap<ColumnKey, ColumnValue>>

Das heißt, eine Karte, die auch eine sortierte Karte enthält. Der erste Schlüssel zu dieser Zuordnung ist der Zeilenschlüssel oder Partitionsschlüssel – der Partitionierungsschlüssel. Der zweite Schlüssel, der der Schlüssel zu einer bereits sortierten Karte ist, ist der Clustering-Schlüssel.

Um die Verteilung der Datenbank zu veranschaulichen, zeichnen wir drei Knoten. Jetzt müssen Sie verstehen, wie Sie die Daten in Knoten zerlegen. Denn wenn wir alles in eins packen (es können übrigens tausend, zweitausend, fünf sein – so viele wie man möchte), geht es hier nicht wirklich um Verteilung. Daher benötigen wir eine mathematische Funktion, die eine Zahl zurückgibt. Nur eine Zahl, ein langer Int, der in einen bestimmten Bereich fällt. Und wir werden einen Knoten haben, der für einen Bereich verantwortlich ist, den zweiten für den zweiten und den n-ten für den n-ten.

Kassandra. Wie man nicht stirbt, wenn man nur Oracle kennt

Diese Zahl wird mithilfe einer Hash-Funktion erfasst, die auf den sogenannten Partitionsschlüssel angewendet wird. Dies ist die Spalte, die in der Primärschlüsselanweisung angegeben ist und die der erste und grundlegendste Schlüssel der Karte sein wird. Es bestimmt, welcher Knoten welche Daten empfängt. In Cassandra wird eine Tabelle mit fast der gleichen Syntax wie in SQL erstellt:

CREATE TABLE users (
	user_id uu id,
	name text,
	year int,
	salary float,
	PRIMARY KEY(user_id)

)

Der Primärschlüssel besteht in diesem Fall aus einer Spalte und ist gleichzeitig der Partitionierungsschlüssel.

Wie werden unsere Benutzer abschneiden? Einige gehen zu einem Knoten, einige zu einem anderen und einige zu einem dritten. Das Ergebnis ist eine gewöhnliche Hash-Tabelle, auch Map genannt, in Python auch Wörterbuch genannt, oder eine einfache Schlüsselwertstruktur, aus der wir alle Werte lesen und nach Schlüssel lesen und schreiben können.

Kassandra. Wie man nicht stirbt, wenn man nur Oracle kennt

Wählen Sie aus, wann aus der zulässigen Filterung ein vollständiger Scan wird oder was nicht erfolgen soll

Schreiben wir eine ausgewählte Anweisung: select * from users where, userid = . Es läuft wie bei Oracle ab: Wir schreiben „select“, geben die Bedingungen an und alles funktioniert, die Benutzer bekommen es. Wenn Sie jedoch beispielsweise einen Benutzer mit einem bestimmten Geburtsjahr auswählen, beschwert sich Cassandra, dass sie die Anfrage nicht erfüllen kann. Da sie überhaupt nichts darüber weiß, wie wir Daten über das Geburtsjahr verteilen, ist bei ihr nur eine Spalte als Schlüssel angegeben. Dann sagt sie: „Okay, diesen Wunsch kann ich noch erfüllen. „Zulassungsfilterung hinzufügen.“ Wir fügen die Direktive hinzu, alles funktioniert. Und in diesem Moment passiert etwas Schreckliches.

Wenn wir mit Testdaten arbeiten, ist alles in Ordnung. Und wenn Sie eine Abfrage in der Produktion ausführen, wo wir beispielsweise 4 Millionen Datensätze haben, dann ist für uns nicht alles sehr gut. Denn „Filterung zulassen“ ist eine Anweisung, die es Cassandra ermöglicht, alle Daten aus dieser Tabelle von allen Knoten, allen Rechenzentren (sofern es viele davon in diesem Cluster gibt) zu sammeln und sie erst dann zu filtern. Dies ist ein Analogon zum vollständigen Scan, und kaum jemand ist damit zufrieden.

Wenn wir nur Benutzer nach ID benötigen würden, wäre das kein Problem. Aber manchmal müssen wir andere Abfragen schreiben und der Auswahl andere Einschränkungen auferlegen. Daher erinnern wir uns: Dies ist alles eine Karte mit einem Partitionierungsschlüssel, in der sich jedoch eine sortierte Karte befindet.

Und sie hat auch einen Schlüssel, den wir Clustering Key nennen. Dieser Schlüssel besteht wiederum aus den von uns ausgewählten Spalten, mit deren Hilfe Cassandra versteht, wie seine Daten physisch sortiert sind und sich auf jedem Knoten befinden. Das heißt, für einige Partitionsschlüssel sagt Ihnen der Clustering-Schlüssel genau, wie die Daten in diesen Baum verschoben werden und welchen Platz sie dort einnehmen werden.

Das ist wirklich ein Baum, dort wird einfach ein Komparator genannt, an den wir einen bestimmten Satz von Spalten in Form eines Objekts übergeben, und er wird auch als Liste von Spalten angegeben.

CREATE TABLE users_by_year_salary_id (
	user_id uuid,
	name text,
	year int,
	salary float,
	PRIMARY KEY((year), salary, user_id)

Achten Sie auf die Primärschlüsselanweisung; ihr erstes Argument (in unserem Fall das Jahr) ist immer der Partitionsschlüssel. Es kann aus einer oder mehreren Spalten bestehen, das spielt keine Rolle. Wenn es mehrere Spalten gibt, muss es in Klammern wieder entfernt werden, damit der Sprachpräprozessor versteht, dass es sich hierbei um den Primärschlüssel handelt und alle anderen Spalten dahinter den Clustering-Schlüssel darstellen. In diesem Fall werden sie in der Reihenfolge ihres Erscheinens im Vergleicher übertragen. Das heißt, die erste Spalte ist signifikanter, die zweite weniger signifikant und so weiter. So schreiben wir beispielsweise „equals“-Felder für Datenklassen: Wir listen die Felder auf und schreiben für sie, welche größer und welche kleiner sind. In Cassandra sind dies relativ gesehen die Felder der Datenklasse, auf die die dafür geschriebenen Gleichheiten angewendet werden.

Wir legen Sortierungen fest und erlassen Beschränkungen

Sie müssen bedenken, dass die Sortierreihenfolge (absteigend, aufsteigend, was auch immer) beim Erstellen des Schlüssels festgelegt wird und später nicht mehr geändert werden kann. Es bestimmt physikalisch, wie die Daten sortiert und gespeichert werden. Wenn Sie den Clustering-Schlüssel oder die Sortierreihenfolge ändern müssen, müssen Sie eine neue Tabelle erstellen und Daten in diese übertragen. Dies funktioniert nicht mit einem vorhandenen.

Kassandra. Wie man nicht stirbt, wenn man nur Oracle kennt

Wir füllten unsere Tabelle mit Benutzern und sahen, dass sie zunächst nach Geburtsjahr in einen Ring eingeordnet wurden und dann auf jedem Knoten nach Gehalt und Benutzer-ID sortiert waren. Jetzt können wir auswählen, indem wir Beschränkungen auferlegen.

Unser funktionierendes Exemplar erscheint wieder where, and, und wir bekommen Benutzer und alles ist wieder gut. Wenn wir jedoch versuchen, nur einen Teil des Clustering-Schlüssels zu verwenden, und zwar einen weniger bedeutsamen, dann wird sich Cassandra sofort darüber beschweren, dass sie in unserer Karte nicht die Stelle finden kann, an der sich dieses Objekt, das diese Felder für den Nullkomparator enthält, und dieses befindet das wurde gerade eingestellt,- wo er liegt. Ich muss alle Daten von diesem Knoten erneut abrufen und filtern. Und das ist ein Analogon zum vollständigen Scan innerhalb eines Knotens, das ist schlecht.

Erstellen Sie in jeder unklaren Situation eine neue Tabelle

Was sollten wir tun, wenn wir Benutzer nach ID, Alter oder Gehalt ansprechen möchten? Nichts. Benutzen Sie einfach zwei Tische. Wenn Sie Benutzer auf drei verschiedene Arten erreichen müssen, gibt es drei Tabellen. Vorbei sind die Zeiten, in denen wir Platz an der Schraube gespart haben. Dies ist die günstigste Ressource. Es kostet viel weniger als die Reaktionszeit, was für den Benutzer schädlich sein kann. Für den Nutzer ist es viel angenehmer, in einer Sekunde etwas zu erhalten, als in 10 Minuten.

Wir tauschen unnötigen Speicherplatz und denormalisierte Daten gegen die Möglichkeit einer guten Skalierung und eines zuverlässigen Betriebs ein. Tatsächlich kann ein Cluster, der aus drei Rechenzentren besteht, von denen jedes über fünf Knoten verfügt, bei einem akzeptablen Grad an Datenerhaltung (wenn nichts verloren geht) den Tod eines Rechenzentrums vollständig überstehen. Und zwei weitere Knoten in jedem der verbleibenden zwei. Und erst danach beginnen die Probleme. Das ist eine ziemlich gute Redundanz, es lohnt sich ein paar zusätzliche SSD-Laufwerke und Prozessoren. Um Cassandra zu verwenden, das niemals SQL ist und in dem es keine Beziehungen und Fremdschlüssel gibt, müssen Sie daher einfache Regeln kennen.

Wir gestalten alles nach Ihren Wünschen. Die Hauptsache sind nicht die Daten, sondern wie die Anwendung damit arbeiten wird. Wenn unterschiedliche Daten auf unterschiedliche Weise oder dieselben Daten auf unterschiedliche Weise empfangen werden müssen, müssen wir sie auf eine für die Anwendung geeignete Weise formulieren. Andernfalls werden wir beim vollständigen Scan scheitern und Cassandra wird uns keinen Vorteil verschaffen.

Die Denormalisierung von Daten ist die Norm. Wir vergessen Normalformen, wir haben keine relationalen Datenbanken mehr. Wenn wir etwas 100 Mal hinlegen, wird es sich 100 Mal hinlegen. Es ist immer noch billiger als anzuhalten.

Wir wählen die Schlüssel für die Partitionierung so aus, dass sie normal verteilt sind. Wir möchten nicht, dass der Hash unserer Schlüssel in einen engen Bereich fällt. Das heißt, das Geburtsjahr im obigen Beispiel ist ein schlechtes Beispiel. Genauer gesagt ist es gut, wenn unsere Benutzer normal nach Geburtsjahr verteilt sind, und schlecht, wenn es sich um Schüler der 5. Klasse handelt – die Aufteilung dort wird nicht sehr gut sein.

Die Sortierung wird einmal bei der Erstellung des Clustering-Schlüssels ausgewählt. Wenn es geändert werden muss, müssen wir unsere Tabelle mit einem anderen Schlüssel aktualisieren.

Und das Wichtigste: Wenn wir dieselben Daten auf 100 verschiedene Arten abrufen müssen, dann haben wir 100 verschiedene Tabellen.

Source: habr.com

Kommentar hinzufügen