Cassandra-Indizierung: Das Gute, das Schlechte und das Hässliche

Innerhalb von NoSQL sind die Vorgänge des Indizierens, Abrufens und Suchens von Informationen eng mit den physischen Speichermechanismen verbunden. Es ist wichtig zu beachten, dass Zeilen hostübergreifend gespeichert werden, eine einzelne Zeile jedoch auf einem einzelnen Host. (mit Replikaten) Spaltenfamilien werden in sortierter Reihenfolge gespeichert, wodurch das Abfragen einer Reihe von Spalten effizient wird (vorausgesetzt, Sie erstrecken sich über Zeilen).

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.

Jetzt, zu den hohen Kosten einer gleichmäßigen Datenverteilung, können Sie den OrderPreservingPartitioner (OPP) verwenden. Ich bin * nicht * unten mit OPP. Das OPP behält die Reihenfolge bei, während es Rowkeys in Token übersetzt. Mit einem Start-Rowkey-Wert und einem End-Rowkey-Wert kann Cassandra * genau bestimmen, welche Hosts die gesuchten Daten enthalten. Es berechnet den Startwert zu einem Token, den Endwert zu einem Token und wählt einfach alles dazwischen aus und gibt es zurück. Wenn Sie jedoch die Reihenfolge beibehalten, sind Ihre Token auch nicht vorhanden, es sei denn, Ihre Rowkeys sind gleichmäßig über den Speicherplatz verteilt, und Sie erhalten einen schiefen Cluster, was die Kosten für die Konfiguration und Verwaltung des Clusters erheblich erhöht. (es lohnt sich nicht)

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.

Darüber hinaus, und IMHO, ist der hässlichste Teil der DIY Wide-Row-Indizierung aus einer Client-Perspektive. In unserer Implementierung haben wir unser Bestes getan, um auf der Clientseite sprachunabhängig zu sein, sodass die Benutzer das beste Tool für den Job auswählen können, um mit den Daten in Cassandra zu interagieren. Mit dieser Mentalität stellen die DIY-Indizes einige Probleme dar. Breite Zeilen verwenden häufig zusammengesetzte Schlüssel (stellen Sie sich vor, Sie hätten eine idx_state_zip , mit der Sie nach Status und dann nach zip abfragen können). Obwohl es „native“ Unterstützung für zusammengesetzte Schlüssel gibt, implementieren alle Clientbibliotheken ihre eigene Version davon (Hector, Astyanax und Thrift). Dies bedeutet, dass der Client, der Daten abfragen muss, über die zusätzliche Logik verfügen muss, um zuerst den Index abzufragen, und zusätzlich müssen alle Clients den zusammengesetzten Schlüssel auf die gleiche Weise erstellen.

Es besser machen…

Aus diesem Grund haben wir beschlossen, zwei Open-Source-Projekte zu veröffentlichen, die helfen, diese Logik auf die Serverseite zu bringen. Das erste Projekt ist Cassandra-Trigger. Auf diese Weise können Sie asynchrone Aktivitäten an Schreibvorgänge in Cassandra anhängen. (eine solche Aktivität könnte die Indizierung sein) Wir haben auch Cassandra-Indexing veröffentlicht. Dies ist druckfrisch und steckt noch in den Kinderschuhen (zB unterstützt es nur UT8Types im Index), aber die Absicht ist, einen generischen serverseitigen Mechanismus bereitzustellen, der Daten indiziert, wenn sie in Cassandra geschrieben werden. Mit der gleichen serverseitigen Technik, die wir bei der Cassandra-Indizierung verwendet haben, konfigurieren Sie einfach die Spalten, die indiziert werden sollen, und der AOP-Code erledigt den Rest, während Sie in die Zieldatei schreiben. Wie immer sind Fragen, Kommentare und Gedanken willkommen. (besonders wenn ich irgendwo außerhalb der Basis bin)

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.