Pytania rekrutacyjne NoSQL

44 pytań — 20 odpowiedzi dostępnych od razu

Darmowe pytania rekrutacyjne NoSQL pomagają przygotować się do rozmowy o doborze modelu danych i systemach rozproszonych. Ten podgląd zawiera przykładowe pytania z pełnej puli 44 zagadnień.

Fiszki obejmują modele zorientowane na agregaty, grafy, brak sztywnego schematu, dystrybucję danych oraz twierdzenie CAP. Pozwalają przećwiczyć uzasadnianie wyboru rozwiązania dla konkretnego problemu.

Darmowy start

Pierwsze 20 odpowiedzi są dostępne od razu

Przeglądaj pełną listę 44 pytań. Pełne odpowiedzi, przykłady kodu i materiały do nauki odblokujesz w panelu.

Odblokuj 24 odpowiedzi

Wprowadzenie do NoSQL

Czym jest NoSQL i jakie cechy łączą bazy określane tym mianem?

NoSQL to nie jedna technologia ani jeden produkt, lecz luźna rodzina baz danych, które odeszły od dominującego przez dekady modelu relacyjnego. Sam termin powstał przypadkowo (jako hashtag na spotkanie społeczności) i nigdy nie doczekał się ścisłej definicji — opisuje raczej pewien ruch i zestaw wspólnych cech niż precyzyjną kategorię.

Bazy zaliczane do NoSQL zwykle mają kilka powtarzających się właściwości:

  • Nie używają modelu relacyjnego ani (w większości) języka SQL,
  • Dobrze działają na klastrach złożonych z wielu maszyn,
  • Najczęściej są open-source,
  • Powstały na potrzeby aplikacji webowych początku XXI wieku,
  • Są schemaless — nie wymagają zdefiniowania schematu z góry.

Warto rozumieć NoSQL jako poszerzenie palety opcji przechowywania danych, a nie zastąpienie baz relacyjnych. Te ostatnie wciąż pozostają rozsądnym domyślnym wyborem dla wielu projektów.

Na czym polega problem niedopasowania impedancji (impedance mismatch)?

Niedopasowanie impedancji to rozbieżność między relacyjnym modelem danych a strukturami danych w pamięci aplikacji. Model relacyjny organizuje dane w tabele i wiersze, gdzie wartości w wierszu muszą być proste — nie można zagnieździć w nich rekordu ani listy. Tymczasem struktury w pamięci programu bywają znacznie bogatsze: zawierają zagnieżdżone obiekty, listy i kolekcje.

Efekt jest taki, że aby zapisać złożoną strukturę z pamięci do bazy relacyjnej, trzeba ją „rozłożyć" na wiele wierszy w wielu tabelach, a przy odczycie z powrotem złożyć. To źródło ciągłej frustracji programistów.

Problem łagodzą frameworki mapowania obiektowo-relacyjnego (ORM), takie jak Hibernate, ale go nie usuwają — ORM bywa nieszczelną abstrakcją i potrafi sam stać się źródłem kłopotów, zwłaszcza gdy zaczyna cierpieć wydajność zapytań. Bazy zorientowane na agregaty atakują ten problem u źródła, pozwalając zapisać złożoną strukturę w jednym kawałku, tak jak istnieje ona w pamięci.

Czym różni się baza integracyjna od bazy aplikacyjnej?

To dwa różne sposoby myślenia o roli bazy danych.

Baza integracyjna (integration database) jest współdzielona przez wiele aplikacji, często tworzonych przez różne zespoły, które zapisują do niej swoje dane. Zaletą jest łatwa wymiana danych między aplikacjami. Wadą — schemat staje się bardzo złożony, bo musi zaspokoić potrzeby wszystkich, a każda zmiana wymaga koordynacji między zespołami. Baza nie może też ufać aplikacjom i sama musi pilnować integralności danych.

Baza aplikacyjna (application database) jest dostępna bezpośrednio tylko dla jednej aplikacji, zarządzanej przez jeden zespół. Tylko ten zespół musi znać strukturę bazy, więc znacznie łatwiej ją utrzymywać i rozwijać. Integracja z innymi systemami przenosi się na poziom interfejsów aplikacji (np. usług sieciowych po HTTP).

Wybór bazy aplikacyjnej daje większą swobodę w doborze technologii składowania danych — to właśnie to podejście otwiera drzwi do stosowania NoSQL. Bazy NoSQL traktuje się zwykle jako bazy aplikacyjne, nie integracyjne.

Dlaczego potrzeba skalowania na klastrach napędziła rozwój NoSQL?

Gdy rośnie ilość danych i ruchu, są dwa sposoby zwiększania mocy:

  • Skalowanie w górę (scale up) — kupno większej maszyny: więcej procesorów, pamięci, dysków. Problem w tym, że duże maszyny są coraz droższe i mają realne ograniczenia.
  • Skalowanie wszerz (scale out) — użycie wielu małych, tanich maszyn w klastrze. Wychodzi taniej przy dużej skali i jest odporniejsze: awaria jednej maszyny nie kładzie całości.

Kłopot polega na tym, że bazy relacyjne nie zostały zaprojektowane do działania na klastrach. Klastrowe rozwiązania relacyjne opierają się na współdzielonym podsystemie dyskowym, który sam staje się pojedynczym punktem awarii, a ręczne dzielenie bazy na fragmenty (sharding) odbiera referencyjną integralność, transakcje i zapytania między fragmentami. Do tego licencje komercyjnych baz relacyjnych liczone od serwera windują koszty na klastrze.

Firmy operujące na ogromną skalę (jak Google czy Amazon) opracowały więc własne rozwiązania zaprojektowane od początku z myślą o klastrach — i to one stały się inspiracją dla ruchu NoSQL.

Modele danych zorientowane na agregaty

Czym jest agregat w kontekście baz NoSQL?

Agregat to zbiór powiązanych danych, które traktujemy jako jedną całość — jednostkę odczytu, zapisu i zarządzania spójnością. Pojęcie pochodzi z Domain-Driven Design, gdzie agregat to grupa obiektów, którą chcemy traktować razem.

Przykładowo zamówienie wraz z jego pozycjami, adresem dostawy i danymi płatności może tworzyć jeden agregat. Zamiast rozbijać te dane na osobne tabele (jak w modelu relacyjnym), przechowuje się je razem jako złożoną strukturę, zwykle identyfikowaną przez klucz.

Agregaty mają dwie ważne konsekwencje:

  • Stanowią naturalną jednostkę dystrybucji — dane jednego agregatu trafiają na ten sam węzeł klastra, co ułatwia replikację i sharding,
  • Stanowią jednostkę atomowości — operacje na pojedynczym agregacie są atomowe, ale operacje obejmujące wiele agregatów już nie.

Bazy klucz-wartość, dokumentowe i kolumnowe są zorientowane na agregaty. Bazy relacyjne i grafowe nazywamy „agregatowo-nieświadomymi" (aggregate-ignorant).

flowchart LR
    subgraph "Model relacyjny (znormalizowany)"
        K[("Klienci")]
        Z[("Zamówienia")]
        P[("Pozycje")]
        A[("Adresy")]
        K --> Z
        Z --> P
        Z --> A
    end
    subgraph "Model agregatowy"
        AGG["Zamówienie #99<br/>+ pozycje<br/>+ adres dostawy<br/>+ płatność"]
    end

Jakie są główne kategorie baz NoSQL?

Wyróżnia się cztery podstawowe kategorie modeli danych w świecie NoSQL:

  • Klucz-wartość (key-value) — najprostszy model, działający jak tablica mieszająca. Wartość jest dla bazy nieprzejrzystym blobem; dostęp wyłącznie po kluczu.
  • Dokumentowe (document) — przechowują dokumenty (np. JSON, XML, BSON), których struktura jest dla bazy widoczna, co pozwala odpytywać po polach wewnątrz dokumentu i pobierać jego fragmenty.
  • Kolumnowe / rodzin kolumn (column-family) — dane to wiersze z kluczem, w których kolumny grupuje się w rodziny kolumn (column families) wczytywane razem. Można je traktować jako dwupoziomową mapę.
  • Grafowe (graph) — przechowują węzły i krawędzie z właściwościami; specjalizują się w danych o złożonych relacjach.

Pierwsze trzy są zorientowane na agregaty i powstały głównie z myślą o klastrach. Bazy grafowe są odmiennym przypadkiem — modelują małe rekordy z bogatymi połączeniami i zwykle działają na pojedynczym serwerze.

Czym różni się baza klucz-wartość od bazy dokumentowej?

Obie przechowują agregaty identyfikowane kluczem, ale różnią się tym, na ile baza „widzi" wnętrze agregatu.

Klucz-wartość Dokumentowa
Wartość jest nieprzejrzysta — baza nie zna jej struktury Struktura dokumentu jest dla bazy widoczna
Dostęp tylko przez wyszukanie po kluczu Można odpytywać po polach wewnątrz dokumentu
Trzeba pobrać cały agregat i przetworzyć po stronie klienta Można pobrać fragment agregatu
Pełna swoboda co do zawartości Baza narzuca dopuszczalne struktury i może budować indeksy po zawartości

W praktyce granica bywa rozmyta — np. niektóre bazy klucz-wartość pozwalają dodać metadane do indeksowania albo rozbić agregat na listy i zbiory, a w bazie dokumentowej można użyć pola identyfikatora do wyszukiwania w stylu klucz-wartość. Zaletą nieprzejrzystości jest swoboda zapisu czegokolwiek; zaletą przejrzystości — większa elastyczność dostępu.

Dlaczego bazy zorientowane na agregaty zwykle nie obsługują transakcji ACID obejmujących wiele agregatów?

W bazach relacyjnych transakcja ACID może objąć dowolną kombinację wierszy z dowolnych tabel — wszystko zatwierdza się albo wycofuje jako jedną operację. To wynika z tego, że model relacyjny nie ma pojęcia agregatu.

Bazy zorientowane na agregaty świadomie rezygnują z transakcji obejmujących wiele agregatów, bo agregat jest jednostką dystrybucji — jego dane mogą leżeć na jednym węźle, a inne agregaty na innych. Zapewnienie atomowości między rozproszonymi węzłami wymagałoby kosztownej koordynacji, która kłóci się z ideą sprawnego działania na klastrze. Dlatego gwarantują jedynie atomową manipulację pojedynczym agregatem.

Konsekwencja jest taka, że jeśli logika biznesowa wymaga atomowej zmiany kilku agregatów naraz, programista musi obsłużyć to sam w kodzie aplikacji — w tym ewentualną awarię w połowie operacji. To zresztą jeden z czynników, które bierze się pod uwagę przy wyznaczaniu granic agregatów: dobrze, gdy potrzeby atomowości mieszczą się w obrębie jednego agregatu. Warto dodać, że bazy grafowe i inne agregatowo-nieświadome zwykle obsługują transakcje ACID podobnie jak relacyjne.

Relacje, grafy i brak schematu

Jak bazy zorientowane na agregaty radzą sobie z relacjami między agregatami?

Agregaty świetnie grupują dane czytane razem, ale dane bywają powiązane na różne sposoby. Klient i jego zamówienia mogą czasem być czytane razem (wtedy pasują do jednego agregatu), a czasem zamówienia przetwarza się niezależnie (wtedy lepiej, by były osobnymi agregatami z jakąś relacją między nimi).

Najprostszy sposób połączenia to osadzenie identyfikatora jednego agregatu w drugim — np. ID klienta w zamówieniu. Działa to dobrze, ale baza pozostaje nieświadoma tej relacji. Dlatego wiele baz (nawet klucz-wartość) udostępnia mechanizmy ujawniające relacje: bazy dokumentowe mogą indeksować zawartość, a niektóre bazy klucz-wartość pozwalają zapisać informacje o powiązaniach w metadanych i „chodzić" po linkach.

Kluczowy problem dotyczy aktualizacji: skoro atomowość obejmuje tylko jeden agregat, operacje na wielu powiązanych agregatach stają się niewygodne i grożą niespójnością po częściowej awarii. Dlatego dane bogate w relacje bywają sygnałem, że lepiej sprawdzi się baza relacyjna lub grafowa — choć trzeba pamiętać, że bazy relacyjne też słabo radzą sobie z bardzo złożonymi relacjami, bo wielokrotne joiny szybko stają się trudne i wolne.

Co oznacza, że baza jest schemaless, i jakie to ma konsekwencje?

Baza schemaless (bezschematowa) nie wymaga zdefiniowania struktury danych z góry — można dodawać dowolne pola do rekordów bez wcześniejszej deklaracji. Daje to dużą swobodę: łatwo zmieniać sposób przechowywania danych w miarę poznawania projektu, łatwo obsługiwać dane niejednorodne, gdzie każdy rekord ma inny zestaw pól, bez tabel pełnych pustych kolumn czy sztucznych nazw typu „custom_field_6".

Pułapka polega na tym, że schemat tak naprawdę nie znika — przenosi się do kodu aplikacji. Program, który czyta dane, prawie zawsze zakłada, że pewne pola istnieją, nazywają się tak a nie inaczej i przechowują dane określonego typu. To jest schemat ukryty (implicit schema).

Wynikają z tego problemy:

  • Żeby zrozumieć, jakie dane są przechowywane, trzeba zaglądać do kodu aplikacji,
  • Baza nie zna schematu, więc nie może go użyć do optymalizacji ani do walidacji spójności,
  • Problem narasta, gdy wiele aplikacji pisanych przez różne osoby korzysta z tej samej bazy.

Dlatego dobrym pomysłem jest enkapsulacja dostępu do bazy w jednej aplikacji lub usłudze, albo wyraźne wydzielenie obszarów agregatu dla różnych aplikacji.

Czym są zmaterializowane widoki (materialized views) w bazach NoSQL?

Orientacja na agregaty ma wadę: gdy potrzebujemy danych ułożonych inaczej niż agregat (np. sprzedaż danego produktu w czasie, a dane mamy ułożone wg zamówień), musimy potencjalnie przejrzeć wiele agregatów. Z pomocą przychodzą zmaterializowane widoki — wcześniej obliczone i zapisane wyniki zapytań, które prezentują dane w innym układzie niż podstawowy agregat.

Są one znacznie ważniejsze w bazach NoSQL niż w relacyjnych, bo niemal każda aplikacja ma zapytania nie pasujące do struktury agregatu. Często buduje się je za pomocą obliczeń map-reduce.

Istnieją dwie strategie ich aktualizacji:

  • Zachłanna (eager) — widok aktualizujemy w tej samej chwili co dane bazowe. Dobre, gdy odczytów widoku jest więcej niż zapisów i zależy nam na świeżości danych.
  • Wsadowa (batch) — widok przeliczamy okresowo zadaniami w tle. Trzeba wtedy zaakceptować pewną „nieświeżość" danych, zależną od wymagań biznesowych.

Widok można budować poza bazą albo zlecić jego utrzymanie samej bazie. W bazach kolumnowych częstym wzorcem jest trzymanie zmaterializowanych widoków w osobnych rodzinach kolumn, co pozwala aktualizować je w ramach tej samej atomowej operacji.

Modele dystrybucji danych

Dlaczego model jednego serwera (single server) bywa najlepszym wyborem?

Pojedynczy serwer to model dystrybucji polegający na… braku dystrybucji — cała baza działa na jednej maszynie obsługującej wszystkie odczyty i zapisy. Choć NoSQL kojarzy się z klastrami, bardzo często jest to opcja godna polecenia jako pierwsza.

Powód jest prosty: eliminuje całą złożoność, którą wprowadza rozpraszanie danych. Łatwiej go obsługiwać administratorom i łatwiej o nim rozumować programistom. Rozproszenie danych daje korzyści (większe wolumeny, większy ruch, wyższa dostępność), ale zawsze kosztem złożoności — więc nie warto go wprowadzać, jeśli korzyści nie są naprawdę przekonujące.

Model jednoserwerowy ma sens szczególnie wtedy, gdy model danych bazy NoSQL lepiej pasuje do aplikacji niż relacyjny. Oczywistym przykładem są bazy grafowe, które najlepiej działają właśnie na jednym serwerze. Jeśli głównie przetwarzamy agregaty, jednoserwerowa baza dokumentowa lub klucz-wartość może być korzystna po prostu dlatego, że ułatwia życie programistom.

Czym jest sharding i jakie problemy rozwiązuje?

Sharding to umieszczanie różnych fragmentów zbioru danych na różnych serwerach (węzłach). Jeśli różni użytkownicy sięgają po różne części danych, każdy z nich rozmawia głównie z jednym węzłem, a obciążenie rozkłada się równomiernie — to zapewnia poziomą skalowalność zapisów i odczytów.

Żeby sharding działał dobrze, dane czytane razem powinny leżeć na tym samym węźle — i tu bardzo pomaga orientacja na agregaty, bo agregat jest naturalną jednostką dystrybucji. Przy rozmieszczaniu danych warto brać pod uwagę lokalizację fizyczną (dane bliżej użytkowników) i równomierność obciążenia.

Historycznie sharding realizowano w logice aplikacji, co komplikowało kod i utrudniało rebalansowanie. Wiele baz NoSQL oferuje auto-sharding, gdzie sama baza przydziela dane do węzłów.

Ważne ograniczenie: sharding sam w sobie nie poprawia odporności. Awaria węzła sprawia, że jego fragment danych staje się niedostępny — a klastry często używają mniej niezawodnych maszyn, więc awarie węzłów są częstsze. Dlatego sharding zwykle łączy się z replikacją. Warto też włączyć go, zanim stanie się konieczny — włączanie shardingu na przeciążonej produkcji potrafi pochłonąć wszystkie zasoby bazy na przenoszenie danych.

Na czym polega replikacja master-slave i kiedy jest przydatna?

W replikacji master-slave dane kopiowane są na wiele węzłów. Jeden węzeł jest masterem (głównym) — to autorytatywne źródło danych, odpowiedzialne za przetwarzanie zapisów. Pozostałe to slave'y (podrzędne), które synchronizują się z masterem. Odczyty mogą iść zarówno do mastera, jak i do slave'ów.

Model ten jest najbardziej przydatny przy obciążeniu zdominowanym przez odczyty: dodając kolejne slave'y i kierując do nich odczyty, skalujemy się poziomo. Nie pomaga jednak przy intensywnych zapisach — master pozostaje wąskim gardłem.

Druga korzyść to odporność odczytów: gdy master padnie, slave'y nadal obsługują odczyty, a któryś z nich można szybko awansować na nowego mastera (ręcznie lub automatycznie przez wybór w klastrze). Dzięki temu master-slave bywa przydatny nawet bez potrzeby skalowania — jako konfiguracja jednego serwera z „gorącą" kopią zapasową.

Ciemna strona to niespójność: różni klienci czytający z różnych slave'ów mogą zobaczyć różne wartości, zanim zmiany dotrą wszędzie. W skrajnym przypadku klient nie odczyta nawet zapisu, który właśnie wykonał.

flowchart TD
    C["Klient<br/>(zapisy)"] -->|write| M["Master"]
    M -->|replikacja| S1["Slave 1"]
    M -->|replikacja| S2["Slave 2"]
    M -.->|read| R0["odczyt"]
    S1 -.->|read| R1["odczyt"]
    S2 -.->|read| R2["odczyt"]

Czym różni się replikacja peer-to-peer od master-slave?

Replikacja peer-to-peer rezygnuje z mastera — wszystkie repliki są równorzędne, każda przyjmuje zapisy, a utrata którejkolwiek nie odcina dostępu do danych. Rozwiązuje to dwa problemy master-slave: master jako pojedynczy punkt awarii oraz wąskie gardło zapisów.

Cena to konflikty zapis-zapis (write-write): skoro można pisać w wielu miejscach, dwie osoby mogą jednocześnie zmodyfikować ten sam rekord. Mamy tu spektrum kompromisów:

  • Na jednym końcu — repliki koordynują każdy zapis, by uniknąć konfliktu. Daje to gwarancję tak silną jak master, ale kosztem ruchu sieciowego. Wystarczy zgoda większości, więc można przeżyć utratę mniejszości węzłów.
  • Na drugim końcu — godzimy się na niespójne zapisy i scalamy je później według reguł zależnych od domeny. Daje to pełną wydajność zapisu do dowolnej repliki.

To kompromis między spójnością a dostępnością. Replikację i sharding można łączyć: częstą strategią dla baz kolumnowych jest peer-to-peer z shardingiem przy współczynniku replikacji 3 (każdy fragment na trzech węzłach), dzięki czemu awaria węzła nie powoduje utraty danych.

flowchart LR
    C1["Klient"] -->|read/write| N1["Węzeł A"]
    C2["Klient"] -->|read/write| N2["Węzeł B"]
    C3["Klient"] -->|read/write| N3["Węzeł C"]
    N1 <-->|synchronizacja| N2
    N2 <-->|synchronizacja| N3
    N3 <-->|synchronizacja| N1

Spójność i twierdzenie CAP

Czym jest konflikt zapis-zapis i jak można go rozwiązać?

Konflikt zapis-zapis (write-write) powstaje, gdy dwie osoby aktualizują ten sam element danych w tym samym czasie. Bez kontroli współbieżności jedna aktualizacja może natychmiast nadpisać drugą, co daje utraconą aktualizację (lost update) — utrata spójności, bo druga zmiana opierała się na stanie sprzed pierwszej, a została zastosowana po niej.

Podejścia do utrzymania spójności dzielą się na dwa rodzaje:

  • Pesymistyczne — zapobiegają konfliktom, najczęściej przez blokady zapisu (write locks): by zmienić wartość, trzeba najpierw zdobyć blokadę, a system dopuszcza tylko jednego klienta naraz.
  • Optymistyczne — pozwalają konfliktom zaistnieć, ale je wykrywają. Typowy mechanizm to aktualizacja warunkowa (conditional update) — przed zapisem sprawdzamy, czy wartość nie zmieniła się od ostatniego odczytu.

Trzecia opcja to zapisanie obu wersji i odnotowanie konfliktu, a potem scalenie (jak w systemach kontroli wersji) — automatyczne lub z udziałem użytkownika.

Choć instynkt podpowiada podejście pesymistyczne, ma ono cenę: blokady poważnie obniżają responsywność i grożą zakleszczeniami (deadlocks). To fundamentalny kompromis między bezpieczeństwem a żywotnością (liveness).

Czym jest konflikt odczyt-zapis i czym jest okno niespójności (inconsistency window)?

Konflikt odczyt-zapis (read-write), czyli niespójny odczyt, zachodzi, gdy jeden klient czyta dane w trakcie zapisu wykonywanego przez innego klienta i widzi je w stanie pośrednim. Przykład: jedna osoba dodaje pozycję do zamówienia i ma zaktualizować koszt wysyłki, a druga w międzyczasie odczytuje pozycje i stary koszt — dane są wewnętrznie sprzeczne. To naruszenie spójności logicznej (logical consistency): zapewnienia, że różne elementy danych pasują do siebie.

W bazach relacyjnych chroni przed tym transakcja. W bazach zorientowanych na agregaty można uniknąć problemu, gdy wszystkie powiązane dane (zamówienie, pozycje, koszt wysyłki) są częścią jednego agregatu — wtedy mamy spójność logiczną w jego obrębie. Problem pojawia się dopiero przy operacjach obejmujących wiele agregatów.

Okno niespójności to czas, przez jaki niespójność jest widoczna. Może być bardzo krótkie (np. ułamki sekundy), ale replikacja potrafi je wydłużyć — zwłaszcza gdy opóźnienia sieci sprawiają, że to samo okno trwa znacznie dłużej na zdalnym slave'ie. Poziom spójności da się zwykle ustawiać per żądanie: słabszy tam, gdzie nie szkodzi, silniejszy tam, gdzie jest krytyczny.

Co oznacza eventual consistency i czym jest spójność read-your-writes?

Eventual consistency (spójność ostateczna) to model, w którym w danej chwili repliki mogą mieć różne wartości, ale jeśli ustaną zapisy, to ostatecznie wszystkie węzły uzgodnią tę samą wartość. Dotyczy to spójności replikacji — zapewnienia, że ten sam element ma tę samą wartość niezależnie od repliki, z której czytamy. Dane nieaktualne nazywamy „nieświeżymi" (stale); pamięć podręczna to zresztą też forma replikacji.

Częstym problemem w systemach eventual consistency jest sytuacja, gdy użytkownik staje się niespójny sam ze sobą — np. dodaje komentarz na jednym węźle, odświeża stronę, a żądanie trafia na inny węzeł, który jeszcze nie dostał zmiany, więc komentarz „znika".

Rozwiązaniem jest spójność read-your-writes — gwarancja, że po wykonaniu zapisu nadal będziemy widzieć własną zmianę. Często osiąga się ją przez spójność sesji (session consistency), czyli read-your-writes w obrębie sesji użytkownika. Techniki to m.in.:

  • Sticky session (session affinity) — przypięcie sesji do jednego węzła. Proste, ale ogranicza pracę load balancera.
  • Znaczniki wersji (version stamps) — każde żądanie zawiera ostatni widziany znacznik wersji, a węzeł czeka, aż będzie miał odpowiednio aktualne dane.

Na czym polega twierdzenie CAP i jak należy je rozumieć?

Twierdzenie CAP mówi, że spośród trzech właściwości — Consistency (spójność), Availability (dostępność) i Partition tolerance (odporność na podział sieci) — można mieć tylko dwie. W kontekście CAP „dostępność" oznacza, że każdy działający węzeł, z którym da się porozumieć, potrafi odczytać i zapisać dane, a „podział" to rozerwanie komunikacji w klastrze na grupy, które się nawzajem nie widzą (split brain).

Popularne ujęcie „dwa z trzech" jest jednak mylące. Pojedynczy serwer jest systemem CA (spójny i dostępny, bez podziałów), ale system rozproszony i tak musi tolerować podziały sieci — one po prostu się zdarzają. Realny sens twierdzenia jest więc taki: gdy nastąpi podział sieci, trzeba wybrać między spójnością a dostępnością.

I nie jest to wybór zero-jedynkowy — można poświęcić odrobinę spójności dla odrobiny dostępności, dobierając kompromis do konkretnych potrzeb. Co więcej, często lepiej myśleć nie o parze spójność/dostępność, lecz o spójności kontra opóźnienie (latency): więcej węzłów zaangażowanych w operację daje lepszą spójność, ale wydłuża czas odpowiedzi. Dostępność można wtedy rozumieć jako granicę opóźnienia, którą jesteśmy w stanie zaakceptować.

(Wspomina się też o akronimie BASE — Basically Available, Soft state, Eventual consistency — jako przeciwwadze dla ACID, ale jest on dość naciągany, a sam jego twórca widział ACID i BASE jako spektrum, nie wybór binarny.)

flowchart TD
    P{"Czy wystąpił podział sieci<br/>(partycja)?"}
    P -->|Nie| OK["System może być<br/>spójny i dostępny"]
    P -->|Tak| W{"Co wybierasz?"}
    W -->|"Spójność (CP)"| CP["Odrzuć część żądań,<br/>utrzymaj jeden stan"]
    W -->|"Dostępność (AP)"| AP["Odpowiadaj zawsze,<br/>godź się na rozbieżności"]

Czym są kworumy (quorums) i jak wyznaczyć liczbę węzłów dla silnej spójności?

Kworum to mechanizm pozwalający uzyskać silną spójność bez angażowania wszystkich replik — wystarczy odpowiednio liczna większość. Im więcej węzłów bierze udział w operacji, tym mniejsze ryzyko niespójności, ale tym większe opóźnienie.

Wprowadza się trzy wielkości:

  • N — współczynnik replikacji (na ilu węzłach trzymamy dane),
  • W — liczba węzłów, które muszą potwierdzić zapis,
  • R — liczba węzłów, z którymi trzeba się skontaktować przy odczycie.

Kworum zapisu wymaga, by W > N/2 — przy konflikcie tylko jeden zapis może uzyskać większość. Silnie spójny odczyt uzyskujemy, gdy R + W > N — wtedy zbiory węzłów dla odczytu i zapisu na pewno się przetną, więc odczyt „zobaczy" najnowszy zapis.

Te wartości można dobierać per operacja, świadomie ważąc kompromisy. Na przykład przy N = 3 ustawienie W = 3, R = 1 daje szybkie, silnie spójne odczyty kosztem wolnych zapisów i braku tolerancji na awarię węzła. Powszechnie uważa się, że współczynnik replikacji 3 daje dobrą odporność — pozwala przeżyć awarię jednego węzła przy zachowaniu kworum. Warto pamiętać, że liczba węzłów w klastrze to nie to samo co współczynnik replikacji — można mieć 100 węzłów, ale replikować dane tylko na 3, a resztę rozkładu zapewnić shardingiem.

flowchart LR
    Z["Zapis<br/>W = 2"] --> A["Węzeł 1"]
    Z --> B["Węzeł 2"]
    O["Odczyt<br/>R = 2"] --> B
    O --> C["Węzeł 3"]
    B -.->|"węzeł wspólny → przecięcie"| X["R + W większe od N<br/>(2 + 2 wobec 3)"]

Znaczniki wersji

21. Czym jest znacznik wersji (version stamp) i jak można go zaimplementować?

Odpowiedź dostępna w pełnej wersji. Odblokuj 24 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi
22. Czym jest vector stamp i do czego służy w systemach peer-to-peer?

Odpowiedź dostępna w pełnej wersji. Odblokuj 24 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi

Map-Reduce

23. Czym jest wzorzec map-reduce i jaki problem rozwiązuje?

Odpowiedź dostępna w pełnej wersji. Odblokuj 24 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi
24. Czym jest funkcja łącząca (combiner) i partycjonowanie w map-reduce?

Odpowiedź dostępna w pełnej wersji. Odblokuj 24 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi
25. Jak komponuje się i inkrementalnie aktualizuje obliczenia map-reduce?

Odpowiedź dostępna w pełnej wersji. Odblokuj 24 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi

Bazy klucz-wartość

26. Czym jest baza klucz-wartość i jakie ma kluczowe cechy?

Odpowiedź dostępna w pełnej wersji. Odblokuj 24 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi
27. W jakich zastosowaniach bazy klucz-wartość sprawdzają się najlepiej?

Odpowiedź dostępna w pełnej wersji. Odblokuj 24 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi
28. Kiedy NIE należy używać bazy klucz-wartość?

Odpowiedź dostępna w pełnej wersji. Odblokuj 24 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi

Bazy dokumentowe

29. Czym jest baza dokumentowa i czym różni się od bazy klucz-wartość?

Odpowiedź dostępna w pełnej wersji. Odblokuj 24 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi
30. W jakich zastosowaniach sprawdzają się bazy dokumentowe?

Odpowiedź dostępna w pełnej wersji. Odblokuj 24 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi
31. Kiedy NIE należy używać bazy dokumentowej?

Odpowiedź dostępna w pełnej wersji. Odblokuj 24 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi

Bazy typu kolumnowego (column-family)

32. Jak zorganizowane są dane w bazie typu column-family?

Odpowiedź dostępna w pełnej wersji. Odblokuj 24 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi
33. W jakich zastosowaniach sprawdzają się bazy column-family?

Odpowiedź dostępna w pełnej wersji. Odblokuj 24 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi
34. Kiedy NIE należy używać bazy column-family?

Odpowiedź dostępna w pełnej wersji. Odblokuj 24 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi

Bazy grafowe

35. Czym jest baza grafowa i dlaczego jest szybka w przechodzeniu relacji?

Odpowiedź dostępna w pełnej wersji. Odblokuj 24 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi
36. Jakie są charakterystyczne cechy baz grafowych pod kątem spójności i dystrybucji?

Odpowiedź dostępna w pełnej wersji. Odblokuj 24 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi
37. W jakich zastosowaniach sprawdzają się bazy grafowe i kiedy ich unikać?

Odpowiedź dostępna w pełnej wersji. Odblokuj 24 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi

Migracje schematu

38. Dlaczego bazy schemaless też wymagają przemyślanej migracji i czym jest migracja inkrementalna?

Odpowiedź dostępna w pełnej wersji. Odblokuj 24 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi
39. Jak migruje się dane przy zmianie struktury agregatu lub w bazie grafowej?

Odpowiedź dostępna w pełnej wersji. Odblokuj 24 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi

Polyglot persistence i wybór bazy

40. Czym jest polyglot persistence?

Odpowiedź dostępna w pełnej wersji. Odblokuj 24 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi
41. Dlaczego warto enkapsulować dostęp do danych w usługach przy polyglot persistence?

Odpowiedź dostępna w pełnej wersji. Odblokuj 24 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi
42. Jak podejść do wyboru bazy danych dla nowego projektu?

Odpowiedź dostępna w pełnej wersji. Odblokuj 24 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi

Poza NoSQL

43. Czym jest event sourcing?

Odpowiedź dostępna w pełnej wersji. Odblokuj 24 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi
44. Jakie alternatywy poza NoSQL warto rozważyć w ramach polyglot persistence?

Odpowiedź dostępna w pełnej wersji. Odblokuj 24 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi

Chcesz poznać wszystkie odpowiedzi?

Uzyskaj pełny dostęp do 44 pytań rekrutacyjnych z NoSQL oraz pozostałych technologii.

Zobacz plany cenowe