Dokud ti všechno běží na jednom serveru, je svět jednoduchý. Jakmile máš serverů víc a musí spolu spolupracovat, platí najednou jiná pravidla: síť mezi nimi občas vypadne, hodiny se každému trochu rozcházejí a „co je vlastně pravda" přestává být jednoznačné. Tahle kapitola je teoretický základ, na kterém stojí Patterny, Messaging i škálování databáze. Bude trochu abstraktní, tak hodně příkladů.
Pár pojmů na úvod
- Distribuovaný systém = víc počítačů, které spolupracují tak, aby navenek vypadaly jako jeden systém (např. databáze běžící na pěti serverech zároveň).
- Uzel (node) = jeden z těch počítačů/serverů.
- Replika = kopie stejných dat uložená na víc uzlech (pro odolnost a rychlejší čtení).
- Partice (network partition) = síť se na chvíli rozdělí a část uzlů se navzájem nevidí — ne proto, že by spadly, ale protože mezi nimi nejde spojení.
Proč je to těžké — 8 omylů (fallacies)
Začátečníci (i zkušení) navrhují, jako by tyhle věci platily. Neplatí ani jedna:
- Síť je spolehlivá. 2. Latence je nulová. 3. Šířka pásma je nekonečná. 4. Síť je bezpečná.
- Topologie sítě se nemění. 6. Je jen jeden správce. 7. Přenos dat je zadarmo. 8. Síť je všude stejná.
Skoro každý záludný bug v distribuovaném systému vznikne tím, že někdo jeden z těchhle omylů potichu předpokládal — třeba „to volání přece vždycky projde".
CAP teorém — co obětuješ, když vypadne síť
Když nastane partice (uzly se navzájem nevidí), musíš si vybrat jednu ze dvou věcí. Obě mít nejde:
- C — Consistency (konzistence): čtení vidí poslední zapsanou hodnotu, nebo radši vrátí chybu — místo aby ti dalo zastaralá data. (Tahle silná forma se přesně nazývá linearizability; v praxi existuje i „skoro silná" konzistence, ale pro mentální model ber „nedostaneš stará data".)
- A — Availability (dostupnost): každý dotaz dostane nějakou odpověď, i kdyby trochu zastaralou. Nikdy nedostaneš chybu „nemůžu".
Partice dřív nebo později nastane vždycky, takže reálná volba zní CP, nebo AP:
- CP (raději konzistence) — banka, platby: při výpadku sítě radši operaci odmítni, než abys ukázal nesprávný zůstatek.
- AP (raději dostupnost) — feed na sociální síti, košík, DNS: radši ukaž možná zastaralá data, hlavně ať to funguje.
CAP je trochu zjednodušení. Přesnější je PACELC: při Partici volíš C vs A, a jindy (Else) volíš mezi nižší latencí a silnější konzistencí. Pointa: pořád něco za něco.
Konzistenční modely — není to jen „konzistentní vs nekonzistentní"
Mezi „všichni vidí všechno hned" a „nikdo nic negarantuje" je celé spektrum:
- Strong consistency (silná) — všichni vidí stejnou hodnotu okamžitě. Pohodlné, ale drahé (uzly se musí pořád domlouvat → pomalejší).
- Eventual consistency (případná) — kopie se nakonec srovnají, ale chvíli se můžou lišit. Levné a dostupné. (Praktická ukázka: replication lag z Databází — z repliky chvíli čteš starou hodnotu.)
- Read-your-writes — vidíš aspoň svoje vlastní změny hned (i když cizí třeba ne).
A když se dvě repliky v AP systému rozejdou, jak se konflikt smíří? Buď naivně last-write-wins (vyhraje pozdější timestamp — ale pozor, kvůli clock skew může tiše zahodit zápis!), nebo přes CRDT (datové struktury, které se umí slít bez konfliktu — třeba čítač, co se vždy správně sečte). Na tuhle otázku „jak vyřešíš konflikt" se senior naráží běžně.
Čas a pořadí — zákeřnější, než čekáš
Hodiny na různých serverech se vždycky trochu rozcházejí (clock skew). Proto nemůžeš spolehlivě
řadit události z víc serverů podle časové značky — událost s pozdějším created_at mohla ve
skutečnosti nastat dřív. „Seřaď podle created_at" napříč servery je rozbité.
Místo fyzického času se používají logické hodiny (počítadla, která zachytí pořadí a příčinnost bez ohledu na reálný čas). Pro úplnost: Lamportovy hodiny dají jen jedno celkové pořadí, ale nepoznají, že dvě události spolu nesouvisí; vektorové hodiny umí navíc rozlišit, že dvě události jsou souběžné (kauzálně nezávislé) — a právě to pohání detekci konfliktních verzí ve verzovaných úložištích (to „verzování", o které se opírá quorum výše). Zatím stačí zapamatovat si ten problém — že času napříč servery nevěříš.
Consensus — jak se víc uzlů na něčem shodne
Jak se skupina serverů shodne na jedné hodnotě (např. „kdo je teď hlavní"), i když některé zrovna padají? Tomu se říká consensus a řeší ho algoritmy jako Raft (čitelnější) nebo Paxos.
Princip je jako hlasování: zvolí se „leader", změny tečou přes něj a každá platí, až ji potvrdí většina uzlů (tomu „většina" se říká quorum). Většina zabrání tomu, aby dvě části sítě rozhodly proti sobě (split brain). Tohle pohání věci jako etcd, ZooKeeper nebo Kafku. (Drobnost k 2026: Kafka už ZooKeeper nepotřebuje — moderně běží na vlastním consensu KRaft.)
Pozor, nepleť si to s jiným „quorem". Výše je quorum consensu (většina uzlů potvrdí změnu). Existuje i read/write quorum u databází bez leadera (Dynamo, Cassandra): pravidlo
R + W > N(N replik, W potvrdí zápis, R se ptáš při čtení) zařídí, že se čtená a zapsaná skupina překryjí aspoň v jednom uzlu → čtení tak narazí na repliku s posledním zápisem. Ale aby poznalo, která z přečtených hodnot je ta nejnovější, musí být hodnoty verzované. Je to příbuzný, ale jiný mechanismus než Raft/Paxos.
Replikace a partitioning
- Replikace = držet kopie dat na víc uzlech (odolnost + rychlejší čtení). Buď synchronní (počkej, až to potvrdí kopie — bezpečné, ale pomalejší), nebo asynchronní (rychlé, ale při pádu hlavního uzlu hrozí ztráta posledních zápisů).
- Partitioning / sharding = rozdělit data na víc uzlů (aby žádný neměl všechno). Pozor na špatně zvolený klíč → na jednom uzlu skončí nepoměrně moc provozu (hot spot).
Tři věci, co potkáš v každém distribuovaném systému
- Consistent hashing — jak rozdělit data, aby se při změně počtu uzlů přesunulo minimum. Naivně bys určil uzel jako „číslo klíče modulo počet uzlů". Jenže když přidáš nebo odebereš uzel, modulo se změní pro skoro všechny klíče a musíš přesunout skoro všechna data — katastrofa. Consistent hashing si uzly i klíče rozmístí po pomyslném kruhu; klíč patří nejbližšímu uzlu po směru. Když uzel přibude nebo zmizí, přesune se jen jeho kousek kruhu, ne všechno. Používá to Cassandra, distribuované cache i load balancery.
- Service discovery — jak služby najdou jedna druhou. V dynamickém prostředí se adresy služeb pořád mění (instance přibývají, mizí). Nikdo si je nemůže napsat natvrdo. Proto existuje registr služeb, kam se každá instance zapíše, a ostatní se ho ptají „kde teď běží služba X?".
- Leader election — jak skupina uzlů zvolí jednoho hlavního. Pro některé úkoly (kdo spouští naplánovanou úlohu, kdo přijímá zápisy) smí být v daný okamžik aktivní jen jeden uzel. Skupina ho zvolí a když „vedoucí" spadne, zvolí nového. Stojí to na consensu (vedoucí musí mít většinu) a brání to split brainu.
Garance doručení (pojítko na messaging a patterny)
Protože síť je nespolehlivá, zprávy se ztrácejí i duplikují. Podle toho, jak se s tím systém vyrovnává, rozlišujeme:
- At-most-once — nanejvýš jednou: žádné duplicity, ale zpráva se může ztratit.
- At-least-once — aspoň jednou: nic se neztratí, ale zpráva může dorazit vícekrát. Nejčastější v praxi → příjemce musí být idempotentní (umět zpracovat duplicitu, viz Patterny).
- Exactly-once — přesně jednou: svatý grál. „Po drátě" reálně neexistuje; v praxi se dělá jako at-least-once + idempotentní zpracování, což navenek vypadá jako přesně jednou.
Failure modes — jak to v praxi praská
- Split brain — při partici si obě strany myslí, že jsou hlavní → dvě verze pravdy, které se rozejdou. Řeší to quorum (hlavní může být jen strana s většinou).
- Cascading failure — jeden uzel spadne, jeho zátěž přeteče na ostatní, ty se přetíží a padají jako dominy. Brání tomu circuit breaker a bulkhead (viz Patterny).
- Clock skew — rozjeté hodiny → špatné řazení událostí, předčasně „expirované" tokeny.
🛠️ Cvičení
- Najdi porušený omyl. Vezmi reálný výpadek (klidně z postmortemu velké firmy) a urči, který z 8 fallacies za ním stál (předpokládali, že síť je spolehlivá? že latence je nulová?).
- CP vs AP. Pro tři služby — bankovní převod, košík v e-shopu, počítadlo lajků — rozhodni CP nebo AP a zdůvodni, co obětuješ, když vypadne síť.
- Spočítej quorum. Máš 5 replik (N=5). Navrhni R a W tak, aby čtení vždy vidělo poslední zápis,
a ověř
R + W > N. Pak zkus W=1, R=1 a vysvětli, co se rozbije. - Eventual consistency v praxi. Popiš situaci, kdy uživatel něco uloží a hned to nevidí, a navrhni řešení typu read-your-writes.
- Pořadí bez globálního času. Máš události ze 3 serverů s rozjetými hodinami. Vysvětli, proč
nestačí řadit podle
created_at.
Náčrt řešení — rozbal, až si cvičení zkusíš sám
- Najdi porušený omyl — u výpadku ukaž, který z 8 fallacies někdo tiše předpokládal (typicky „síť je spolehlivá" — volání, co prý vždycky projde, nebo „latence je nulová"). Pozor: skoro každý záludný bug v distribuovaném systému vzniká právě potichu předpokládaným omylem.
- CP vs AP — bankovní převod = CP (radši operaci odmítni, než ukázat špatný zůstatek), košík i počítadlo lajků = AP (radši zastaralá data, hlavně ať to jede). Pozor: u CP obětuješ dostupnost, u AP konzistenci — partice nastane vždycky, takže si vybíráš.
- Spočítej quorum — při N=5 zvol třeba W=3, R=3, ať
R + W > N(6 > 5) a čtená a zapsaná skupina se překryjí; W=1, R=1 dá2 > 5neplatí → čtení může minout poslední zápis a vrátit starou hodnotu. Pozor: tohle je read/write quorum bez leadera, ne consensus (Raft/Paxos), a hodnoty musí být verzované. - Eventual consistency v praxi — uživatel zapíše do hlavního uzlu, ale hned čte z repliky, která ještě nedohnala (replication lag), takže svou změnu nevidí; read-your-writes vyřešíš tím, že jeho vlastní čtení po zápisu pošleš na hlavní uzel (nebo počkáš na dohnání). Pozor: stačí zaručit „vidíš svoje změny", ne silnou konzistenci pro všechny.
- Pořadí bez globálního času — hodiny serverů se rozcházejí (clock skew), takže událost s pozdějším
created_atmohla nastat dřív → řazení podle časové značky je rozbité; používej logické (Lamportovy/vektorové) hodiny. Pozor: času napříč servery prostě nevěř, ani „synchronizovanému".
🧠 Otázky & odpovědi
Proč si při partici musíš vybrat mezi konzistencí a dostupností (CAP)?
Při partici se uzly navzájem nevidí. Buď operaci odmítneš, dokud si nejsi jistý, že vracíš aktuální data (CP — obětuješ dostupnost), nebo odpovíš z toho, co máš, i kdyby to byla starší data (AP — obětuješ konzistenci). Obojí naráz nejde, dokud partice trvá. Mimo partici je přesnější PACELC: pak volíš mezi nižší latencí a silnější konzistencí.
Co znamená quorum R + W > N?
Tohle je quorum u databází bez leadera (Dynamo/Cassandra), ne to samé co consensus (Raft/Paxos)!
Máš N replik. Zápis čeká na potvrzení od W replik, čtení se ptá R replik. Když R + W > N, skupina
zapisovaných a skupina čtených replik se musí překrýt aspoň v jedné → čtení proto narazí na repliku
s posledním zápisem (a podle verze pozná, která hodnota je nejnovější). Příklad: N=3, W=2, R=2. Nižší
W a R = rychlejší, ale roste riziko, že přečteš starou hodnotu.
Proč je exactly-once doručení v podstatě mýtus?
Síť může zprávu ztratit i duplikovat a přes nespolehlivý kanál nejde atomicky „doručit a zároveň potvrdit". Co funguje v praxi: at-least-once (nikdy se neztratí, ale může dorazit víckrát) + idempotentní příjemce (umí duplicitu zahodit podle ID). Navenek to vypadá jako přesně jednou, ale čisté exactly-once „po drátě" neexistuje.
Proč nejde řadit události z více serverů podle časové značky?
Hodiny na různých serverech se rozcházejí (clock skew), takže created_at neurčuje spolehlivě
skutečné pořadí — událost s pozdějším časem mohla nastat dřív. Pro pořadí se proto používají logické
hodiny, které zachytí příčinnost nezávisle na fyzickém čase.
Co je split brain a jak mu brání quorum?
Při partici se síť rozdělí a obě strany si můžou myslet, že jsou hlavní (leader) → vzniknou dvě verze pravdy, které se rozejdou. Quorum (vyžaduj potvrzení od většiny) zajistí, že hlavní může být jen strana s většinou uzlů; menšina se odmítne tvářit jako hlavní, takže zapisuje jen jedna strana.
Proč se pro rozdělení dat na uzly používá consistent hashing místo prostého modulo?
Kdybys uzel určil jako „číslo klíče modulo počet uzlů", tak při přidání nebo odebrání uzlu se modulo změní skoro pro všechny klíče → musel bys přesunout skoro všechna data. Consistent hashing rozmístí uzly i klíče po pomyslném kruhu a klíč patří nejbližšímu uzlu; když uzel přibude nebo zmizí, přesune se jen jeho úsek kruhu, ne všechno. Proto ho používá Cassandra, distribuované cache i load balancery.
