Das Schlechte : Partitionierung
Eines der schwierigen Dinge, an die man sich zunächst gewöhnen muss, ist, dass Abfragen, die sich über Zeilen erstrecken, ohne Indizes (sehr) schlecht sein können. Wenn man jedoch an unser Speichermodell zurückdenkt, ist das nicht überraschend. Die Strategie, die Cassandra verwendet, um die Zeilen auf Hosts zu verteilen, wird als Partitionierung bezeichnet.
Bei der Partitionierung wird der Bereich der Zeilenschlüssel aufgeteilt, der sie dem „Token-Ring“ zuweist, der jedem Host auch die Verantwortung für ein Segment (dh eine Partition) des Zeilenschlüsselbereichs zuweist. Sie haben dies wahrscheinlich gesehen, als Sie Ihren Cluster mit einem „Token“ initialisiert haben. Das Token gibt dem Host einen Ort entlang des Token-Rings, der die Verantwortung für einen Abschnitt des Token-Bereichs zuweist. Bei der Partitionierung wird der rowkey dem Token-Bereich zugeordnet.
Es gibt zwei primäre Partitionierer: Random und Order Preserving . Sie sind angemessen benannt. Der RandomPartitioner hasht die Rowkeys in Token. Mit dem RandomPartitioner ist das Token ein Hash des Rowkeys. Dies macht es gut, Ihre Daten gleichmäßig auf eine Reihe von Knoten zu verteilen, macht jedoch das Abfragen eines Bereichs des Rowkey-Bereichs unglaublich schwierig. Aus nur einem „Start rowkey“ -Wert und einem „end rowkey“ -Wert kann Cassandra nicht bestimmen, welchen Bereich des Token-Speicherplatzes Sie benötigen. Es muss im Wesentlichen einen „Tabellenscan“ durchführen, um die Abfrage zu beantworten, und ein „Tabellenscan“ in Cassandra ist schlecht, da er zu jeder Maschine gehen muss (höchstwahrscheinlich zu ALLEN Maschinen, wenn Sie eine gute Hash-Funktion haben), um die Abfrage zu beantworten.
Das Gute: Sekundärindizes
Cassandra bietet einen nativen Indexierungsmechanismus in Sekundärindizes. Sekundäre Indizes arbeiten von den Spaltenwerten ab. Sie deklarieren einen sekundären Index für eine Spaltenfamilie. Datastax hat eine gute Dokumentation zur Verwendung. Unter der Haube verwaltet Cassandra eine „versteckte Spaltenfamilie“ als Index. (Einzelheiten finden Sie in der Präsentation von Ed Anuff) Da Cassandra keine Spaltenwertinformationen in einem Knoten verwaltet und sich sekundäre Indizes auf den Spaltenwert (und nicht auf rowkeys) beziehen, muss weiterhin eine Abfrage an alle Knoten gesendet werden. Darüber hinaus werden Sekundärindizes für Mengen mit hoher Kardinalität nicht empfohlen. Ich habe noch nicht nachgesehen, aber ich gehe davon aus, dass dies an dem Datenmodell liegt, das in der „versteckten Spaltenfamilie“ verwendet wird. Wenn die ausgeblendete Spaltenfamilie eine Zeile pro eindeutigem Wert speichert (mit rowkeys als Spalten), müssen die Zeilen gescannt werden, um festzustellen, ob sie innerhalb des Bereichs in der Abfrage liegen.
Aus Eds Präsentation:
- Nicht empfohlen für hohe Kardinalitätswerte (z. B. Zeitstempel, Geburtsdaten, Schlüsselwörter usw.)
- Erfordert mindestens einen Gleichheitsvergleich in einer Abfrage – nicht ideal für weniger als / größer als / Bereichsabfragen
- Unsortiert – Die Ergebnisse sind in Token-Reihenfolge, nicht in Abfragewertreihenfolge
- Cassandra versteht nativ die Suche nach Datentypen
Mit all dem funktionieren Sekundärindizes sofort und wir hatten guten Erfolg damit, sie für einfache Werte zu verwenden.
Das Hässliche : Do-It-Yourself (DIY) / Wide-Rows
Nun liegt Schönheit im Auge des Betrachters. Eines der schönen Dinge an NoSQL ist die Einfachheit. Die Konstrukte sind einfach: Schlüsselbereiche, Spaltenfamilien, Zeilen und Spalten. Es einfach zu halten bedeutet jedoch manchmal, dass Sie die Dinge selbst in die Hand nehmen müssen.
Dies ist bei Indizes mit breiter Zeile der Fall. Mit Cassandras Speichermodell ist es einfach, eigene Indizes zu erstellen, bei denen jeder Zeilenschlüssel zu einer Spalte im Index wird. Dies ist manchmal schwer zu verstehen, aber stellen wir uns vor, wir haben einen Fall, in dem wir alle Benutzer in einer Postleitzahl auswählen möchten. Die Spaltenfamilie der Hauptbenutzer ist in userid eingegeben, die Postleitzahl ist eine Spalte in jeder Benutzerzeile. Wir könnten sekundäre Indizes verwenden, aber es gibt einige Postleitzahlen. Stattdessen könnten wir eine Spaltenfamilie mit einer einzelnen Zeile namens „idx_zipcode“ pflegen. Wir könnten dann Spalten in diese Zeile der Form „zipcode_userid“ schreiben. Da die Spalten in sortierter Reihenfolge gespeichert sind, können schnell alle Spalten abgefragt werden, die mit „18964“ beginnen (z. B. könnten wir 18964_ und 18964_ZZZZZZ als Start- und Endwerte verwenden).
Ein offensichtlicher Nachteil dieses Ansatzes besteht darin, dass Zeilen auf einem Host in sich geschlossen sind. (mit Ausnahme von Replikaten) bedeutet dies, dass alle Abfragen einen einzelnen Knoten treffen. Ich habe noch keine gute Antwort darauf gefunden.