NewSQL = NoSQL+ACID

NewSQL = NoSQL+ACID
Donedávna Odnoklassniki ukladal približne 50 TB údajov spracovaných v reálnom čase na serveri SQL Server. Pre takýto objem je takmer nemožné poskytnúť rýchly a spoľahlivý prístup do dátového centra, ktorý by bol odolný voči poruchám pomocou SQL DBMS. V takýchto prípadoch sa zvyčajne používa jedno z úložísk NoSQL, ale nie všetko je možné preniesť do NoSQL: niektoré entity vyžadujú záruky transakcie ACID.

To nás priviedlo k použitiu úložiska NewSQL, teda DBMS, ktoré poskytuje odolnosť voči chybám, škálovateľnosť a výkon NoSQL systémov, no zároveň zachováva záruky ACID známe klasickým systémom. Funkčných priemyselných systémov tejto novej triedy je málo, preto sme takýto systém sami implementovali a uviedli do komerčnej prevádzky.

Ako to funguje a čo sa stalo - prečítajte si pod rezom.

Dnes je mesačné publikum Odnoklassniki viac ako 70 miliónov jedinečných návštevníkov. my Sme v prvej päťke najväčšie sociálne siete na svete a patrí medzi dvadsať stránok, na ktorých používatelia trávia najviac času. Infraštruktúra OK zvláda veľmi vysoké zaťaženie: viac ako milión HTTP požiadaviek/s na fronte. Časti serverovej flotily s viac ako 8000 1 kusmi sú umiestnené blízko seba - v štyroch moskovských dátových centrách, čo umožňuje medzi nimi sieťovú latenciu menšiu ako XNUMX ms.

Cassandru používame od roku 2010, počnúc verziou 0.6. Dnes je v prevádzke niekoľko desiatok klastrov. Najrýchlejší klaster spracuje viac ako 4 milióny operácií za sekundu a najväčší ukladá 260 TB.

Všetko sú to však bežné klastre NoSQL používané na ukladanie slabo koordinované údajov. Chceli sme nahradiť hlavné konzistentné úložisko, Microsoft SQL Server, ktorý sa používa od založenia Odnoklassniki. Úložisko pozostávalo z viac ako 300 strojov SQL Server Standard Edition, ktoré obsahovali 50 TB dát – podnikateľských subjektov. Tieto údaje sa upravujú ako súčasť transakcií ACID a vyžadujú sa vysoká konzistencia.

Na distribúciu údajov medzi uzlami SQL Servera sme použili vertikálne aj horizontálne rozdeľovanie (sharding). Historicky sme používali jednoduchú schému zdieľania údajov: každá entita bola spojená s tokenom – funkciou ID entity. Entity s rovnakým tokenom boli umiestnené na rovnaký server SQL. Vzťah master-detail bol implementovaný tak, že tokeny hlavného a podriadeného záznamu sa vždy zhodovali a nachádzali sa na rovnakom serveri. V sociálnej sieti sa takmer všetky záznamy generujú v mene používateľa – to znamená, že všetky používateľské údaje v rámci jedného funkčného podsystému sú uložené na jednom serveri. To znamená, že obchodná transakcia takmer vždy zahŕňala tabuľky z jedného SQL servera, čo umožnilo zabezpečiť konzistentnosť údajov pomocou lokálnych ACID transakcií bez potreby použitia pomalé a nespoľahlivé distribuované transakcie ACID.

Vďaka shardingu a zrýchleniu SQL:

  • Nepoužívame obmedzenia cudzieho kľúča, pretože pri zdieľaní môže byť ID entity umiestnené na inom serveri.
  • Uložené procedúry a spúšťače nepoužívame kvôli dodatočnému zaťaženiu CPU DBMS.
  • Nepoužívame JOINy ​​z dôvodu vyššie uvedeného a množstva náhodných čítaní z disku.
  • Mimo transakcie používame úroveň izolácie Read Uncommitted na zníženie zablokovania.
  • Vykonávame len krátke transakcie (v priemere kratšie ako 100 ms).
  • Viacriadkové UPDATE a DELETE nepoužívame kvôli veľkému počtu uviaznutia – aktualizujeme vždy len jeden záznam.
  • Dopyty vykonávame vždy len na indexy – dotaz s plánom skenovania celej tabuľky pre nás znamená preťaženie databázy a jej zlyhanie.

Tieto kroky nám umožnili vytlačiť takmer maximálny výkon zo serverov SQL. Problémov však bolo čoraz viac. Pozrime sa na ne.

Problémy s SQL

  • Keďže sme používali vlastnoručne písaný sharding, pridávanie nových fragmentov robili manuálne správcovia. Po celý tento čas škálovateľné repliky údajov nespĺňali požiadavky.
  • S rastúcim počtom záznamov v tabuľke sa znižuje rýchlosť vkladania a modifikácie, pri pridávaní indexov do existujúcej tabuľky sa rýchlosť o faktor znižuje, vytváranie a opätovné vytváranie indexov nastáva s prestojmi.
  • Malé množstvo produkčného systému Windows pre SQL Server sťažuje správu infraštruktúry

Ale hlavný problém je

odolnosť proti chybám

Klasický SQL server má slabú odolnosť voči chybám. Povedzme, že máte len jeden databázový server a ten zlyhá raz za tri roky. Počas tejto doby je stránka mimo prevádzky na 20 minút, čo je prijateľné. Ak máte 64 serverov, stránka je mimo prevádzky raz za tri týždne. A ak máte 200 serverov, stránka nefunguje každý týždeň. Toto je problém.

Čo možno urobiť na zlepšenie odolnosti servera SQL voči chybám? Wikipedia nás pozýva stavať vysoko dostupný klaster: kde v prípade poruchy niektorého z komponentov existuje záložný.

Vyžaduje si to flotilu drahých zariadení: početné duplikácie, optické vlákno, zdieľané úložisko a zahrnutie rezervy nefunguje spoľahlivo: asi 10 % prepojení končí zlyhaním záložného uzla ako vlak za hlavným uzlom.

No hlavnou nevýhodou takéhoto vysoko dostupného klastra je nulová dostupnosť, ak zlyhá dátové centrum, v ktorom sa nachádza. Odnoklassniki má štyri dátové centrá a my potrebujeme zabezpečiť prevádzku v prípade úplného zlyhania jedného z nich.

Na to by sme mohli použiť Multi-Master replikácia zabudovaná do SQL Servera. Toto riešenie je oveľa drahšie kvôli cene softvéru a trpí známymi problémami s replikáciou – nepredvídateľné oneskorenia transakcií pri synchrónnej replikácii a oneskorenia pri aplikácii replikácií (a v dôsledku toho aj stratené úpravy) pri asynchrónnej replikácii. Naznačené manuálne riešenie konfliktov robí túto možnosť pre nás úplne neaplikovateľnou.

Všetky tieto problémy si vyžadovali radikálne riešenie a začali sme ich podrobne analyzovať. Tu sa musíme zoznámiť s tým, čo SQL Server robí hlavne – transakciami.

Jednoduchá transakcia

Uvažujme o najjednoduchšej transakcii z pohľadu aplikovaného SQL programátora: pridanie fotografie do albumu. Albumy a fotografie sú uložené v rôznych platniach. Album má verejné počítadlo fotografií. Potom je takáto transakcia rozdelená do nasledujúcich krokov:

  1. Album zamkneme na kľúč.
  2. Vytvorte záznam v tabuľke fotografií.
  3. Ak má fotografia verejný stav, pridajte do albumu verejné počítadlo fotografií, aktualizujte záznam a potvrďte transakciu.

Alebo v pseudokóde:

TX.start("Albums", id);
Album album = albums.lock(id);
Photo photo = photos.create(…);

if (photo.status == PUBLIC ) {
    album.incPublicPhotosCount();
}
album.update();

TX.commit();

Vidíme, že najbežnejším scenárom obchodnej transakcie je načítanie údajov z databázy do pamäte aplikačného servera, zmena niečoho a uloženie nových hodnôt späť do databázy. Zvyčajne pri takejto transakcii aktualizujeme niekoľko subjektov, niekoľko tabuliek.

Pri vykonávaní transakcie môže dôjsť k súbežnej úprave rovnakých údajov z iného systému. Antispam môže napríklad rozhodnúť, že používateľ je nejakým spôsobom podozrivý, a preto by už všetky fotografie používateľa nemali byť verejné, je potrebné ich poslať na moderovanie, čo znamená zmeniť photo.status na inú hodnotu a vypnúť príslušné počítadlá. Je zrejmé, že ak k tejto operácii dôjde bez záruk atomicity aplikácie a izolácie konkurenčných modifikácií, ako napr. ACID, potom výsledok nebude taký, aký je potrebný – buď počítadlo fotografií ukáže nesprávnu hodnotu, alebo sa neodošlú všetky fotografie na moderovanie.

Počas celej existencie Odnoklassniki bolo napísaných veľa podobného kódu, ktorý manipuluje s rôznymi podnikateľskými subjektmi v rámci jednej transakcie. Na základe skúseností z migrácií na NoSQL z Prípadná konzistentnosť Vieme, že najväčšia výzva (a časová investícia) pochádza z vývoja kódu na udržanie konzistentnosti údajov. Preto sme považovali za hlavnú požiadavku na nové úložisko zabezpečenie skutočných ACID transakcií pre aplikačnú logiku.

Ďalšími, nemenej dôležitými požiadavkami boli:

  • Ak dátové centrum zlyhá, musí byť dostupné čítanie aj zápis do nového úložiska.
  • Udržiavanie súčasnej rýchlosti vývoja. To znamená, že pri práci s novým úložiskom by množstvo kódu malo byť približne rovnaké, nemalo by byť potrebné nič pridávať do úložiska, vyvíjať algoritmy na riešenie konfliktov, udržiavanie sekundárnych indexov atď.
  • Rýchlosť nového úložiska musela byť dosť vysoká ako pri čítaní dát, tak aj pri spracovaní transakcií, čo v skutočnosti znamenalo, že akademicky prísne, univerzálne, ale pomalé riešenia, ako napr. dvojfázové záväzky.
  • Automatické škálovanie za chodu.
  • Používanie bežných lacných serverov bez nutnosti nákupu exotického hardvéru.
  • Možnosť vývoja úložiska vývojármi spoločnosti. Inými slovami, prednosť dostali proprietárne alebo open source riešenia, najlepšie v Jave.

Rozhodnutia, rozhodnutia

Pri analýze možných riešení sme dospeli k dvom možným možnostiam architektúry:

Prvým je zobrať akýkoľvek SQL server a implementovať požadovanú odolnosť voči chybám, škálovací mechanizmus, klaster prepnutia pri zlyhaní, riešenie konfliktov a distribuované, spoľahlivé a rýchle ACID transakcie. Túto možnosť sme ohodnotili ako veľmi netriviálnu a náročnú na prácu.

Druhou možnosťou je vziať si hotové úložisko NoSQL s implementovaným škálovaním, failover klastrom, riešením konfliktov a implementovať transakcie a SQL sami. Na prvý pohľad aj úloha implementácie SQL, nehovoriac o ACID transakciách, vyzerá ako úloha, ktorá bude trvať roky. Potom sme si však uvedomili, že sada funkcií SQL, ktorú používame v praxi, je vzdialená od ANSI SQL ako Cassandra CQL ďaleko od ANSI SQL. Pri ešte bližšom pohľade na CQL sme si uvedomili, že je to celkom blízko tomu, čo sme potrebovali.

Cassandra a CQL

Čo je teda na Cassandre zaujímavé, aké má schopnosti?

Po prvé, tu môžete vytvoriť tabuľky, ktoré podporujú rôzne typy údajov; na primárnom kľúči môžete vykonať SELECT alebo UPDATE.

CREATE TABLE photos (id bigint KEY, owner bigint,…);
SELECT * FROM photos WHERE id=?;
UPDATE photos SET … WHERE id=?;

Na zabezpečenie konzistencie údajov repliky používa Cassandra kvórový prístup. V najjednoduchšom prípade to znamená, že keď sú tri repliky toho istého riadka umiestnené na rôznych uzloch klastra, zápis sa považuje za úspešný, ak väčšina uzlov (t. j. dva z troch) potvrdila úspech tejto operácie zápisu. . Údaje riadku sa považujú za konzistentné, ak pri čítaní bola väčšina uzlov vyzvaná a potvrdená. Pri troch replikách je teda zaručená úplná a okamžitá konzistencia údajov, ak jeden uzol zlyhá. Tento prístup nám umožnil implementovať ešte spoľahlivejšiu schému: vždy posielať požiadavky na všetky tri repliky, pričom čakáme na odpoveď od dvoch najrýchlejších. Neskorá odpoveď tretej repliky sa v tomto prípade zahodí. Uzol, ktorý neskoro reaguje, môže mať vážne problémy – brzdy, zber odpadu v JVM, priame obnovenie pamäte v jadre Linuxu, zlyhanie hardvéru, odpojenie od siete. To však žiadnym spôsobom neovplyvňuje operácie ani dáta klienta.

Zavolá sa prístup, keď kontaktujeme tri uzly a dostaneme odpoveď od dvoch špekulácie: žiadosť o extra repliky sa odošle ešte skôr, ako „padne“.

Ďalšou výhodou Cassandry je Batchlog, mechanizmus, ktorý zaisťuje, že dávka zmien, ktoré vykonáte, sa buď úplne použije, alebo sa nepoužije vôbec. To nám umožňuje vyriešiť A v ACID - atomicita po vybalení z krabice.

Najbližšie k transakciám v Cassandre sú tzv.ľahké transakcie". Ale sú ďaleko od „skutočných“ transakcií ACID: v skutočnosti je to príležitosť CAS na údajoch iba z jedného záznamu, s použitím konsenzu s použitím ťažkého protokolu Paxos. Preto je rýchlosť takýchto transakcií nízka.

Čo nám v Cassandre chýbalo

Takže sme museli implementovať skutočné ACID transakcie v Cassandre. Pomocou čoho by sme mohli jednoducho implementovať dve ďalšie pohodlné funkcie klasickej DBMS: konzistentné rýchle indexy, ktoré by nám umožnili vykonávať výbery dát nielen podľa primárneho kľúča, a pravidelný generátor monotónnych automaticky sa zvyšujúcich ID.

C*Jeden

Tak sa zrodil nový DBMS C*Jeden, ktorý pozostáva z troch typov serverových uzlov:

  • Úložisko – (takmer) štandardné servery Cassandra zodpovedné za ukladanie dát na lokálne disky. S rastúcou záťažou a objemom dát sa ich množstvo dá jednoducho škálovať na desiatky a stovky.
  • Koordinátori transakcií – zabezpečujú realizáciu transakcií.
  • Klienti sú aplikačné servery, ktoré implementujú obchodné operácie a iniciujú transakcie. Takých klientov môžu byť tisíce.

NewSQL = NoSQL+ACID

Servery všetkých typov sú súčasťou spoločného klastra, na vzájomnú komunikáciu používajú interný protokol správ Cassandra klebety na výmenu informácií o klastri. Pomocou Heartbeat sa servery dozvedia o vzájomných zlyhaniach, udržiavajú jednu dátovú schému – tabuľky, ich štruktúru a replikáciu; deliaca schéma, topológia klastrov atď.

Klienti

NewSQL = NoSQL+ACID

Namiesto štandardných ovládačov sa používa režim Fat Client. Takýto uzol neuchováva údaje, ale môže pôsobiť ako koordinátor pre realizáciu požiadaviek, to znamená, že samotný Klient vystupuje ako koordinátor jeho požiadaviek: dopytuje repliky úložiska a rieši konflikty. Ten je nielen spoľahlivejší a rýchlejší ako štandardný ovládač, ktorý vyžaduje komunikáciu so vzdialeným koordinátorom, ale umožňuje aj kontrolu prenosu požiadaviek. Mimo transakcie otvorenej na klientovi sa požiadavky odosielajú do úložísk. Ak klient otvoril transakciu, všetky požiadavky v rámci transakcie sa odošlú koordinátorovi transakcie.
NewSQL = NoSQL+ACID

C* One Transaction Coordinator

Koordinátor je niečo, čo sme pre C*One implementovali od nuly. Je zodpovedný za správu transakcií, zámkov a poradia, v ktorom sa transakcie aplikujú.

Pre každú obsluhovanú transakciu generuje koordinátor časovú pečiatku: každá nasledujúca transakcia je väčšia ako predchádzajúca transakcia. Keďže Cassandrin systém riešenia konfliktov je založený na časových pečiatkach (z dvoch konfliktných záznamov sa za aktuálny považuje ten s najnovšou časovou pečiatkou), konflikt sa vždy vyrieši v prospech následnej transakcie. Takto sme implementovali Hodinky Lamport - lacný spôsob riešenia konfliktov v distribuovanom systéme.

Zámky

Na zabezpečenie izolácie sme sa rozhodli použiť najjednoduchšiu metódu – pesimistické zámky založené na primárnom kľúči záznamu. Inými slovami, v transakcii musí byť záznam najskôr uzamknutý, až potom prečítaný, upravený a uložený. Až po úspešnom odovzdaní môže byť záznam odomknutý, aby ho mohli použiť konkurenčné transakcie.

Implementácia takéhoto uzamykania je jednoduchá v nedistribuovanom prostredí. V distribuovanom systéme existujú dve hlavné možnosti: buď implementovať distribuované uzamykanie na klastri, alebo distribuovať transakcie tak, aby transakcie zahŕňajúce rovnaký záznam boli vždy obsluhované tým istým koordinátorom.

Keďže v našom prípade sú údaje už distribuované medzi skupiny lokálnych transakcií v SQL, bolo rozhodnuté priradiť lokálne skupiny transakcií koordinátorom: jeden koordinátor vykonáva všetky transakcie s tokenmi od 0 do 9, druhý - s tokenmi od 10 do 19, a tak ďalej. Výsledkom je, že každá inštancia koordinátora sa stane hlavným orgánom transakčnej skupiny.

Potom môžu byť zámky implementované vo forme banálnej HashMap v pamäti koordinátora.

Zlyhania koordinátora

Keďže jeden koordinátor obsluhuje výlučne skupinu transakcií, je veľmi dôležité rýchlo zistiť skutočnosť jeho zlyhania, aby druhý pokus o vykonanie transakcie vypršal. Aby to bolo rýchle a spoľahlivé, použili sme plne prepojený protokol tlkotu kvóra:

Každé dátové centrum hostí aspoň dva uzly koordinátora. Každý koordinátor pravidelne posiela správu o tepovej frekvencii ostatným koordinátorom a informuje ich o jej fungovaní, ako aj o tom, ktoré správy o tepovej frekvencii od ktorých koordinátorov v klastri naposledy dostal.

NewSQL = NoSQL+ACID

Každý koordinátor, ktorý dostáva podobné informácie od ostatných ako súčasť svojich srdcových správ, sa sám rozhodne, ktoré uzly klastra fungujú a ktoré nie, pričom sa riadi princípom kvóra: ak uzol X prijal informácie od väčšiny uzlov v klastri o normálnom stave príjem správ z uzla Y, potom , Y funguje. A naopak, akonáhle väčšina oznámi chýbajúce správy z uzla Y, potom Y odmietne. Je zvláštne, že ak kvórum informuje uzol X, že už od neho nedostáva správy, samotný uzol X sa bude považovať za zlyhaný.

Správy srdcového tepu sa odosielajú s vysokou frekvenciou, približne 20-krát za sekundu, s periódou 50 ms. V Jave je ťažké zaručiť odozvu aplikácie do 50 ms kvôli porovnateľnej dĺžke prestávok spôsobených garbage collectorom. Tento čas odozvy sa nám podarilo dosiahnuť pomocou zberača odpadu G1, ktorý nám umožňuje určiť cieľ pre trvanie GC prestávok. Avšak niekedy, pomerne zriedka, prestávky kolektora prekročia 50 ms, čo môže viesť k chybnej detekcii poruchy. Aby sa tak nestalo, koordinátor nehlási poruchu vzdialeného uzla, keď z neho zmizne prvá tlkotová správa, iba ak zmizlo niekoľko za sebou.Takto sa nám podarilo odhaliť poruchu uzla koordinátora v roku 200 pani.

Nestačí však rýchlo pochopiť, ktorý uzol prestal fungovať. Musíme s tým niečo urobiť.

Rezervácia

Klasická schéma zahŕňa v prípade hlavného zlyhania začatie nových volieb pomocou jedného z módne univerzálne algoritmy. Takéto algoritmy však majú dobre známe problémy s časovou konvergenciou a dĺžkou samotného volebného procesu. Dokázali sme sa vyhnúť takýmto dodatočným oneskoreniam pomocou schémy výmeny koordinátora v plne prepojenej sieti:

NewSQL = NoSQL+ACID

Povedzme, že chceme vykonať transakciu v skupine 50. Vopred si určme náhradnú schému, teda ktoré uzly vykonajú transakcie v skupine 50 v prípade zlyhania hlavného koordinátora. Naším cieľom je zachovať funkčnosť systému v prípade zlyhania dátového centra. Určme, že prvá rezerva bude uzol z iného dátového centra a druhá rezerva bude uzol z tretieho. Táto schéma sa vyberie raz a nezmení sa, kým sa nezmení topológia klastra, teda kým doň nevstúpia nové uzly (čo sa stáva veľmi zriedka). Postup pri výbere nového aktívneho mastera, ak starý zlyhá, bude vždy nasledovný: prvá rezerva sa stane aktívnou master, a ak prestane fungovať, druhá rezerva sa stane aktívnou master.

Táto schéma je spoľahlivejšia ako univerzálny algoritmus, pretože na aktiváciu nového mastera stačí určiť zlyhanie starého.

Ako však klienti pochopia, ktorý majster teraz pracuje? Nie je možné odoslať informácie tisíckam klientov za 50 ms. Môže nastať situácia, keď klient odošle požiadavku na otvorenie transakcie, pričom ešte nevie, že tento master už nefunguje, a požiadavka vyprší. Aby sa tak nestalo, klienti špekulatívne posielajú žiadosť o otvorenie transakcie správcovi skupiny a obom jeho rezervám naraz, no na túto požiadavku zareaguje len ten, kto je momentálne aktívnym majstrom. Všetku následnú komunikáciu v rámci transakcie bude klient realizovať len s aktívnym masterom.

Majstri záloh umiestňujú prijaté požiadavky na transakcie, ktoré nie sú ich, do frontu nenarodených transakcií, kde sú na nejaký čas uložené. Ak aktívny hlavný server zomrie, nový hlavný server spracuje požiadavky na otvorenie transakcií zo svojho frontu a odpovie klientovi. Ak klient už otvoril transakciu so starým masterom, potom sa druhá odpoveď ignoruje (a samozrejme, takáto transakcia sa nedokončí a klient ju zopakuje).

Ako transakcia funguje

Povedzme, že klient poslal koordinátorovi požiadavku na otvorenie transakcie pre taký a taký subjekt s takým a takým primárnym kľúčom. Koordinátor uzamkne túto entitu a umiestni ju do tabuľky zámkov v pamäti. V prípade potreby koordinátor načíta túto entitu z úložiska a výsledné dáta uloží v stave transakcie do pamäte koordinátora.

NewSQL = NoSQL+ACID

Keď chce klient zmeniť údaje v transakcii, pošle koordinátorovi požiadavku na úpravu entity a koordinátor umiestni nové údaje do tabuľky stavu transakcie v pamäti. Tým je nahrávanie dokončené – do úložiska sa neuskutoční žiadny záznam.

NewSQL = NoSQL+ACID

Keď klient požaduje svoje vlastné zmenené údaje ako súčasť aktívnej transakcie, koordinátor koná takto:

  • ak je ID už v transakcii, potom sa údaje prevezmú z pamäte;
  • ak v pamäti nie je žiadne ID, chýbajúce dáta sa načítajú z úložných uzlov, kombinujú sa s tými, ktoré sú už v pamäti, a výsledok sa odovzdá klientovi.

Klient si teda môže prečítať svoje vlastné zmeny, ale ostatní klienti tieto zmeny nevidia, pretože sú uložené iba v pamäti koordinátora, ešte nie sú v uzloch Cassandra.

NewSQL = NoSQL+ACID

Keď klient odošle potvrdenie, stav, ktorý bol v pamäti služby, uloží koordinátor do protokolovanej dávky a odošle sa ako protokolovaná dávka do úložiska Cassandra. Predajne urobia všetko potrebné pre to, aby bol tento balík atomicky (úplne) aplikovaný, a vrátia odpoveď koordinátorovi, ktorý uvoľní zámky a potvrdí klientovi úspešnosť transakcie.

NewSQL = NoSQL+ACID

A na vrátenie späť potrebuje koordinátor iba uvoľniť pamäť obsadenú stavom transakcie.

V dôsledku vyššie uvedených zlepšení sme implementovali princípy ACID:

  • Atomicita. Je to záruka, že žiadna transakcia nebude čiastočne zaznamenaná v systéme, buď budú dokončené všetky jej čiastkové operácie, alebo nebude dokončená žiadna. Tento princíp dodržiavame prostredníctvom logovanej dávky v Cassandre.
  • Dôslednosť. Každá úspešná transakcia podľa definície zaznamenáva iba platné výsledky. Ak sa po otvorení transakcie a vykonaní časti operácií zistí, že výsledok je neplatný, vykoná sa rollback.
  • Izolácia. Keď sa transakcia vykoná, súbežné transakcie by nemali ovplyvniť jej výsledok. Konkurenčné transakcie sú izolované pomocou pesimistických zámkov na koordinátorovi. Pre čítania mimo transakcie sa princíp izolácie dodržiava na úrovni Read Committed.
  • stabilita. Bez ohľadu na problémy na nižších úrovniach – výpadok systému, zlyhanie hardvéru – by zmeny vykonané úspešne dokončenou transakciou mali zostať zachované aj pri obnovení prevádzky.

Čítanie podľa indexov

Zoberme si jednoduchú tabuľku:

CREATE TABLE photos (
id bigint primary key,
owner bigint,
modified timestamp,
…)

Má ID (primárny kľúč), vlastníka a dátum úpravy. Musíte podať veľmi jednoduchú požiadavku - vybrať údaje o vlastníkovi s dátumom zmeny „za posledný deň“.

SELECT *
WHERE owner=?
AND modified>?

Aby bol takýto dotaz spracovaný rýchlo, v klasickom SQL DBMS je potrebné zostaviť index podľa stĺpcov (vlastník, upravený). Môžeme to urobiť celkom jednoducho, pretože teraz máme záruky KYSELINA!

Indexy v C*One

Existuje zdrojová tabuľka s fotografiami, v ktorej je primárnym kľúčom ID záznamu.

NewSQL = NoSQL+ACID

Pre index vytvorí C*One novú tabuľku, ktorá je kópiou originálu. Kľúč je rovnaký ako výraz indexu a obsahuje aj primárny kľúč záznamu zo zdrojovej tabuľky:

NewSQL = NoSQL+ACID

Teraz je možné dotaz na „vlastníka za posledný deň“ prepísať ako výber z inej tabuľky:

SELECT * FROM i1_test
WHERE owner=?
AND modified>?

Konzistencia údajov vo fotografiách zdrojovej tabuľky a indexovej tabuľke i1 je automaticky udržiavaná koordinátorom. Na základe samotnej dátovej schémy koordinátor pri prijatí zmeny vygeneruje a uloží zmenu nielen v hlavnej tabuľke, ale aj v kópiách. Na indexovej tabuľke sa nevykonávajú žiadne ďalšie akcie, protokoly sa nečítajú a nepoužívajú sa žiadne zámky. To znamená, že pridávanie indexov nespotrebováva takmer žiadne zdroje a nemá prakticky žiadny vplyv na rýchlosť aplikácie úprav.

Pomocou ACID sme boli schopní implementovať indexy podobné SQL. Sú konzistentné, škálovateľné, rýchle, skladateľné a zabudované do dopytovacieho jazyka CQL. Na podporu indexov nie sú potrebné žiadne zmeny v kóde aplikácie. Všetko je také jednoduché ako v SQL. A čo je najdôležitejšie, indexy neovplyvňujú rýchlosť vykonávania úprav pôvodnej tabuľky transakcií.

Čo sa stalo

C*One sme vyvinuli pred tromi rokmi a uviedli do komerčnej prevádzky.

Čo sme nakoniec dostali? Zhodnoťme to na príklade subsystému spracovania a ukladania fotografií, jedného z najdôležitejších typov údajov v sociálnej sieti. Nehovoríme o telách samotných fotografií, ale o všemožných metainformáciách. Teraz má Odnoklassniki asi 20 miliárd takýchto záznamov, systém spracováva 80 8 žiadostí o čítanie za sekundu, až XNUMX XNUMX transakcií ACID za sekundu spojených s úpravou údajov.

Keď sme použili SQL s replikačným faktorom = 1 (ale v RAID 10), metainformácie fotografie boli uložené na vysoko dostupnom klastri 32 počítačov so serverom Microsoft SQL Server (plus 11 záloh). Na ukladanie záloh bolo vyčlenených aj 10 serverov. Spolu 50 drahých áut. Systém zároveň pracoval pri menovitom zaťažení, bez rezervy.

Po migrácii na nový systém sme dostali replikačný faktor = 3 – kópiu v každom dátovom centre. Systém pozostáva zo 63 úložných uzlov Cassandra a 6 koordinačných strojov, spolu 69 serverov. Ale tieto stroje sú oveľa lacnejšie, ich celková cena je asi 30% nákladov na SQL systém. Súčasne sa zaťaženie udržiava na 30%.

So zavedením C*One sa tiež znížila latencia: v SQL trvala operácia zápisu asi 4,5 ms. V C*One - asi 1,6 ms. Trvanie transakcie je v priemere menej ako 40 ms, potvrdenie je dokončené za 2 ms, dĺžka čítania a zápisu je v priemere 2 ms. 99. percentil - iba 3-3,1 ms, počet prestávok sa 100-krát znížil - všetko kvôli rozšírenému využívaniu špekulácií.

V súčasnosti je väčšina uzlov SQL Servera vyradená z prevádzky, nové produkty sa vyvíjajú iba pomocou C*One. Prispôsobili sme C*One, aby fungoval v našom cloude jeden oblak, čo umožnilo urýchliť nasadenie nových klastrov, zjednodušiť konfiguráciu a zautomatizovať prevádzku. Bez zdrojového kódu by to bolo oveľa zložitejšie a ťažkopádnejšie.

Teraz pracujeme na presune našich ďalších úložných zariadení do cloudu – ale to je úplne iný príbeh.

Zdroj: hab.com

Pridať komentár