HighLoad++, Mikhail Tyulenev (MongoDB): Spójność przyczynowa: od teorii do praktyki

Najbliższa konferencja HighLoad++ odbędzie się 6 i 7 kwietnia 2020 w St. Petersburgu.
Szczegóły i bilety по ссылке. HighLoad++ Syberia 2019. Hala „Krasnojarsk”. 25 czerwca, 12:00. Tezy i prezentacja.

HighLoad++, Mikhail Tyulenev (MongoDB): Spójność przyczynowa: od teorii do praktyki

Zdarza się, że wymagania praktyczne stoją w sprzeczności z teorią, gdzie nie bierze się pod uwagę aspektów istotnych dla produktu komercyjnego. W tym wykładzie przedstawiono proces wyboru i łączenia różnych podejść do tworzenia komponentów spójności przyczynowej w oparciu o badania akademickie oparte na wymaganiach produktu komercyjnego. Słuchacze dowiedzą się o istniejących teoretycznych podejściach do zegarów logicznych, śledzeniu zależności, bezpieczeństwie systemu, synchronizacji zegarów i dlaczego MongoDB zdecydowało się na określone rozwiązania.

Michaił Tyulenew (zwany dalej MT): – Opowiem o spójności przyczynowej – jest to funkcja, nad którą pracowaliśmy w MongoDB. Pracuję w grupie systemów rozproszonych, robiliśmy to jakieś dwa lata temu.

HighLoad++, Mikhail Tyulenev (MongoDB): Spójność przyczynowa: od teorii do praktyki

Przy okazji musiałem zapoznać się z wieloma badaniami akademickimi, ponieważ ta funkcja została dość dobrze zbadana. Okazało się, że ani jeden artykuł nie mieści się w tym, co jest wymagane w produkcyjnej bazie danych, ze względu na bardzo specyficzne wymagania, które prawdopodobnie występują w każdej aplikacji produkcyjnej.

Opowiem o tym, jak my, jako konsumenci Badań Naukowych, przygotowujemy z niego coś, co możemy następnie zaprezentować naszym użytkownikom jako gotowe danie, wygodne i bezpieczne w użyciu.

Spójność przyczynowa. Zdefiniujmy pojęcia

Na początek chcę powiedzieć ogólnie, czym jest spójność przyczynowa. Są dwie postacie - Leonard i Penny (serial „Teoria wielkiego podrywu”):

HighLoad++, Mikhail Tyulenev (MongoDB): Spójność przyczynowa: od teorii do praktyki

Załóżmy, że Penny jest w Europie i Leonard chce jej urządzić przyjęcie-niespodziankę. I nie przychodzi mu do głowy nic lepszego niż wyrzucenie jej z listy znajomych i wysłanie wszystkim znajomym aktualizacji na kanale: „Uczyńmy Penny szczęśliwą!” (jest w Europie, śpi, nie widzi tego wszystkiego i nie może tego zobaczyć, bo jej tam nie ma). Ostatecznie usuwa ten post, usuwa go z Feedu i przywraca dostęp, aby niczego nie zauważyła i nie było skandalu.
Wszystko fajnie, ale załóżmy, że system jest rozproszony i coś poszło nie tak. Może się np. zdarzyć, że ograniczenie dostępu Penny nastąpiło po ukazaniu się tego wpisu, jeśli zdarzenia nie są ze sobą powiązane przyczynowo-skutkowo. Właściwie jest to przykład sytuacji, w której do wykonania funkcji biznesowej wymagana jest spójność przyczynowa (w tym przypadku).

Tak naprawdę są to dość nietrywialne właściwości bazy danych – wspiera je bardzo niewiele osób. Przejdźmy do modeli.

Modele spójności

Czym dokładnie jest model spójności w bazach danych? To tylko niektóre z gwarancji, jakie daje system rozproszony odnośnie tego, jakie dane klient może otrzymać i w jakiej kolejności.

W zasadzie wszystkie modele spójności sprowadzają się do podobieństwa systemu rozproszonego do systemu działającego na przykład na jednym węźle laptopa. I tak podobny jest system działający na tysiącach rozproszonych geograficznie „węzłów” do laptopa, w którym wszystkie te funkcje są w zasadzie wykonywane automatycznie.

Dlatego modele spójności stosuje się tylko do systemów rozproszonych. Wszystkie systemy, które wcześniej istniały i działały przy tym samym skalowaniu pionowym, nie miały takich problemów. Istniała jedna pamięć podręczna buforowa i wszystko było z niej zawsze odczytywane.

Modelka Mocna

Właściwie pierwszy model jest silny (lub linia zdolności wzrostu, jak to się często nazywa). Jest to model spójności zapewniający, że każda zmiana, po potwierdzeniu jej wystąpienia, jest widoczna dla wszystkich użytkowników systemu.

Tworzy to globalny porządek wszystkich zdarzeń w bazie danych. Jest to właściwość charakteryzująca się bardzo dużą konsystencją i generalnie jest bardzo kosztowna. Jednak jest bardzo dobrze wspierany. Jest po prostu bardzo drogi i powolny – po prostu rzadko używany. Nazywa się to zdolnością do wzrostu.

Istnieje inna, silniejsza właściwość obsługiwana w Spannerze — zwana spójnością zewnętrzną. Porozmawiamy o tym trochę później.

Przyczynowy

Następnym jest Causal, o którym właśnie mówiłem. Istnieje jeszcze kilka podpoziomów pomiędzy Silnym a Przyczynowym, o których nie będę mówił, ale wszystkie sprowadzają się do Przyczynowego. Jest to ważny model, ponieważ jest najsilniejszym ze wszystkich modeli, najsilniejszą spójnością w obecności sieci lub partycji.

Przyczyny to w rzeczywistości sytuacja, w której zdarzenia są połączone związkiem przyczynowo-skutkowym. Bardzo często są one postrzegane z punktu widzenia klienta jako Przeczytaj swoje prawa. Jeśli klient zaobserwował pewne wartości, nie może dostrzec wartości, które były w przeszłości. Już zaczyna widzieć odczyty prefiksów. Wszystko sprowadza się do tego samego.
Przyczyny jako model spójności to częściowe uporządkowanie zdarzeń na serwerze, w którym zdarzenia od wszystkich klientów są obserwowane w tej samej kolejności. W tym przypadku Leonard i Penny.

Ewentualny

Trzeci model to ostateczna spójność. To właśnie obsługują absolutnie wszystkie systemy rozproszone, minimalny model, który w ogóle ma sens. Oznacza to, że gdy mamy pewne zmiany w danych, w pewnym momencie stają się one spójne.

W takim momencie nic nie mówi, inaczej zamieniłaby się w Zewnętrzną Spójność – to byłaby zupełnie inna historia. Niemniej jednak jest to bardzo popularny model, najczęstszy. Domyślnie wszyscy użytkownicy systemów rozproszonych korzystają ze spójności ostatecznej.

Chcę podać kilka przykładów porównawczych:

HighLoad++, Mikhail Tyulenev (MongoDB): Spójność przyczynowa: od teorii do praktyki

Co oznaczają te strzałki?

  • Czas oczekiwania. W miarę wzrostu siły spójności staje się ona większa z oczywistych powodów: trzeba utworzyć więcej rekordów, uzyskać potwierdzenie od wszystkich hostów i węzłów uczestniczących w klastrze, że dane już tam są. W związku z tym najszybszą odpowiedzią jest Eventual Consistency, bo tam z reguły można nawet zapisać ją w pamięci i to w zasadzie wystarczy.
  • Dostępność. Jeśli rozumiemy to jako zdolność systemu do reagowania w obecności przerw w sieci, partycji czy innego rodzaju awarii, to odporność na awarie wzrasta wraz ze spadkiem modelu spójności, gdyż wystarczy nam, że żyje jeden host i jednocześnie czas generuje pewne dane. Ostateczna spójność w ogóle nie gwarantuje niczego w odniesieniu do danych — może to być wszystko.
  • Anomalie. Jednocześnie oczywiście wzrasta liczba anomalii. W Silnej Spójności prawie w ogóle nie powinny istnieć, ale w Ostatecznej Spójności mogą być czymkolwiek. Powstaje pytanie: dlaczego ludzie wybierają spójność ostateczną, jeśli zawiera ona anomalie? Odpowiedź jest taka, że ​​można zastosować modele spójności ostatecznej i na przykład w krótkim okresie czasu występują anomalie; istnieje możliwość wykorzystania kreatora do odczytu i mniej więcej spójnych danych; Często możliwe jest zastosowanie modeli o silnej spójności. W praktyce to działa, a często liczba anomalii jest ograniczona w czasie.

Twierdzenie CAP-a

Kiedy widzisz słowa konsystencja, dostępność – co przychodzi Ci na myśl? Zgadza się – twierdzenie CAP! Teraz chcę rozwiać ten mit... To nie ja, to Martin Kleppmann, który napisał wspaniały artykuł, wspaniałą książkę.

HighLoad++, Mikhail Tyulenev (MongoDB): Spójność przyczynowa: od teorii do praktyki

Twierdzenie CAP to zasada sformułowana w pierwszej dekadzie XXI wieku, która głosi, że spójność, dostępność, podziały: weź dowolne dwa i nie możesz wybrać trzech. To była pewna zasada. Zostało to udowodnione jako twierdzenie kilka lat później przez Gilberta i Lyncha. Potem zaczęto to traktować jak mantrę – systemy zaczęto dzielić na CA, CP, AP i tak dalej.

Twierdzenie to faktycznie zostało udowodnione dla następujących przypadków... Po pierwsze, dostępność nie była rozważana jako wartość ciągła od zera do setek (0 - system jest „martwy”, 100 – reaguje szybko; jesteśmy przyzwyczajeni do tak rozpatrywania tego problemu) , ale jako właściwość algorytmu, która gwarantuje, że przy wszystkich jego wykonaniach zwraca dane.

Nie ma ani słowa o czasie reakcji! Istnieje algorytm, który zwraca dane po 100 latach - absolutnie wspaniały dostępny algorytm, który jest częścią twierdzenia CAP.
Po drugie: twierdzenie zostało udowodnione dla zmian wartości tego samego klucza, mimo że zmiany te są skalowalne. Oznacza to, że w rzeczywistości praktycznie się ich nie stosuje, gdyż modele różnią się: Konsystencją ostateczną, Konsystencją silną (być może).

Po co to wszystko? Co więcej, twierdzenie CAP dokładnie w takiej formie, w jakiej zostało udowodnione, praktycznie nie ma zastosowania i jest rzadko stosowane. W formie teoretycznej w jakiś sposób wszystko ogranicza. Okazuje się, że pewna zasada jest intuicyjnie poprawna, ale ogólnie nie została udowodniona.

Spójność przyczynowa jest najsilniejszym modelem

To, co się teraz dzieje, polega na tym, że możesz uzyskać wszystkie trzy rzeczy: spójność, dostępność za pomocą partycji. W szczególności spójność przyczynowa jest najsilniejszym modelem spójności, który nadal działa w obecności partycji (przerw w sieci). Dlatego cieszy się tak dużym zainteresowaniem i dlatego się nim zajęliśmy.

HighLoad++, Mikhail Tyulenev (MongoDB): Spójność przyczynowa: od teorii do praktyki

Po pierwsze, ułatwia pracę twórcom aplikacji. W szczególności obecność doskonałego wsparcia ze strony serwera: gdy wszystkie rekordy występujące u jednego klienta mają gwarancję, że dotrą w tej samej kolejności na innego klienta. Po drugie, wytrzymuje przegrody.

Kuchnia wewnętrzna MongoDB

Pamiętając, że jest obiad, ruszamy do kuchni. Opowiem o modelu systemu, czyli czym jest MongoDB dla tych, którzy pierwszy raz słyszą o takiej bazie danych.

HighLoad++, Mikhail Tyulenev (MongoDB): Spójność przyczynowa: od teorii do praktyki

HighLoad++, Mikhail Tyulenev (MongoDB): Spójność przyczynowa: od teorii do praktyki

MongoDB (zwana dalej „MongoDB”) jest systemem rozproszonym obsługującym skalowanie poziome, czyli sharding; w każdym fragmencie obsługuje także redundancję danych, czyli replikację.

Sharding w MongoDB (nie relacyjnej bazie danych) dokonuje automatycznego równoważenia, czyli każdy zbiór dokumentów (lub „tabela” w kontekście danych relacyjnych) jest dzielony na części, a serwer automatycznie przenosi je pomiędzy shardami.

Router zapytań, który dystrybuuje żądania dla klienta, to klient, przez który działa. Wie już, gdzie i jakie dane się znajdują, i kieruje wszystkie żądania do odpowiedniego sharda.

Kolejna ważna kwestia: MongoDB to pojedynczy master. Jest jeden Podstawowy - może przyjmować rekordy obsługujące klucze, które zawiera. Nie można wykonać zapisu Multi-Master.

Wydaliśmy wersję 4.2 - pojawiły się tam nowe ciekawe rzeczy. W szczególności wstawili Lucene - search - czyli plik wykonywalny Java bezpośrednio do Mongo i tam stało się możliwe wyszukiwanie poprzez Lucene, tak samo jak w Elastica.

I stworzyli nowy produkt - Wykresy, jest on również dostępny w Atlasie (własna chmura Mongo). Mają darmowy poziom – możesz się nim bawić. Bardzo spodobało mi się Wykresy - wizualizacja danych, bardzo intuicyjna.

Składniki Konsystencja przyczynowa

Naliczyłem około 230 artykułów, które opublikowano na ten temat – autorstwa Lesliego Lamperta. Teraz, z mojej pamięci, przekażę Państwu niektóre fragmenty tych materiałów.

HighLoad++, Mikhail Tyulenev (MongoDB): Spójność przyczynowa: od teorii do praktyki

Wszystko zaczęło się od artykułu Lesliego Lamperta, który powstał w latach 1970. XX wieku. Jak widać, pewne badania na ten temat wciąż trwają. Obecnie spójność przyczynowa cieszy się zainteresowaniem w związku z rozwojem systemów rozproszonych.

Ograniczenia

Jakie istnieją ograniczenia? Jest to właściwie jeden z głównych punktów, ponieważ ograniczenia narzucane przez system produkcyjny bardzo różnią się od ograniczeń istniejących w artykułach akademickich. Często są dość sztuczne.

HighLoad++, Mikhail Tyulenev (MongoDB): Spójność przyczynowa: od teorii do praktyki

  • Po pierwsze, „MongoDB” jest pojedynczym masterem, jak już powiedziałem (to znacznie upraszcza).
  • Uważamy, że system powinien obsłużyć około 10 tysięcy shardów. Nie możemy podejmować żadnych decyzji architektonicznych, które jednoznacznie ograniczałyby tę wartość.
  • Mamy chmurę, ale zakładamy, że dana osoba nadal powinna mieć taką możliwość, gdy pobierze plik binarny, uruchomi go na swoim laptopie i wszystko będzie działać świetnie.
  • Zakładamy coś, co Research rzadko zakłada: klienci zewnętrzni mogą robić, co chcą. MongoDB jest oprogramowaniem typu open source. W związku z tym klienci mogą być tak mądrzy i wściekli - mogą chcieć wszystko zepsuć. Spekulujemy, że bizantyjskie Feilory mogą pochodzić.
  • W przypadku klientów zewnętrznych znajdujących się poza obwodem istnieje ważne ograniczenie: jeśli ta funkcja jest wyłączona, nie należy obserwować żadnego pogorszenia wydajności.
  • Kolejna kwestia jest ogólnie antyakademicka: kompatybilność poprzednich i przyszłych wersji. Stare sterowniki muszą obsługiwać nowe aktualizacje, a baza danych musi obsługiwać stare sterowniki.

Ogólnie rzecz biorąc, wszystko to nakłada ograniczenia.

Składniki spójności przyczynowej

Opowiem teraz o niektórych elementach. Jeśli ogólnie rozważymy spójność przyczynową, możemy wybrać bloki. Wybraliśmy z prac należących do pewnego bloku: Śledzenie zależności, dobór zegarów, w jaki sposób te zegary mogą być ze sobą synchronizowane oraz jak zapewniamy bezpieczeństwo – to mniej więcej to, o czym będę mówił:

HighLoad++, Mikhail Tyulenev (MongoDB): Spójność przyczynowa: od teorii do praktyki

Pełne śledzenie zależności

Dlaczego jest to potrzebne? Aby podczas replikacji danych każdy rekord, każda zmiana danych zawierała informację o tym, od jakich zmian zależy. Pierwsza i naiwna zmiana polega na tym, że każda wiadomość zawierająca rekord zawiera informacje o poprzednich wiadomościach:

HighLoad++, Mikhail Tyulenev (MongoDB): Spójność przyczynowa: od teorii do praktyki

W tym przykładzie liczba w nawiasach klamrowych to numery rekordów. Czasami te rekordy z wartościami są przenoszone nawet w całości, czasami przenoszone są niektóre wersje. Najważniejsze jest to, że każda zmiana zawiera informacje o poprzedniej (oczywiście niesie to wszystko w sobie).

Dlaczego zdecydowaliśmy się nie stosować tego podejścia (pełne śledzenie)? Oczywiście, ponieważ takie podejście jest niepraktyczne: każda zmiana w sieci społecznościowej zależy od wszystkich poprzednich zmian w tej sieci społecznościowej, przenoszenia, powiedzmy, Facebooka lub VKontakte przy każdej aktualizacji. Niemniej jednak istnieje wiele badań nad pełnym śledzeniem zależności – są to sieci przedspołecznościowe; w niektórych sytuacjach to naprawdę działa.

Jawne śledzenie zależności

Następny jest bardziej ograniczony. Uwzględnia się tutaj również przekazywanie informacji, ale tylko to, co jest wyraźnie zależne. Co zależy od tego, co z reguły określa Aplikacja. Kiedy dane są replikowane, zapytanie zwraca odpowiedzi tylko wtedy, gdy poprzednie zależności zostały spełnione, czyli pokazane. To jest istota działania spójności przyczynowej.

HighLoad++, Mikhail Tyulenev (MongoDB): Spójność przyczynowa: od teorii do praktyki

Widzi, że rekord 5 zależy od rekordów 1, 2, 3, 4 – w związku z tym czeka, aż klient będzie miał dostęp do zmian wprowadzonych decyzją dostępową Penny, gdy wszystkie poprzednie zmiany przeszły już przez bazę danych.

To też nam nie odpowiada, ponieważ informacji jest wciąż za dużo, a to spowolni proces. Jest inne podejście...

Zegar Lamporta

Są bardzo starzy. Zegar Lamporta oznacza, że ​​te zależności są złożone w funkcję skalarną zwaną Zegarem Lamporta.

Funkcja skalarna jest pewną liczbą abstrakcyjną. Często nazywany jest czasem logicznym. Z każdym wydarzeniem licznik ten rośnie. Licznik, który jest aktualnie znany procesowi, wysyła każdą wiadomość. Oczywiste jest, że procesy mogą nie być zsynchronizowane, mogą mieć zupełnie inne czasy. Niemniej jednak system w jakiś sposób równoważy zegar z takimi komunikatami. Co się dzieje w tym przypadku?

Aby było jasne, podzieliłem ten duży fragment na dwie części: znajomi mogą znajdować się w jednym węźle, który zawiera fragment kolekcji, a kanał może znajdować się w innym węźle, który zawiera fragment tej kolekcji. Czy jest jasne, w jaki sposób mogą wymknąć się spod kontroli? Najpierw kanał powie: „Replikowano”, a następnie Przyjaciele. Jeśli system nie zapewni jakiejś gwarancji, że Feed nie zostanie wyświetlony, dopóki nie zostaną dostarczone także zależności Friends w kolekcji Friends, to będziemy mieli dokładnie taką sytuację, o której wspomniałem.

Widzisz, jak licznik czasu w Feed logicznie wzrasta:

HighLoad++, Mikhail Tyulenev (MongoDB): Spójność przyczynowa: od teorii do praktyki

Zatem główna właściwość tego Zegara Lamporta i spójności przyczynowej (wyjaśniona za pomocą Zegara Lamporta) jest następująca: jeśli mamy zdarzenia A i B, a zdarzenie B zależy od zdarzenia A*, to wynika z tego, że czas logiczny zdarzenia A jest mniejszy niż LogicalTime ze zdarzenia B.

* Czasem mówi się też, że A wydarzyło się przed B, czyli A wydarzyło się przed B – jest to pewna relacja, która częściowo porządkuje cały zespół zdarzeń, który miał miejsce w ogóle.

Coś przeciwnego jest nieprawidłowe. To właściwie jedna z głównych wad zegara Lamport – częściowy porządek. Istnieje koncepcja zdarzeń równoczesnych, to znaczy takich, w których ani (A nie wydarzyło się przed B), ani (A nie wydarzyło się przed B). Przykładem może być równoległe dodanie przez Leonarda kogoś innego jako przyjaciela (nawet nie Leonarda, ale na przykład Sheldona).
Jest to właściwość często wykorzystywana podczas pracy z zegarami Lamporta: zwracają oni szczególną uwagę na funkcję i na tej podstawie wyciągają wniosek, że być może te zdarzenia są zależne. Ponieważ jeden ze sposobów jest prawdziwy: jeśli czas logiczny A jest mniejszy niż czas logiczny B, wówczas B nie może nastąpić przed A; a jeśli więcej, to może.

Zegar wektorowy

Logicznym rozwinięciem zegara Lamporta jest zegar wektorowy. Różnią się tym, że każdy znajdujący się tutaj węzeł ma swój własny, oddzielny zegar i jest przesyłany jako wektor.
W tym przypadku widzisz, że zerowy indeks wektora odpowiada za Feed, a pierwszy indeks wektora za Friends (każdy z tych węzłów). A teraz wzrosną: zerowy indeks „Feed” wzrasta podczas pisania – 1, 2, 3:

HighLoad++, Mikhail Tyulenev (MongoDB): Spójność przyczynowa: od teorii do praktyki

Dlaczego zegar wektorowy jest lepszy? Ponieważ pozwalają dowiedzieć się, które zdarzenia są jednoczesne i kiedy występują w różnych węzłach. Jest to bardzo ważne w przypadku systemu fragmentowania, takiego jak MongoDB. Tego jednak nie wybraliśmy, choć jest to cudowna rzecz, a do tego świetnie się sprawdza, i pewnie by nam pasowało...

Jeśli mamy 10 tysięcy shardów, to 10 tysięcy składowych nie jesteśmy w stanie przenieść, nawet jeśli to skompresujemy lub wymyślimy coś innego – ładunek i tak będzie kilkukrotnie mniejszy od objętości tego całego wektora. Dlatego z zaciśniętym sercem i zębami porzuciliśmy to podejście i przeszliśmy do innego.

Klucz TrueTime. Zegar atomowy

Powiedziałem, że będzie historia o Spannerze. To fajna rzecz, rodem z XXI wieku: zegary atomowe, synchronizacja GPS.

Jaki jest pomysł? „Spanner” to system Google, który od niedawna stał się nawet dostępny dla ludzi (dodali do niego SQL). Każda transakcja ma tam jakiś znacznik czasu. Ponieważ czas jest zsynchronizowany*, każdemu zdarzeniu można przypisać konkretny czas – zegary atomowe mają czas oczekiwania, po którym „nadchodzi” inny czas.

HighLoad++, Mikhail Tyulenev (MongoDB): Spójność przyczynowa: od teorii do praktyki

Zatem po prostu zapisując dane w bazie danych i czekając przez pewien okres czasu, automatycznie gwarantuje się możliwość serializacji zdarzenia. Mają najsilniejszy model Spójności, jaki w zasadzie można sobie wyobrazić - jest to Spójność Zewnętrzna.

* To jest główny problem zegarów Lamparta – w systemach rozproszonych nigdy nie są one synchroniczne. Mogą się różnić; nawet w przypadku NTP nadal nie działają zbyt dobrze. „Spanner” ma zegar atomowy, a synchronizacja trwa, jak się wydaje, mikrosekundy.

Dlaczego nie wybraliśmy? Nie zakładamy, że nasi użytkownicy mają wbudowany zegar atomowy. Kiedy się pojawią, będą wbudowane w każdy laptop, będzie jakaś super fajna synchronizacja GPS - wtedy tak... Ale na razie najlepsze, co jest możliwe, to Amazon, stacje bazowe - dla fanatyków... Więc użyliśmy innych zegarków .

Zegar hybrydowy

To właśnie jest zaznaczane w MongoDB przy zapewnianiu spójności przyczynowej. W jaki sposób są hybrydowe? Hybryda jest wartością skalarną, ale ma dwa składniki:

HighLoad++, Mikhail Tyulenev (MongoDB): Spójność przyczynowa: od teorii do praktyki

  • Pierwsza to epoka Uniksa (ile sekund minęło od „początku świata komputerów”).
  • Drugi to pewien przyrost, także 32-bitowa liczba całkowita bez znaku.

Właściwie to wszystko. Jest takie podejście: część odpowiedzialna za czas jest cały czas zsynchronizowana z zegarem; za każdym razem, gdy następuje aktualizacja, ta część jest synchronizowana z zegarem i okazuje się, że czas jest zawsze mniej więcej poprawny, a przyrost pozwala rozróżnić zdarzenia, które miały miejsce w tym samym momencie.

Dlaczego jest to ważne dla MongoDB? Ponieważ pozwala na wykonanie pewnego rodzaju restauracji rezerwowych w określonym momencie, czyli wydarzenie jest indeksowane czasowo. Jest to ważne, gdy potrzebne są określone zdarzenia; W przypadku bazy danych zdarzenia to zmiany w bazie danych, które wystąpiły w określonych odstępach czasu.

Najważniejszy powód podam tylko Tobie (proszę, nikomu nie mów)! Zrobiliśmy to, ponieważ tak wyglądają zorganizowane, indeksowane dane w MongoDB OpLog. OpLog to struktura danych, która zawiera absolutnie wszystkie zmiany w bazie danych: najpierw trafiają one do OpLog, a następnie są stosowane do samego Storage w przypadku, gdy jest to replikowana data lub shard.

To był główny powód. Jednak istnieją również praktyczne wymagania dotyczące tworzenia bazy danych, co oznacza, że ​​powinna ona być prosta - mało kodu, jak najmniej uszkodzonych elementów, które trzeba przepisać i przetestować. Fakt, że nasze oplogi były indeksowane przez zegary hybrydowe bardzo pomógł i pozwolił nam dokonać właściwego wyboru. To naprawdę się opłaciło i w jakiś magiczny sposób zadziałało już przy pierwszym prototypie. To było bardzo fajne!

Synchronizacja zegara

W literaturze naukowej opisano kilka metod synchronizacji. Mówię o synchronizacji, gdy mamy dwa różne fragmenty. Jeśli jest jeden zestaw replik, nie ma potrzeby żadnej synchronizacji: jest to „pojedynczy zestaw replik”; mamy OpLog, w którym wpadają wszystkie zmiany - w tym przypadku wszystko jest już po kolei uporządkowane w samym „Oplogu”. Ale jeśli mamy dwa różne shardy, ważna jest tutaj synchronizacja czasu. Tutaj zegar wektorowy pomógł bardziej! Ale my ich nie mamy.

HighLoad++, Mikhail Tyulenev (MongoDB): Spójność przyczynowa: od teorii do praktyki

Drugi jest odpowiedni - to „Heartbeats”. Istnieje możliwość wymiany niektórych sygnałów występujących w każdej jednostce czasu. Ale pulsy są zbyt wolne, nie możemy zapewnić opóźnienia naszemu klientowi.

Prawdziwy czas to oczywiście cudowna rzecz. Ale to znowu chyba przyszłość... Choć w Atlasie da się to już zrobić, to już są szybkie synchronizatory czasu „Amazonka”. Ale nie będzie ona dostępna dla wszystkich.

Plotkowanie ma miejsce wtedy, gdy wszystkie wiadomości zawierają czas. To mniej więcej to, czego używamy. Każda wiadomość między węzłami, sterownik, router węzła danych, absolutnie wszystko dla MongoDB jest pewnego rodzaju elementem, komponentem bazy danych, który zawiera działający zegar. Wszędzie mają znaczenie czasu hybrydowego, jest on przekazywany. 64 bity? To pozwala, jest to możliwe.

Jak to wszystko działa razem?

Tutaj patrzę na jeden zestaw replik, żeby było trochę łatwiej. Istnieją pierwotne i wtórne. Drugorzędny wykonuje replikację i nie zawsze jest całkowicie zsynchronizowany z Podstawowym.

Wstawienie następuje do „Podkładu” z określoną wartością czasu. Ta wkładka zwiększa liczbę wewnętrzną o 11, jeśli jest to maksimum. Lub sprawdzi wartości zegara i zsynchronizuje się z zegarem, jeśli wartości zegara są większe. Pozwala to organizować według czasu.

Po dokonaniu nagrania następuje ważny moment. Zegar znajduje się w „MongoDB” i jest zwiększany tylko w przypadku zapisu do „Oplog”. Jest to zdarzenie zmieniające stan systemu. W absolutnie wszystkich klasycznych artykułach za zdarzenie uważa się moment, w którym wiadomość trafi do węzła: wiadomość dotarła, co oznacza, że ​​system zmienił swój stan.

Wynika to z faktu, że w trakcie badań nie jest do końca jasne, jak ten przekaz zostanie zinterpretowany. Wiemy na pewno, że jeśli nie znajdzie to odzwierciedlenia w „Oplogu”, to nie zostanie w żaden sposób zinterpretowane, a zmiana stanu systemu będzie jedynie wpisem w „Oplogu”. To nam wszystko upraszcza: model to upraszcza i pozwala nam to uporządkować w ramach jednego zestawu replik i wielu innych przydatnych rzeczy.

Zwracana jest wartość już wpisana do „Oplogu” – wiemy, że „Oplog” już zawiera tę wartość, a jego czas wynosi 12. Teraz, powiedzmy, odczyt rozpoczyna się od innego węzła (Secondary) i przesyła poClusterTime w wiadomość. Mówi: „Potrzebuję wszystkiego, co wydarzyło się przynajmniej po 12 lub w ciągu dwunastej” (patrz obrazek powyżej).

Nazywa się to przyczynowością spójną (CAT). W teorii istnieje takie pojęcie, że jest to jakiś wycinek czasu, który sam w sobie jest spójny. W tym przypadku można powiedzieć, że jest to stan układu jaki zaobserwowano w chwili 12.

Teraz nic tu jeszcze nie ma, ponieważ ten rodzaj symuluje sytuację, gdy potrzebujesz Drugorzędnego do replikowania danych z Podstawowego. Czeka... I teraz dane dotarły - zwraca te wartości z powrotem.

HighLoad++, Mikhail Tyulenev (MongoDB): Spójność przyczynowa: od teorii do praktyki

Mniej więcej tak to wszystko działa. Prawie.

Co oznacza „prawie”? Załóżmy, że jest ktoś, kto przeczytał i zrozumiał, jak to wszystko działa. Zdałem sobie sprawę, że za każdym razem, gdy pojawia się ClusterTime, aktualizuje wewnętrzny zegar logiczny, a następnie następny wpis zwiększa się o jeden. Ta funkcja zajmuje 20 linii. Załóżmy, że ta osoba przesyła największą liczbę 64-bitową minus jeden.

Dlaczego „minus jeden”? Ponieważ do tej wartości zostanie podstawiony zegar wewnętrzny (oczywiście jest to największa możliwa i większa od czasu aktualnego), wówczas nastąpi wpis w „Oplogu”, a zegar zostanie podniesiony o inną jednostkę - i już będzie będzie wartością maksymalną (są po prostu wszystkie jednostki, nie ma dokąd pójść), unsaint int).

Oczywiste jest, że po tym system staje się całkowicie niedostępny. Można go jedynie rozładować i wyczyścić - dużo pracy ręcznej. Pełna dostępność:

HighLoad++, Mikhail Tyulenev (MongoDB): Spójność przyczynowa: od teorii do praktyki

Co więcej, jeśli zostanie to powtórzone gdzie indziej, wówczas cała gromada po prostu upadnie. Absolutnie niedopuszczalna sytuacja, którą każdy może zorganizować bardzo szybko i łatwo! Dlatego uznaliśmy ten moment za jeden z najważniejszych. Jak temu zapobiec?

Naszym sposobem jest podpisanie klastraTime

Tak jest to przekazywane w wiadomości (przed niebieskim tekstem). Ale zaczęliśmy także generować podpis (niebieski tekst):

HighLoad++, Mikhail Tyulenev (MongoDB): Spójność przyczynowa: od teorii do praktyki

Podpis jest generowany za pomocą klucza przechowywanego w bazie danych, w bezpiecznym miejscu; sam w sobie jest generowany i aktualizowany (użytkownicy nic na ten temat nie widzą). Generowany jest skrót, a każda wiadomość jest podpisana podczas tworzenia i sprawdzana po otrzymaniu.
W umysłach ludzi prawdopodobnie pojawia się pytanie: „Jak bardzo to spowalnia pracę?” Mówiłem, że powinno działać szybko, zwłaszcza przy braku tej funkcji.

Co oznacza w tym przypadku użycie spójności przyczynowej? Ma to na celu pokazanie parametru afterClusterTime. Bez tego i tak po prostu przekaże wartości. Plotki, począwszy od wersji 3.6, zawsze działają.

Jeśli zostawimy ciągłe generowanie podpisów, spowolni to system nawet w przypadku braku funkcji, która nie spełnia naszych podejść i wymagań. Co więc zrobiliśmy?

Zrób to szybko!

To dość prosta rzecz, ale trik jest ciekawy – podzielę się nim, może kogoś zainteresuje.
Mamy skrót, który przechowuje podpisane dane. Wszystkie dane przechodzą przez pamięć podręczną. Pamięć podręczna nie podpisuje określonego czasu, ale zakres. Kiedy nadejdzie jakaś wartość, generujemy Range, maskujemy ostatnie 16 bitów i podpisujemy tę wartość:

HighLoad++, Mikhail Tyulenev (MongoDB): Spójność przyczynowa: od teorii do praktyki

Otrzymując taki podpis, przyspieszamy system (względnie) 65 tysięcy razy. Działa świetnie: kiedy przeprowadzaliśmy eksperymenty, czas faktycznie skrócił się 10 tysięcy razy, gdy mieliśmy aktualizację sekwencyjną. Oczywiste jest, że gdy są ze sobą sprzeczne, to nie działa. Ale w większości praktycznych przypadków to działa. Połączenie podpisu Range z podpisem rozwiązało problem bezpieczeństwa.

Czego się nauczyliśmy?

Wnioski, jakie z tego wyciągnęliśmy:

  • Musimy czytać materiały, historie, artykuły, bo mamy mnóstwo ciekawych rzeczy. Kiedy pracujemy nad jakąś funkcją (szczególnie teraz, kiedy przeprowadzaliśmy transakcje itp.), musimy przeczytać i zrozumieć. Zajmuje to trochę czasu, ale jest naprawdę bardzo przydatne, ponieważ wyjaśnia, gdzie jesteśmy. Wydawało się, że nie wymyśliliśmy nic nowego – po prostu wzięliśmy składniki.

    Generalnie jest pewna różnica w myśleniu, kiedy jest konferencja naukowa (np. Sigmon) – wszyscy skupiają się na nowych pomysłach. Co nowego w naszym algorytmie? Nie ma tu nic szczególnie nowego. Nowość polega raczej na sposobie, w jaki połączyliśmy istniejące podejścia. Dlatego pierwszą rzeczą jest przeczytanie klasyki, zaczynając od Lamparta.

  • W produkcji wymagania są zupełnie inne. Jestem pewien, że wielu z Was nie ma do czynienia z „sferycznymi” bazami danych w abstrakcyjnej próżni, ale z normalnymi, rzeczywistymi rzeczami, które mają problemy z dostępnością, opóźnieniami i odpornością na błędy.
  • Ostatnią rzeczą jest to, że musieliśmy przyjrzeć się różnym pomysłom i połączyć kilka zupełnie różnych artykułów w jedno podejście, razem. Pomysł na przykład podpisania wziął się ogólnie z artykułu, w którym rozważano protokół Paxos, który w przypadku niebizantyjskich nieudaczników znajduje się w protokole autoryzacji, w przypadku bizantyjskich – poza protokołem autoryzacyjnym... Ogólnie rzecz biorąc, właśnie o to nam chodzi skończyło się na zrobieniu.

    Nie ma tu absolutnie nic nowego! Ale skoro już to wszystko wymieszaliśmy... To jakby powiedzieć, że przepis na sałatkę Oliviera to bzdura, bo jajka, majonez i ogórki już wymyślono... To mniej więcej ta sama historia.

HighLoad++, Mikhail Tyulenev (MongoDB): Spójność przyczynowa: od teorii do praktyki

Skończę z tym. Dziękuję!

pytania

Pytanie od publiczności (zwane dalej B): – Dziękuję, Michaił, za raport! Ciekawy jest temat czasu. Używasz plotek. Mówili, że każdy ma swój czas, każdy zna swój czas lokalny. Jak rozumiem, mamy sterownik - może być wielu klientów ze sterownikami, planistów zapytań też, shardów... A do czego sprowadza się system, jeśli nagle pojawia się rozbieżność: ktoś decyduje, że to dla minuta do przodu, ktoś minutę do tyłu? Gdzie skończymy?

MT: – Rzeczywiście świetne pytanie! Chciałem tylko porozmawiać o odłamkach. Jeśli dobrze rozumiem pytanie, to mamy następującą sytuację: jest shard 1 i shard 2, odczyt następuje z tych dwóch shardów - mają one rozbieżność, nie oddziałują ze sobą, ponieważ czas, który znają, jest inny, szczególnie czas, w którym istnieją w oplogach.
Załóżmy, że fragment 1 utworzył milion rekordów, fragment 2 nie zrobił nic, a żądanie dotarło do dwóch fragmentów. A pierwszy z nich ma afterClusterTime ponad milion. W takiej sytuacji, jak wyjaśniłem, shard 2 w ogóle nie zareaguje.

W: – Chciałem wiedzieć, jak się synchronizują i wybierają jeden logiczny czas?

MT: - Bardzo łatwa synchronizacja. Shard, gdy przychodzi do niego afterClusterTime i nie znajduje czasu w „Oplogu”, inicjuje brak akceptacji. Oznacza to, że podnosi swój czas rękami do tej wartości. Oznacza to, że nie ma żadnych zdarzeń pasujących do tego żądania. Tworzy to wydarzenie sztucznie i w ten sposób staje się spójny przyczynowo.

W: – A co jeśli po tym dotrą do niego jakieś inne zdarzenia, które zaginęły gdzieś w sieci?

MT: – Shard jest zaprojektowany w taki sposób, że nie pojawi się ponownie, ponieważ jest to pojedynczy mistrz. Jeśli już się zapisał, to nie przyjdą, ale przyjdą później. Nie może się zdarzyć, że coś gdzieś utknie, potem nie napisze, a potem nadejdą te zdarzenia - i spójność przyczynowa zostanie zerwana. Jeśli nie napisze, wszyscy powinni przyjść następni (będzie na nich czekać).

HighLoad++, Mikhail Tyulenev (MongoDB): Spójność przyczynowa: od teorii do praktyki

W: – Mam kilka pytań odnośnie kolejek. Spójność przyczynowa zakłada, że ​​istnieje określona kolejka działań, które należy wykonać. Co się stanie, jeśli zniknie jedna z naszych paczek? Nadchodzi 10, 11... 12 zniknęło, a wszyscy inni czekają, aż się spełni. I nagle zgasł nam samochód, nie możemy nic zrobić. Czy istnieje maksymalna długość kolejki, która gromadzi się przed wykonaniem? Jaka fatalna awaria ma miejsce, gdy którykolwiek stan zostanie utracony? Co więcej, jeśli zapiszemy, że jest jakiś poprzedni stan, to czy powinniśmy jakoś od tego zacząć? Ale go nie odepchnęli!

MT: – To także świetne pytanie! Co my robimy? MongoDB ma koncepcję kworum pisze, kworum czyta. W jakich przypadkach wiadomość może zostać utracona? Kiedy zapis nie stanowi kworum lub gdy odczyt nie stanowi kworum (mogą się również przykleić jakieś śmieci).
W odniesieniu do spójności przyczynowej przeprowadzono duży test eksperymentalny, w wyniku którego w przypadku braku kworum przy zapisach i odczytach dochodzi do naruszeń spójności przyczynowej. Dokładnie to, co mówisz!

Nasza rada: podczas korzystania ze spójności przyczynowej korzystaj przynajmniej z odczytu kworum. W takim przypadku nic nie zostanie utracone, nawet jeśli zostanie utracony zapis kworum... Jest to sytuacja ortogonalna: jeśli użytkownik nie chce, aby dane zostały utracone, musi skorzystać z rekordu kworum. Spójność przyczynowa nie gwarantuje trwałości. Trwałość gwarantuje replikacja i maszyny związane z replikacją.

W: – Kiedy tworzymy instancję, która wykonuje za nas sharding (odpowiednio nie master, ale slave), opiera się ona na czasie uniksowym własnej maszyny lub na czasie „master”; Czy synchronizuje się po raz pierwszy czy okresowo?

MT: – Wyjaśnię teraz. Shard (czyli partycja pozioma) – zawsze jest tam Podstawowy. Odłamek może mieć „mistrza” i mogą istnieć repliki. Ale fragment zawsze obsługuje nagrywanie, ponieważ musi obsługiwać jakąś domenę (fragment ma Podstawową).

W: – Czyli wszystko zależy wyłącznie od „mistrza”? Czy zawsze używany jest czas główny?

MT: - Tak. Można powiedzieć w przenośni: zegar tyka, gdy następuje wejście do „mastera”, do „Oplogu”.

W: – Mamy klienta, który łączy się i nie musi nic wiedzieć o czasie?

MT: – Nie musisz w ogóle nic wiedzieć! Jeśli mówimy o tym, jak to działa na kliencie: gdy klient chce zastosować spójność przyczynową, musi otworzyć sesję. Teraz wszystko jest na miejscu: transakcje w sesji i odzyskanie uprawnień... Sesja to uporządkowanie logicznych zdarzeń zachodzących u klienta.

Jeśli otworzy tę sesję i powie tam, że chce spójności przyczynowej (jeśli sesja domyślnie obsługuje spójność przyczynową), wszystko zadziała automatycznie. Sterownik zapamiętuje ten czas i zwiększa go w przypadku otrzymania nowej wiadomości. Zapamiętuje jaką odpowiedź zwróciła poprzednia z serwera, który zwrócił dane. Następne żądanie będzie zawierać afterCluster("czas większy niż ten").

Klient nie musi wiedzieć absolutnie nic! Jest to dla niego całkowicie nieprzejrzyste. Jeśli ludzie korzystają z tych funkcji, co mogą zrobić? Po pierwsze, możesz bezpiecznie czytać strony wtórne: możesz pisać do bazy podstawowej i czytać z replikowanych geograficznie jednostek pomocniczych i mieć pewność, że to działa. Jednocześnie sesje, które zostały nagrane na Podstawowym, można nawet przenieść na Dodatkowe, czyli możesz wykorzystać nie jedną sesję, a kilka.

W: – Nowa warstwa informatyki – typy danych CRDT (ang. Confused-free Replicated Data Types) – jest silnie powiązana z tematem spójności ostatecznej. Czy rozważałeś integrację tego typu danych z bazą danych i co możesz o tym powiedzieć?

MT: - Dobre pytanie! CRDT ma sens w przypadku konfliktów zapisu: w MongoDB pojedynczy master.

W: – Mam pytanie od devopsów. Czy w prawdziwym świecie zdarzają się takie jezuickie sytuacje, kiedy dochodzi do awarii bizantyjskiej, a źli ludzie wewnątrz chronionego obwodu zaczynają wdzierać się do protokołu, w specjalny sposób wysyłać paczki rzemieślnicze?

HighLoad++, Mikhail Tyulenev (MongoDB): Spójność przyczynowa: od teorii do praktyki

MT: – Źli ludzie na obrzeżach są jak koń trojański! Źli ludzie wewnątrz obwodu mogą zrobić wiele złych rzeczy.

W: – Jasne jest, że pozostawienie, z grubsza mówiąc, dziury w serwerze, przez którą można wrzucić zoo słoni i zawalić na zawsze całe skupisko… Ręczne odzyskanie zajmie trochę czasu… To, delikatnie mówiąc, jest zło. Z drugiej strony to ciekawe: w prawdziwym życiu, w praktyce zdarzają się sytuacje, w których naturalnie dochodzi do podobnych ataków wewnętrznych?

MT: – Ponieważ rzadko spotykam się z naruszeniami bezpieczeństwa w prawdziwym życiu, nie jestem w stanie powiedzieć, czy one się zdarzają. Ale jeśli mówimy o filozofii rozwoju, myślimy w ten sposób: mamy obwód zapewniający chłopakom, którzy zajmują się ochroną - to jest zamek, mur; a wewnątrz obwodu możesz robić, co chcesz. Oczywiste jest, że istnieją użytkownicy, którzy mogą tylko przeglądać i są użytkownicy, którzy mogą usuwać katalog.

W zależności od praw, szkody, jakie mogą wyrządzić użytkownicy, mogą być spowodowane myszą lub słoniem. Oczywiste jest, że użytkownik z pełnymi uprawnieniami może w ogóle wszystko. Użytkownik z ograniczonymi prawami może wyrządzić znacznie mniej szkód. W szczególności nie może złamać systemu.

W: – Na chronionym obszarze ktoś próbował utworzyć nieoczekiwane protokoły dla serwera, aby całkowicie zniszczyć serwer, a jeśli masz szczęście, cały klaster... Czy kiedykolwiek jest tak „dobrze”?

MT: „Nigdy nie słyszałem o takich rzeczach”. To, że w ten sposób można zawiesić serwer, nie jest tajemnicą. Fałsz w środku, będąc z protokołu, będąc autoryzowanym użytkownikiem, który może napisać coś takiego w wiadomości... Właściwie to niemożliwe, bo i tak zostanie to zweryfikowane. Możliwe jest wyłączenie tego uwierzytelniania dla użytkowników, którzy tego nie chcą - to jest ich problem; oni, z grubsza rzecz biorąc, zniszczyli same ściany i można tam wepchnąć słonia, który zdepcze... Ale w sumie można przebrać się za mechanika, przyjść i wyciągnąć!

W: – Dziękuję za raport. Siergiej (Yandex). W Mong istnieje stała, która ogranicza liczbę członków z prawem głosu w zestawie replik i ta stała wynosi 7 (siedem). Dlaczego jest to stała? Dlaczego nie jest to jakiś parametr?

MT: – Mamy zestawy replik z 40 węzłami. Zawsze jest większość. Nie wiem, która wersja...

W: – W zestawie replik możesz uruchamiać członków bez prawa głosu, ale jest maksymalnie 7 członków z prawem głosu. Jak możemy przetrwać zamknięcie w tym przypadku, jeśli nasz zestaw replik jest rozproszony w 3 centrach danych? Jedno centrum danych może łatwo się wyłączyć, a inna maszyna może wypaść.

MT: – To już trochę wykracza poza zakres raportu. To jest pytanie ogólne. Może opowiem ci o tym później.

HighLoad++, Mikhail Tyulenev (MongoDB): Spójność przyczynowa: od teorii do praktyki

Kilka reklam 🙂

Dziękujemy za pobyt z nami. Podobają Ci się nasze artykuły? Chcesz zobaczyć więcej ciekawych treści? Wesprzyj nas składając zamówienie lub polecając znajomym, VPS w chmurze dla programistów od 4.99 USD, unikalny odpowiednik serwerów klasy podstawowej, który został przez nas wymyślony dla Ciebie: Cała prawda o VPS (KVM) E5-2697 v3 (6 rdzeni) 10GB DDR4 480GB SSD 1Gbps od 19$ czyli jak udostępnić serwer? (dostępne z RAID1 i RAID10, do 24 rdzeni i do 40 GB DDR4).

Dell R730xd 2 razy taniej w centrum danych Equinix Tier IV w Amsterdamie? Tylko tutaj 2 x Intel TetraDeca-Core Xeon 2x E5-2697v3 2.6 GHz 14C 64 GB DDR4 4x960 GB SSD 1 Gb/s 100 Telewizor od 199 USD w Holandii! Dell R420 — 2x E5-2430 2.2 GHz 6C 128 GB DDR3 2x960 GB SSD 1 Gb/s 100 TB — od 99 USD! Czytać o Jak zbudować firmę infrastrukturalną klasy z wykorzystaniem serwerów Dell R730xd E5-2650 v4 o wartości 9000 euro za grosz?

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

Dodaj komentarz