Cassandra. Jak nie umrzeć, jeśli znasz tylko Wyrocznię

Hej Habr.

Nazywam się Misha Butrimov, chciałbym opowiedzieć trochę o Cassandrze. Moja historia przyda się tym, którzy nigdy nie mieli styczności z bazami danych NoSQL – kryje w sobie wiele funkcji implementacyjnych i pułapek, o których warto wiedzieć. A jeśli nie widziałeś niczego innego niż Oracle lub jakąkolwiek inną relacyjną bazę danych, te rzeczy uratują ci życie.

Co jest takiego dobrego w Cassandrze? Jest to baza danych NoSQL zaprojektowana bez pojedynczego punktu awarii, która dobrze się skaluje. Jeśli chcesz dodać kilka terabajtów do jakiejś bazy danych, po prostu dodajesz węzły do ​​pierścienia. Rozszerzyć je na inne centrum danych? Dodaj węzły do ​​klastra. Zwiększyć przetworzone RPS? Dodaj węzły do ​​klastra. Działa to także w odwrotnym kierunku.

Cassandra. Jak nie umrzeć, jeśli znasz tylko Wyrocznię

W czym jeszcze jest dobra? Chodzi o obsługę wielu żądań. Ale ile to dużo? 10, 20, 30, 40 tysięcy żądań na sekundę to niewiele. 100 tysięcy żądań na sekundę w celu nagrania. Są firmy, które twierdzą, że obsługują 2 miliony żądań na sekundę. Pewnie będą musieli w to uwierzyć.

I w zasadzie Cassandra różni się od danych relacyjnych jedną dużą różnicą - wcale nie jest do nich podobna. I bardzo ważne jest, aby o tym pamiętać.

Nie wszystko, co wygląda tak samo, działa tak samo

Kiedyś przyszedł do mnie kolega i zapytał: „Oto język zapytań CQL Cassandra, który zawiera instrukcję wyboru, ma gdzie, ma i. Piszę listy i nie działa. Dlaczego?". Traktowanie Cassandry jak relacyjnej bazy danych to doskonały sposób na popełnienie brutalnego samobójstwa. I nie będę tego promować, w Rosji jest to zabronione. Po prostu zaprojektujesz coś złego.

Na przykład przychodzi do nas klient i mówi: „Zbudujmy bazę danych dla seriali, albo bazę dla katalogu z przepisami. Będziemy tam mieli dania z jedzeniem lub listę seriali i aktorów, którzy w nich występują. Mówimy radośnie: „Idziemy!” Wystarczy wysłać dwa bajty, kilka znaków i gotowe, wszystko będzie działać bardzo szybko i niezawodnie. I wszystko jest w porządku, dopóki nie przychodzą klienci i nie mówią, że gospodynie domowe rozwiązują także odwrotny problem: mają listę produktów i chcą wiedzieć, jakie danie chcą ugotować. Nie żyjesz.

Dzieje się tak, ponieważ Cassandra jest bazą danych hybrydową: jednocześnie dostarcza kluczową wartość i przechowuje dane w szerokich kolumnach. W Javie lub Kotlinie można to opisać w ten sposób:

Map<RowKey, SortedMap<ColumnKey, ColumnValue>>

Oznacza to, że mapa zawiera również posortowaną mapę. Pierwszym kluczem do tej mapy jest klucz wiersza lub klucz partycji - klucz partycjonowania. Drugi klucz, będący kluczem do już posortowanej mapy, to klucz grupujący.

Aby zilustrować rozkład bazy danych, narysujmy trzy węzły. Teraz musisz zrozumieć, jak rozłożyć dane na węzły. Bo jeśli upchniemy wszystko w jeden (swoją drogą, może być tysiąc, dwa tysiące, pięć – tyle, ile chcesz), to tak naprawdę nie chodzi o dystrybucję. Dlatego potrzebujemy funkcji matematycznej, która zwróci liczbę. Tylko liczba, długa liczba całkowita, która będzie mieścić się w pewnym zakresie. I będziemy mieli jeden węzeł odpowiedzialny za jeden zakres, drugi za drugi, n-ty za n-ty.

Cassandra. Jak nie umrzeć, jeśli znasz tylko Wyrocznię

Liczba ta jest pobierana za pomocą funkcji skrótu, która jest stosowana do tak zwanego klucza partycji. Jest to kolumna określona w dyrektywie klucza podstawowego i jest to kolumna, która będzie pierwszym i najbardziej podstawowym kluczem mapy. Określa, który węzeł otrzyma jakie dane. W Cassandrze tworzona jest tabela z prawie taką samą składnią jak w SQL:

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

)

Klucz podstawowy w tym przypadku składa się z jednej kolumny i jest jednocześnie kluczem partycjonującym.

Jak będą działać nasi użytkownicy? Niektórzy pójdą do jednego węzła, inni do drugiego, a jeszcze inni do trzeciego. Rezultatem jest zwykła tablica mieszająca, znana również jako mapa, znana również jako słownik w Pythonie lub prosta struktura wartości klucza, z której możemy czytać wszystkie wartości, czytać i zapisywać według klucza.

Cassandra. Jak nie umrzeć, jeśli znasz tylko Wyrocznię

Wybierz: kiedy zezwolenie na filtrowanie zamienia się w pełne skanowanie lub czego nie robić

Napiszmy kilka instrukcji wyboru: select * from users where, userid = . Okazuje się, że w Oracle: piszemy wybierz, określamy warunki i wszystko działa, użytkownicy to dostają. Ale jeśli wybierzesz na przykład użytkownika z określonym rokiem urodzenia, Cassandra skarży się, że nie może spełnić żądania. Ponieważ nie ma zielonego pojęcia o tym, w jaki sposób rozpowszechniamy dane o roku urodzenia - ma tylko jedną kolumnę oznaczoną jako klucz. Następnie mówi: „OK, nadal mogę spełnić tę prośbę. Dodaj zezwolenie na filtrowanie.” Dodajemy dyrektywę, wszystko działa. I w tym momencie dzieje się coś strasznego.

Kiedy uruchamiamy dane testowe, wszystko jest w porządku. A gdy wykonujemy zapytanie na produkcji, gdzie mamy np. 4 miliony rekordów, to nie wszystko jest dla nas zbyt dobre. Ponieważ zezwolenie na filtrowanie to dyrektywa, która pozwala Cassandrze zebrać wszystkie dane z tej tabeli ze wszystkich węzłów, wszystkich centrów danych (jeśli jest ich wiele w tym klastrze), a dopiero potem je przefiltrować. Jest to odpowiednik Full Scan i mało kto jest nim zachwycony.

Gdybyśmy potrzebowali użytkowników tylko według identyfikatora, nie byłoby w tym nic złego. Czasami jednak musimy napisać inne zapytania i nałożyć inne ograniczenia na wybór. Dlatego pamiętajmy: to wszystko jest mapą, która ma klucz partycjonowania, ale wewnątrz znajduje się mapa posortowana.

Ma także klucz, który nazywamy Kluczem Klastrowym. Ten klucz, który z kolei składa się z wybranych przez nas kolumn, za pomocą którego Cassandra rozumie, w jaki sposób jej dane są fizycznie posortowane i będą zlokalizowane w każdym węźle. Oznacza to, że w przypadku niektórych kluczy partycji klucz klastrowania powie dokładnie, jak wypchnąć dane do tego drzewa i jakie miejsce tam zajmą.

To tak naprawdę jest drzewo, nazywa się tam po prostu komparator, do którego przekazujemy pewien zbiór kolumn w postaci obiektu, i jest on też określony jako lista kolumn.

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

Zwróć uwagę na dyrektywę klucza podstawowego; jej pierwszym argumentem (w naszym przypadku jest rok) jest zawsze klucz partycji. Może składać się z jednej lub więcej kolumn, nie ma to znaczenia. Jeśli jest kilka kolumn, należy je ponownie usunąć w nawiasach, aby preprocesor języka zrozumiał, że jest to klucz podstawowy, a za nim wszystkie pozostałe kolumny to klucz klastrowania. W takim przypadku będą one transmitowane w komparatorze w kolejności, w jakiej się pojawiają. Oznacza to, że pierwsza kolumna jest bardziej znacząca, druga jest mniej znacząca i tak dalej. Jak piszemy np. równe pola dla klas danych: wylistowujemy pola i dla nich piszemy które są większe, a które mniejsze. W Cassandrze są to, mówiąc relatywnie, pola klasy danych, do których zostaną zastosowane zapisane dla niej wartości równe.

Ustawiamy sortowanie i narzucamy ograniczenia

Należy pamiętać, że kolejność sortowania (malejąco, rosnąco, jakakolwiek) ustalana jest w momencie tworzenia klucza i nie można jej później zmienić. Fizycznie określa, w jaki sposób dane będą sortowane i jak będą przechowywane. Jeśli zajdzie potrzeba zmiany klucza grupowania lub kolejności sortowania, konieczne będzie utworzenie nowej tabeli i przeniesienie do niej danych. To nie będzie działać z już istniejącym.

Cassandra. Jak nie umrzeć, jeśli znasz tylko Wyrocznię

Wypełniliśmy naszą tabelę użytkownikami i zobaczyliśmy, że wpadli w pierścień, najpierw według roku urodzenia, a następnie w każdym węźle według wynagrodzenia i identyfikatora użytkownika. Teraz możemy wybierać, nakładając ograniczenia.

Nasz działający pojawia się ponownie where, and, zdobywamy użytkowników i znowu wszystko jest w porządku. Ale jeśli spróbujemy użyć tylko części klucza Clustering, i to mniej znaczącego, to Cassandra od razu będzie narzekać, że nie może znaleźć na naszej mapie miejsca, gdzie ten obiekt, który ma te pola dla komparatora zerowego, i ten właśnie ustalono , - gdzie leży. Będę musiał ponownie pobrać wszystkie dane z tego węzła i je przefiltrować. Jest to odpowiednik pełnego skanowania w węźle, to jest złe.

W każdej niejasnej sytuacji utwórz nową tabelę

Jeśli chcemy mieć możliwość targetowania użytkowników według identyfikatora, wieku lub wynagrodzenia, co powinniśmy zrobić? Nic. Po prostu użyj dwóch tabel. Jeśli chcesz dotrzeć do użytkowników na trzy różne sposoby, dostępne będą trzy tabele. Dawno minęły czasy, kiedy oszczędzaliśmy miejsce na śrubie. To najtańsze źródło informacji. Kosztuje znacznie mniej niż czas reakcji, co może być szkodliwe dla użytkownika. Użytkownikowi o wiele przyjemniej jest otrzymać coś w ciągu sekundy niż w ciągu 10 minut.

Zamieniamy niepotrzebną przestrzeń i zdenormalizowane dane na możliwość dobrego skalowania i niezawodnego działania. W końcu klaster składający się z trzech centrów danych, z których każde ma pięć węzłów, przy akceptowalnym poziomie zachowania danych (gdy nic nie zostanie utracone), jest w stanie całkowicie przetrwać śmierć jednego centrum danych. I jeszcze dwa węzły w każdym z pozostałych dwóch. I dopiero potem zaczynają się problemy. Jest to całkiem niezła redundancja, warta kilku dodatkowych dysków SSD i procesorów. Dlatego, żeby skorzystać z Cassandry, która nigdy nie jest SQLem, w którym nie ma relacji, kluczy obcych, trzeba znać proste zasady.

Zaprojektujemy wszystko zgodnie z Twoim życzeniem. Najważniejsze nie są dane, ale to, jak aplikacja będzie z nimi współpracować. Jeśli ma otrzymywać różne dane na różne sposoby lub te same dane na różne sposoby, musimy to ułożyć w sposób wygodny dla aplikacji. Inaczej nie uda nam się przeprowadzić Pełnego Skanowania i Cassandra nie da nam żadnej przewagi.

Denormalizacja danych jest normą. Zapominamy o normalnych formularzach, nie mamy już relacyjnych baz danych. Jeśli położymy coś 100 razy, to położy się 100 razy. To wciąż tańsze niż zatrzymanie się.

Wybieramy klucze do partycjonowania, aby były normalnie dystrybuowane. Nie chcemy, aby skrót naszych kluczy mieścił się w jednym wąskim zakresie. Oznacza to, że rok urodzenia w powyższym przykładzie jest złym przykładem. Mówiąc dokładniej, dobrze jest, jeśli nasi użytkownicy są normalnie podzieleni według roku urodzenia, a źle, jeśli mówimy o uczniach piątej klasy - podział tam nie będzie zbyt dobry.

Sortowanie wybierane jest jednorazowo na etapie tworzenia klucza klastrowania. Jeśli zajdzie potrzeba zmiany, będziemy musieli zaktualizować naszą tabelę o inny klucz.

I najważniejsze: jeśli będziemy musieli odzyskać te same dane na 100 różnych sposobów, będziemy mieli 100 różnych tabel.

Źródło: www.habr.com

Dodaj komentarz