Mindenki jó napot. Itt Vladiszlav Rodin. Jelenleg az OTUS portálon tanítok olyan kurzusokat, amelyek a szoftverarchitektúrára és a nagy terhelésnek kitett szoftverarchitektúrára összpontosítanak. A "Magas terhelésű architekták" kurzus új szakaszának indulása előtt eldöntöttem, hogy írok egy rövid egyedi anyagot, amelyet meg szeretnék osztani veletek.

Bevezetés
Mivel a HDD-ken körülbelül 400-700 művelet futtatható másodpercenként (ami összehasonlíthatatlan a tipikus magas terhelésű rendszer által várható rps-ekkel), a klasszikus lemezes adatbázis szűk keresztmetszet képez az architektúrában. Ezért különös figyelmet kell fordítani ennek a tárolónak a skálázási mintáira.
Jelenleg két adatbázis skálázási minta áll rendelkezésre: replikáció és shardelés. A sharding lehetővé teszi az írási műveletek skálázását, ezáltal csökkentve az egy szerverre jutó írási rps-t a klaszterben. A replikáció ugyanezt teszi az olvasási műveletekkel. Éppen ezt a mintát tárgyalja ez a cikk.
Replikáció
Ha a replikációt teljesen felszínesen nézzük, egy egyszerű dolog: volt egy szerver, amelyen az adatok tárolódtak, majd ez a szerver már nem bírta a terhelést az adatok olvasásakor. Hozzáadsz még pár szervert, szinkronizálod az adatokat az összes szerveren, és a felhasználó bármelyik szerverről olvashat a klaszterben.
A látszólagos egyszerűsége ellenére többféle osztályozási lehetőség létezik ezen rendszer különböző megvalósításaira:
- A klaszter szerepei szerint (master-master vagy master-slave)
- A továbbított objektumok szerint (row-based, statement-based vagy mixed)
- A csomópontok szinkronizálási mechanizmusa szerint
Ma a harmadik pontra fogunk fókuszálni.
Hogyan történik a tranzakció elkötelezése
Ez a téma közvetlenül nem kapcsolódik a replikációhoz, erről külön cikk is íródhat, azonban mivel a tranzakció elkötelezésének mechanizmusa nélkül a további olvasás értelmetlen, engedjétek meg, hogy emlékeztessem a legfontosabb dolgokra. A tranzakció elkötelezése három lépésben történik:
- A tranzakció rögzítése az adatbázis naplójában.
- A tranzakció alkalmazása az adatbázis motorban.
- A megerősítés visszaküldése a kliensnek a tranzakció sikeres alkalmazásáról.
Különböző adatbázisok esetén a fenti algoritmusban lehetnek finomságok: például a MySQL InnoDB motorjában két napló található: az egyik a replikálásra (binary log), a másik pedig az ACID fenntartására (undo/redolog), míg a PostgreSQL-ben egy napló látja el mindkét funkciót (write ahead log = WAL). De a fentiek csupán az általános koncepciót tükrözik, amely figyelembe veszi ezeket a finomságokat.
Szinkron (sync) replikáció
Adjunk hozzá a tranzakciós elkötelezési algoritmushoz logikát a beérkezett változások replikálásához:
- A tranzakció rögzítése az adatbázis naplójában.
- A tranzakció alkalmazása az adatbázis motorban.
- Adatok küldése minden replikának.
- Megerősítés beszerzése minden replikától a tranzakció végrehajtásáról.
- A megerősítés visszaküldése a kliensnek a tranzakció sikeres alkalmazásáról.
Ezzel a megközelítéssel több hátrányhoz jutunk:
- az ügyfél várakozik, amíg a változások alkalmazásra kerülnek minden replikán.
- a klaszterben lévő csomópontok számának növelésével csökkentjük annak esélyét, hogy a írási művelet sikeresen végbemegy.
Ha az első pont viszonylag világos, a második pont okait érdemes részletezni. Ha szinkron replikáció esetén nem kapunk választ legalább egy csomóponttól, visszagörgetjük a tranzakciót. Így, a klaszterben lévő csomópontok számának növelésével növeljük annak valószínűségét, hogy a írási művelet meghiúsul.
Várhatunk-e megerősítést csupán a csomópontok egy részétől, például a 51%-tól (kvórum)? Igen, várhatunk, de a klasszikus változatban minden csomópont megerősítése szükséges, mivel így tudjuk biztosítani az adatok teljes konzisztenciáját a klaszterben, ami kétségtelen előny ennek a replikálási módnak.
Aszinkron (async) replikáció
Módosítsuk az előző algoritmust. Az adatokat a replikákra „valamikor később” küldjük, és „valamikor később” a változások alkalmazásra kerülnek a replikákon:
- A tranzakció rögzítése az adatbázis naplójában.
- A tranzakció alkalmazása az adatbázis motorban.
- A megerősítés visszaküldése a kliensnek a tranzakció sikeres alkalmazásáról.
- Adatok küldése a replikákra és a változások alkalmazása általuk.
Ez a megközelítés ahhoz vezet, hogy a klaszter gyorsan működik, mivel nem tartjuk vissza az ügyfelet, amíg az adatok eljutnak a replikákhoz és el nem készülnek.
De a replikákra az adatok „valamikor később” való visszaküldésének feltétele elveszítheti a tranzakciót, mégpedig a felhasználónak megerősített tranzakció elvesztését, hiszen ha az adatok nem tudtak replikálódni, a művelet sikerességéről szóló megerősítést az ügyfélnek elküldték, de a csomópont, ahová a változások érkeztek, HDD-hiba miatt kiesett, akkor elveszítjük a tranzakciót, ami nagyon kellemetlen következményekkel járhat.
Fél-szinkron (semisync) replikáció
Végre eljutottunk a félszinkron replikációhoz. Ez a replikációs forma nem túl ismert és nem túl elterjedt, de mégis jelentős érdeklődésre tart számot, mivel képes ötvözni a szinkron és aszinkron replikációk előnyeit.
Próbáljuk meg egyesíteni a két korábbi megközelítést. Nem fogunk sokáig várni az ügyfélre, de megköveteljük, hogy az adatok replikálódjanak:
- A tranzakció rögzítése az adatbázis naplójában.
- A tranzakció alkalmazása az adatbázis motorban.
- Adatok küldése a replikákra.
- Megerősítés kapása a replikától a módosítások fogadásáról (a módosítások "később" kerülnek alkalmazásra).
- A megerősítés visszaküldése a kliensnek a tranzakció sikeres alkalmazásáról.
Figyeljen arra, hogy ezzel az algoritmussal a tranzakció elvesztése csak akkor következik be, ha mind a módosításokat elfogadó node, mind a replikáló node leáll. Az ilyen hiba valószínűsége alacsonynak számít, és ezeket a kockázatokat elfogadják.
De e megközelítésnél fennáll a fantomolvasás kockázata. Képzeljünk el egy következő forgatókönyvet: a 4. lépés során nem kaptunk megerősítést egyetlen replikától sem. Ezt a tranzakciót vissza kell vonnunk, az ügyfélnek pedig nem adunk megerősítést. Mivel az adatok a 2. lépésnél érvényesültek, a 2. lépés befejezése és a tranzakció visszavonása között időbeli rés keletkezik, amely alatt párhuzamos tranzakciók láthatják azokat a változásokat, amelyeknek nem kellene megjelenniük az adatbázisban.
Lose-less félszinkron replikáció
Ha egy kicsit elgondolkodunk, a lépések algoritmusban való megfordításával megoldhatjuk a fantomolvasás problémáját ebben a forgatókönyvben:
- A tranzakció rögzítése az adatbázis naplójában.
- Replika adatok küldése.
- Megerősítés kapása a replikától a módosítások fogadásáról (a módosítások "később" kerülnek alkalmazásra).
- A tranzakció alkalmazása az adatbázis motorban.
- A megerősítés visszaküldése a kliensnek a tranzakció sikeres alkalmazásáról.
Mostantól csak akkor rögzítjük a módosításokat, ha azok replikálódtak.
Következtetés
Mindig érvényes, hogy nincsenek tökéletes megoldások, csak megoldások halmaza, mindegyik saját előnyeivel és hátrányaival rendelkezik, és különböző feladatcsoportok megoldására alkalmas. Ez teljesen igaz a replikált adatbázis szinkronizálási mechanizmusának kiválasztására is. A félszinkron replikáció előnyeinek halmaza elég szilárd és érdekes ahhoz, hogy érdemes figyelembe venni, annak ellenére, hogy kis mértékben elterjedt.
Ennyi volt. Találkozunk a következő eseményen. !
Forrás: habr.com
