Ahoj všichni. Tady je Vladislav Rodin. V současné době učím na portálu OTUS kurzy zaměřené na architekturu softwaru a architekturu softwaru vystaveného vysoké zátěži. Před začátkem nového běhu kurzu jsem se rozhodl napsat malý autorský materiál, o který se s vámi chci podělit.

Úvod
Vzhledem k tomu, že na HDD může probíhat pouze přibližně 400-700 operací za sekundu (což se nedá srovnat s typickými rps, které přicházejí na vysoce zatížený systém), klasická disková databáze představuje úzké hrdlo architektury. Proto je nutné věnovat zvláštní pozornost vzorům škálování tohoto úložiště.
V současné době existují 2 vzory škálování databáze: replikace a shardování. Shardování umožňuje škálovat operaci zápisu a tím snižovat rps na zápis, které přichází na jeden server vašeho clusteru. Replikace umožňuje dělat totéž, ale s operacemi čtení. Tento vzor je předmětem tohoto článku.
Replikace
Pokud se podíváme na replikaci zcela z povrchu, je to jednoduchá záležitost: měli jste jeden server, na němž ležely data, a pak tento server přestal zvládat zátěž na čtení těchto dat. Přidáte další servery, synchronizujete data na všech serverech a uživatel může číst z libovolného serveru vašeho clusteru.
Navzdory zdánlivé jednoduchosti existuje několik variant klasifikace různých realizací tohoto schématu:
- Podle rolí v clusteru (master-master nebo master-slave)
- Podle přenášených objektů (row-based, statement-based nebo mixed)
- Podle mechanismu synchronizace uzlů
Dnes se podíváme právě na třetí bod.
Jak probíhá závazek transakce
Toto téma se přímo netýká replikace, o něm by mohl být napsán samostatný článek, ale protože bez pochopení mechanismu závazku transakce by další čtení bylo zbytečné, dovolím si připomenout ty nejzákladnější věci. Závazek transakce probíhá ve 3 fázích:
- Zápis transakce do protokolu databáze.
- Aplikace transakce v databázovém enginu.
- Odeslání potvrzení klientovi o úspěšném provedení transakce.
Různé databáze mohou mít v tomto algoritmu své specifické rysy: například v enginu InnoDB databáze MySQL existují 2 protokoly: jeden pro replikaci (binary log) a druhý pro zajištění ACID (undo/redor log), zatímco PostgreSQL má jeden protokol, který vykonává obě funkce (write ahead log = WAL). Ale zde je představena obecná koncepce, která tyto nuance ignoruje.
Synchronní (sync) replikace
Přidejme do algoritmu potvrzení transakce logiku replikace získaných změn:
- Zápis transakce do protokolu databáze.
- Aplikace transakce v databázovém enginu.
- Odeslání dat na všechny repliky.
- Obdržení potvrzení od všech replik o provedení transakce.
- Odeslání potvrzení klientovi o úspěšném provedení transakce.
Tento přístup nám přináší řadu nevýhod:
- klient čeká na aplikaci změn na všech replikách.
- se zvyšujícím se počtem uzlů v clusteru snižujeme pravděpodobnost, že operace zápisu proběhne úspěšně.
Zatímco první bod je více méně jasný, důvody druhého bodu je třeba objasnit. Pokud při synchronní replikaci neobdržíme odpověď alespoň od jednoho uzlu, transakce se vrátí zpět. Tímto způsobem, když zvyšujete počet uzlů v clusteru, zvyšujete pravděpodobnost, že operace zápisu selže.
Můžeme čekat na potvrzení pouze od určité části uzlů, například od 51 % (kvórum)? Ano, můžeme, nicméně v klasické variantě je požadováno potvrzení od všech uzlů, protože pouze tak zajistíme plnou konzistenci dat v clusteru, což představuje nepochybnou výhodu tohoto typu replikace.
Asynchronní (async) replikace
Upravme předchozí algoritmus. Data na repliky budeme posílat "někdy později" a "někdy později" budou změny na replikách aplikovány:
- Zápis transakce do protokolu databáze.
- Aplikace transakce v databázovém enginu.
- Odeslání potvrzení klientovi o úspěšném provedení transakce.
- Odeslání dat na repliky a aplikace změn jimi.
Tento přístup vede k tomu, že cluster pracuje rychle, protože nedržíme klienta na čekací listině, dokud se data nedostanou k replikám a ještě je potvrzeny.
Ale podmínka odesílání dat na repliky "někdy později" může vést k ztrátě transakce, a to dokonce ztrátě potvrzené uživatelem, protože pokud se data nestihla replikovat a potvrzení o úspěšném provedení operace bylo odesláno klientovi, ale uzlu, na který přišly změny, selhal HDD, ztrácíme transakci, což může mít velmi nepříjemné následky.
Polosynchronní (semisync) replikace
Konečně jsme se dostali k polosychronní replikaci. Tento typ replikace není příliš známý a není moc rozšířen, ale přesto nás zajímá, protože může kombinovat výhody jak synchronní, tak asynchronní replikace.
Zkusíme spojit dva předchozí přístupy. Nechceme dlouho zadržovat klienta, ale požadujeme, aby se data replikovala:
- Zápis transakce do protokolu databáze.
- Aplikace transakce v databázovém enginu.
- Odesílání dat na repliky.
- Získání potvrzení od repliky o přijetí změn (aplikovány budou 'někdy později').
- Odeslání potvrzení klientovi o úspěšném provedení transakce.
Všimněte si, že při tomto algoritmu dochází ke ztrátě transakce pouze v případě, že selže jak uzel přijímající změny, tak uzel-replika. Pravděpodobnost takové poruchy je považována za malou a toto riziko je přijímáno.
Při tomto přístupu však existuje riziko fantomových čtení. Představme si následující scénář: v kroku 4 jsme nedostali potvrzení od žádné repliky. Musíme tuto transakci vrátit zpět a klientovi nevrátíme potvrzení. Vzhledem k tomu, že data byla aplikována v kroku 2, mezi ukončením kroku 2 a vrácením transakce se objevuje časový interval, během něhož paralelní transakce mohou vidět ty změny, které by v databázi být neměly.
Lose-less semisync replikace
Pokud o tom chvilku popřemýšlíme, můžeme jednoduše vyměnit kroky algoritmu a vyřešit problém fantomových čtení v tomto scénáři:
- Zápis transakce do protokolu databáze.
- Odesílání dat repliky.
- Získání potvrzení od repliky o přijetí změn (aplikovány budou 'někdy později').
- Aplikace transakce v databázovém enginu.
- Odeslání potvrzení klientovi o úspěšném provedení transakce.
Nyní změny potvrzujeme pouze, pokud byly replikovány.
Výstup
Jak už to bývá, neexistují dokonalá řešení, existuje soubor řešení, z nichž každé má své výhody a nevýhody a je vhodné pro řešení různých tříd úloh. To platí i pro výběr mechanismu synchronizace dat v replikované databázi. Soubor výhod, které polosychronní replikace nabízí, je dostatečně významný a zajímavý, aby ji bylo možné považovat za zasluhující pozornost, i přes její nízkou rozšířenost.
To je vše. Na viděnou na !
Zdroj: habr.com
