indeksowanie Cassandry: dobre, złe i brzydkie

w NoSQL operacje indeksowania, pobierania i wyszukiwania informacji są ściśle powiązane z fizycznymi mechanizmami przechowywania. Ważne jest, aby pamiętać, że wiersze są przechowywane między hostami, ale jeden wiersz jest przechowywany na jednym Hostie. rodziny kolumn (z replikami) są przechowywane w posortowanej kolejności, co sprawia, że Zapytanie o zestaw kolumn jest wydajne (pod warunkiem, że są one rozciągnięte na wiersze).

The Bad : Partycjonowanie

jedną z trudnych rzeczy, do których należy się przyzwyczaić na początku, jest to, że bez żadnych zapytań indeksów, które obejmują wiersze, mogą (bardzo) być złe. Wracając jednak do naszego modelu przechowywania, nie jest to zaskakujące. Strategia, której używa Cassandra do dystrybucji wierszy między hostami, nazywa się partycjonowaniem.

partycjonowanie jest aktem rzeźbienia zakresu klawiszy RowKey przypisując je do” token ring”, który również przypisuje odpowiedzialność za segment (tj. partycję) zakresu rowkey każdemu hostowi. Prawdopodobnie widziałeś to podczas inicjalizacji klastra za pomocą”tokena”. Token daje hostowi lokalizację wzdłuż pierścienia tokenów, co przypisuje odpowiedzialność za część zakresu tokenów. Partycjonowanie polega na odwzorowaniu wiersza na zakres tokenów.

istnieją dwa podstawowe partycjonery: Random i Order Preserving. Są one odpowiednio nazwane. RandomPartitioner hashuje rowkeys na tokeny. W przypadku RandomPartitioner token jest skrótem klucza wierszowego. Zapewnia to równomierne rozłożenie danych na zestaw węzłów, ale sprawia, że odpytywanie zakresu przestrzeni rowkey jest niezwykle trudne. Na podstawie wartości „start rowkey” i wartości „end rowkey” Cassandra nie może określić zakresu potrzebnej przestrzeni tokenów. Zasadniczo musi wykonać „skanowanie tabeli”, aby odpowiedzieć na zapytanie, a” skanowanie tabeli ” w Cassandrze jest złe, ponieważ musi przejść do każdej maszyny (najprawdopodobniej wszystkich maszyn, jeśli masz dobrą funkcję skrótu), aby odpowiedzieć na zapytanie.

teraz, kosztem równomiernej dystrybucji danych, możesz zatrudnić OrderPreservingPartitioner (OPP). Nie zgadzam się z OPP. OPP zachowuje porządek, ponieważ tłumaczy rowkeys na tokeny. Teraz, biorąc pod uwagę początkową wartość rowkey i końcową wartość rowkey, Cassandra * może * dokładnie określić, które hosty mają dane, których szukasz. Oblicza wartość początkową tokena wartość końcową tokena i po prostu wybiera i zwraca wszystko pomiędzy. Ale zachowując porządek, o ile twoje rowkeys nie są równomiernie rozmieszczone w przestrzeni, Twoje tokeny nie będą również i otrzymasz koślawy klaster, co znacznie zwiększa koszty konfiguracji i Administracji klastra. (nie warto)

dobre : indeksy wtórne

Cassandra zapewnia natywny mechanizm indeksowania w indeksach wtórnych. Indeksy drugorzędne działają poza wartościami kolumn. Deklarujesz indeks wtórny w rodzinie kolumn. Datastax posiada dobrą dokumentację dotyczącą użytkowania. Pod maską Cassandra utrzymuje” ukrytą rodzinę kolumn ” jako indeks. Ponieważ Cassandra nie przechowuje informacji o wartości kolumn w żadnym z węzłów, a indeksy drugorzędne znajdują się na wartości kolumn (zamiast rowkeys), zapytanie nadal musi zostać wysłane do wszystkich węzłów. Dodatkowo, indeksy drugorzędne nie są zalecane dla zestawów o wysokiej cardinalności. Jeszcze nie szukałem, ale zakładam, że jest to spowodowane modelem danych używanym w „rodzinie kolumn ukrytych”. Jeśli ukryta rodzina kolumn przechowuje wiersz na unikalną wartość (z rowkeys jako kolumnami), oznacza to skanowanie wierszy w celu określenia, czy znajdują się one w zakresie zapytania.
z prezentacji Eda:

  • nie zaleca się stosowania przy wysokich wartościach kardynalnych (np. znaczniki czasu, daty urodzenia, słowa kluczowe itp.
  • wymaga co najmniej jednego porównania równości w zapytaniu-nie jest Świetne dla zapytań mniej/więcej / zakres
  • nieposortowane-wyniki są w kolejności tokenowej, a nie kolejności wartości zapytania
  • ograniczone do wyszukiwania na typach danych, Cassandra natywnie rozumie

z tym wszystkim, indeksy wtórne działają po wyjęciu z pudełka i odnieśliśmy sukces, używając ich na prostych wartościach.

brzydki: zrób to sam (DIY) / Wide-Rows

teraz piękno jest w oku patrzącego. Jedną z pięknych rzeczy w NoSQL jest prostota. Konstrukcje są proste: przestrzenie kluczy, rodziny kolumn, wiersze i kolumny. Utrzymanie go w prostocie oznacza jednak, że czasami trzeba wziąć sprawy w swoje ręce.

tak jest w przypadku Indeksów szerokorzędowych. Korzystając z modelu pamięci masowej Cassandra, można łatwo zbudować własne indeksy, w których każdy wiersz-klucz staje się kolumną w indeksie. Czasami trudno jest to ogarnąć, ale wyobraźmy sobie, że mamy przypadek, w którym chcemy wybrać wszystkich użytkowników w kodzie pocztowym. Rodzina kolumn main users jest kluczowana na userid, kod pocztowy jest kolumną w każdym wierszu użytkownika. Moglibyśmy użyć drugorzędnych indeksów, ale jest sporo kodów pocztowych. Zamiast tego moglibyśmy utrzymywać rodzinę kolumn z pojedynczym wierszem o nazwie „idx_zipcode”. Następnie możemy zapisać kolumny do tego wiersza w postaci „zipcode_userid”. Ponieważ kolumny są przechowywane w porządku posortowanym, szybko jest wyszukiwać wszystkie kolumny, które zaczynają się od „18964” (np. możemy użyć 18964_ i 18964_zzzzz jako wartości początkowych i końcowych).

oczywistym minusem tego podejścia jest to, że wiersze są samodzielne na hoście. (ponownie z wyjątkiem replik) oznacza to, że wszystkie zapytania trafią do jednego węzła. Nie znalazłem na to jeszcze dobrej odpowiedzi.

dodatkowo, IMHO, najbrzydsza część indeksowania szerokorzędowego DIY jest z perspektywy klienta. W naszej implementacji dołożyliśmy wszelkich starań, aby być agnostykiem językowym po stronie klienta, pozwalając ludziom wybrać najlepsze narzędzie do pracy do interakcji z danymi w Cassandra. Z tą mentalnością, indeksy DIY stwarzają pewne problemy. Szerokie wiersze często używają klawiszy złożonych (wyobraź sobie, że masz idx_state_zip, który pozwoli Ci odpytywać według stanu, a następnie zip). Chociaż istnieje” natywne ” wsparcie dla kluczy złożonych, wszystkie biblioteki klienckie implementują ich własną wersję (Hector, Astyanax i Thrift). Oznacza to, że klient potrzebujący odpytywania danych musi mieć dodaną logikę, aby najpierw odpytywać indeks, a dodatkowo wszyscy klienci muszą skonstruować klucz złożony w ten sam sposób.

Żeby Było Lepiej…

z tego właśnie powodu zdecydowaliśmy się wydać dwa projekty open source, które pomagają wypchnąć tę logikę na stronę serwera. Pierwszy projekt to Cassandra-Triggers. Pozwala to na dołączanie asynchronicznych działań do zapisów w Cassandrze. (jedną z takich czynności może być indeksowanie) wydaliśmy również indeksowanie Cassandry. Jest to bardzo popularne rozwiązanie i wciąż jest w powijakach (np. obsługuje tylko typy UT8 w indeksie), ale intencją jest dostarczenie ogólnego mechanizmu po stronie serwera, który indeksuje dane zapisane do Cassandry. Stosując tę samą technikę po stronie serwera, której użyliśmy w indeksowaniu Cassandry, po prostu konfigurujesz kolumny, które chcesz zindeksować, a kod AOP robi resztę, gdy piszesz do docelowego CF. Jak zawsze pytania, komentarze i przemyślenia są mile widziane. (zwlaszcza jesli jestem gdzieś poza bazą)

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany.