NewSQL = NoSQL + ACID

NewSQL = NoSQL + ACID
Dar visai neseniai „Odnoklassniki“ saugojo apie 50 TB realiuoju laiku apdorotų duomenų „SQL Server“. Tokiam kiekiui beveik neįmanoma užtikrinti greitos, patikimos ir net duomenų centro gedimams atsparios prieigos naudojant SQL DBVS. Paprastai tokiais atvejais naudojama viena iš NoSQL saugyklų, tačiau ne viską galima perkelti į NoSQL: kai kuriems subjektams reikalingos ACID sandorių garantijos.

Tai paskatino mus naudoti NewSQL saugyklą, tai yra DBVS, kuri užtikrina NoSQL sistemų atsparumą gedimams, mastelio keitimą ir našumą, tačiau kartu išlaiko klasikinėms sistemoms pažįstamas ACID garantijas. Šios naujos klasės pramoninių sistemų yra nedaug, todėl tokią sistemą įdiegėme patys ir pradėjome komerciškai eksploatuoti.

Kaip tai veikia ir kas atsitiko – skaitykite po pjūviu.

Šiandien „Odnoklassniki“ mėnesio auditorija yra daugiau nei 70 milijonų unikalių lankytojų. Mes Esame geriausiųjų penketuke didžiausi socialiniai tinklai pasaulyje ir tarp dvidešimties svetainių, kuriose vartotojai praleidžia daugiausiai laiko. OK infrastruktūra apdoroja labai dideles apkrovas: daugiau nei milijonas HTTP užklausų per sekundę. Daugiau nei 8000 vienetų serverių parko dalys yra arti viena kitos – keturiuose Maskvos duomenų centruose, todėl tarp jų tinklo delsa yra mažesnė nei 1 ms.

„Cassandra“ naudojame nuo 2010 m., pradedant nuo 0.6 versijos. Šiandien veikia kelios dešimtys grupių. Greičiausias klasteris apdoroja daugiau nei 4 milijonus operacijų per sekundę, o didžiausiose parduotuvėse – 260 TB.

Tačiau tai yra įprasti NoSQL klasteriai, naudojami saugojimui silpnai koordinuotas duomenis. Norėjome pakeisti pagrindinę nuoseklią saugyklą „Microsoft SQL Server“, kuri buvo naudojama nuo „Odnoklassniki“ įkūrimo. Saugyklą sudarė daugiau nei 300 SQL Server Standard Edition mašinų, kuriose buvo 50 TB duomenų – verslo subjektų. Šie duomenys yra modifikuojami kaip ACID operacijų dalis ir reikalaujama aukšta konsistencija.

Norėdami paskirstyti duomenis tarp SQL serverio mazgų, naudojome vertikalius ir horizontalius skaidymas (skaldymas). Istoriškai naudojome paprastą duomenų skirstymo schemą: kiekvienas objektas buvo susietas su prieigos raktu – objekto ID funkcija. Objektai su tuo pačiu prieigos raktu buvo patalpinti į tą patį SQL serverį. Pagrindinis ir detalės ryšys buvo įgyvendintas taip, kad pagrindinio ir antrinio įrašo prieigos raktai visada atitiktų ir būtų tame pačiame serveryje. Socialiniame tinkle beveik visi įrašai generuojami vartotojo vardu, o tai reiškia, kad visi vieno funkcinio posistemio vartotojo duomenys yra saugomi viename serveryje. Tai reiškia, kad verslo operacija beveik visada apėmė lenteles iš vieno SQL serverio, o tai leido užtikrinti duomenų nuoseklumą naudojant vietines ACID operacijas, nenaudojant lėtas ir nepatikimas paskirstytos ACID operacijos.

Dėl dalijimosi ir pagreitinto SQL:

  • Mes nenaudojame svetimo rakto apribojimų, nes dalijant objekto ID gali būti kitame serveryje.
  • Mes nenaudojame saugomų procedūrų ir trigerių dėl papildomos DBVS procesoriaus apkrovos.
  • Mes nenaudojame JOIN dėl visų aukščiau išvardytų dalykų ir daugybės atsitiktinių nuskaitymų iš disko.
  • Už operacijos ribų mes naudojame "Read Uncommitted" izoliacijos lygį, kad sumažintume aklavietes.
  • Atliekame tik trumpas operacijas (vidutiniškai trumpesnes nei 100 ms).
  • Kelių eilučių UPDATE ir DELETE nenaudojame dėl didelio aklavietės skaičiaus – vienu metu atnaujiname tik vieną įrašą.
  • Visada atliekame užklausas tik indeksams – užklausa su pilnu lentelės nuskaitymo planu mums reiškia duomenų bazės perkrovimą ir jos nesėkmę.

Šie veiksmai leido mums išspausti beveik maksimalų SQL serverių našumą. Tačiau problemų vis daugėjo. Pažiūrėkime į juos.

Problemos su SQL

  • Kadangi naudojome savarankiškai parašytą dalijimąsi, naujų skeveldrų pridėjimą rankiniu būdu atliko administratoriai. Visą šį laiką keičiamo dydžio duomenų kopijos neaptarnavo užklausų.
  • Didėjant įrašų skaičiui lentelėje, mažėja įterpimo ir modifikavimo greitis, kai pridedami indeksai, su prastovomis atsiranda indeksų kūrimo ir atkūrimo greitis.
  • Turint nedidelį gamybinės versijos „Windows“, skirtą SQL Server, sunku valdyti infrastruktūrą

Tačiau pagrindinė problema yra

atsparumas gedimams

Klasikinis SQL serveris yra prastai atsparus gedimams. Tarkime, kad turite tik vieną duomenų bazės serverį ir jis sugenda kartą per trejus metus. Per šį laiką svetainė neveikia 20 minučių, o tai yra priimtina. Jei turite 64 serverius, svetainė neveikia kartą per tris savaites. Ir jei turite 200 serverių, tada svetainė neveikia kiekvieną savaitę. Tai yra problema.

Ką galima padaryti norint pagerinti SQL serverio atsparumą gedimams? Vikipedija kviečia mus kurti labai prieinamas klasteris: kur sugedus kuriam nors komponentui yra atsarginis.

Tam reikia brangios įrangos parko: daugybė dubliavimų, šviesolaidis, bendra saugykla, o rezervo įtraukimas neveikia patikimai: apie 10% perjungimų baigiasi atsarginio mazgo gedimu kaip traukinys už pagrindinio mazgo.

Tačiau pagrindinis tokio didelio prieinamumo klasterio trūkumas yra nulinis pasiekiamumas, jei sugenda duomenų centras, kuriame jis yra. „Odnoklassniki“ turi keturis duomenų centrus, ir mes turime užtikrinti veikimą visiškai sugedus vienam iš jų.

Tam galėtume panaudoti Multi-Master replikacija, integruota į SQL serverį. Šis sprendimas yra daug brangesnis dėl programinės įrangos kainos ir kenčia nuo gerai žinomų replikacijos problemų - nenuspėjamų operacijų vėlavimų su sinchroniniu replikavimu ir replikacijų taikymo vėlavimais (ir dėl to prarastų modifikacijų) su asinchroniniu replikavimu. Numanomas rankinis konfliktų sprendimas todėl ši parinktis mums visiškai netaikoma.

Visos šios problemos reikalavo radikalaus sprendimo, ir mes pradėjome jas detaliai analizuoti. Čia turime susipažinti su tuo, kuo daugiausia užsiima SQL Server – sandoriais.

Paprastas sandoris

Panagrinėkime paprasčiausią operaciją taikomojo SQL programuotojo požiūriu: nuotraukos įtraukimą į albumą. Albumai ir nuotraukos saugomi skirtingose ​​plokštelėse. Albume yra viešas nuotraukų skaitiklis. Tada tokia operacija yra padalinta į šiuos veiksmus:

  1. Rakiname albumą raktu.
  2. Sukurkite įrašą nuotraukų lentelėje.
  3. Jei nuotraukos būsena yra vieša, pridėkite viešą nuotraukų skaitiklį prie albumo, atnaujinkite įrašą ir atlikite operaciją.

Arba pseudokode:

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

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

TX.commit();

Matome, kad labiausiai paplitęs verslo sandorio scenarijus yra nuskaityti duomenis iš duomenų bazės į taikomųjų programų serverio atmintį, ką nors pakeisti ir išsaugoti naujas reikšmes atgal į duomenų bazę. Paprastai tokioje operacijoje mes atnaujiname kelis subjektus, kelias lenteles.

Vykdant operaciją, tuo pačiu metu gali būti keičiami tie patys duomenys iš kitos sistemos. Pavyzdžiui, Antispam gali nuspręsti, kad vartotojas yra kažkaip įtartinas ir todėl visos vartotojo nuotraukos nebeturi būti viešos, jas reikia siųsti moderuoti, o tai reiškia pakeisti photo.status į kokią nors kitą reikšmę ir išjungti atitinkamus skaitiklius. Akivaizdu, kad jei ši operacija atliekama be garantijų dėl taikymo atomiškumo ir konkuruojančių modifikacijų izoliacijos, kaip RŪGŠTIS, tada rezultatas bus ne toks, kokio reikia – arba nuotraukų skaitiklis parodys neteisingą reikšmę, arba ne visos nuotraukos bus išsiųstos moderuoti.

Per visą Odnoklassniki egzistavimą buvo parašyta daug panašaus kodo, manipuliuojančio įvairiais verslo subjektais per vieną operaciją. Remiantis migracijos į NoSQL patirtimi iš Galutinis nuoseklumas Žinome, kad didžiausias iššūkis (ir laiko investicijos) kyla kuriant kodą siekiant išlaikyti duomenų nuoseklumą. Todėl manėme, kad pagrindinis naujos saugyklos reikalavimas yra realių ACID operacijų, skirtų taikymo logikai, užtikrinimas.

Kiti, ne mažiau svarbūs reikalavimai buvo:

  • Jei duomenų centras sugenda, turi būti galima skaityti ir rašyti į naują saugyklą.
  • Išlaikyti esamą plėtros greitį. Tai yra, dirbant su nauja saugykla, kodo kiekis turėtų būti maždaug toks pat, nereikia nieko pridėti prie saugyklos, kurti konfliktų sprendimo algoritmus, palaikyti antrinius indeksus ir pan.
  • Naujos saugyklos greitis turėjo būti gana didelis tiek nuskaitant duomenis, tiek apdorojant operacijas, o tai reiškė, kad akademiškai griežti, universalūs, bet lėti sprendimai, kaip pvz. dviejų fazių įsipareigojimai.
  • Automatinis mastelio keitimas skrendant.
  • Naudojant įprastus pigius serverius, nereikia pirkti egzotiškos techninės įrangos.
  • Įmonės kūrėjų galimybė plėtoti saugyklą. Kitaip tariant, pirmenybė buvo teikiama patentuotiems arba atvirojo kodo sprendimams, pageidautina Java.

Sprendimai, sprendimai

Analizuodami galimus sprendimus, priėjome prie dviejų galimų architektūros pasirinkimų:

Pirmiausia reikia paimti bet kurį SQL serverį ir įdiegti reikiamą atsparumą gedimams, mastelio keitimo mechanizmą, perjungimo klasterį, konfliktų sprendimą ir paskirstytas, patikimas ir greitas ACID operacijas. Šią parinktį įvertinome kaip labai nebanalią ir daug darbo reikalaujančią.

Antrasis variantas – paimti paruoštą NoSQL saugyklą su įdiegtu mastelio keitimu, perjungimo klasteriu, konfliktų sprendimu ir patiems įdiegti operacijas bei SQL. Iš pirmo žvilgsnio net SQL diegimo užduotis, jau nekalbant apie ACID operacijas, atrodo kaip užduotis, kuri užtruks ne vienerius metus. Bet tada supratome, kad SQL funkcijų rinkinys, kurį naudojame praktiškai, yra toks pat toli nuo ANSI SQL Cassandra CQL toli nuo ANSI SQL. Dar atidžiau pažvelgę ​​į CQL supratome, kad ji yra gana arti to, ko mums reikia.

Cassandra ir CQL

Taigi, kuo Cassandra įdomu, kokias galimybes ji turi?

Pirma, čia galite kurti lenteles, kurios palaiko įvairius duomenų tipus, pagrindiniame rakte galite atlikti SELECT arba UPDATE.

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

Siekdama užtikrinti kopijos duomenų nuoseklumą, Cassandra naudoja kvorumo principą. Paprasčiausiu atveju tai reiškia, kad kai trys tos pačios eilutės kopijos dedamos į skirtingus klasterio mazgus, įrašymas laikomas sėkmingu, jei dauguma mazgų (ty du iš trijų) patvirtino šios rašymo operacijos sėkmę. . Eilučių duomenys laikomi nuosekliais, jei skaitant dauguma mazgų buvo apklausti ir patvirtinti. Taigi, naudojant tris kopijas, visiškas ir momentinis duomenų nuoseklumas garantuojamas, jei vienas mazgas sugenda. Šis metodas leido įgyvendinti dar patikimesnę schemą: visada siųsti užklausas visoms trims kopijoms, laukiant atsakymo iš dviejų greičiausių. Šiuo atveju pavėluotas trečiosios kopijos atsakymas atmetamas. Pavėluotai reaguojantis mazgas gali turėti rimtų problemų – stabdžių, šiukšlių surinkimo JVM, tiesioginio atminties atkūrimo Linux branduolyje, aparatinės įrangos gedimo, atsijungimo nuo tinklo. Tačiau tai neturi jokios įtakos kliento operacijoms ar duomenims.

Vadinamas metodas, kai susisiekiame su trimis mazgais ir gauname atsakymą iš dviejų spekuliacija: užklausa dėl papildomų kopijų išsiunčiama dar prieš jai „nukritus“.

Kitas „Cassandra“ pranašumas yra „Batchlog“ – mechanizmas, užtikrinantis, kad jūsų atliekamų pakeitimų paketas būtų visiškai pritaikytas arba visai nepritaikytas. Tai leidžia mums išspręsti A rūgštyje – atomiškumas iš dėžutės.

Arčiausiai sandorių Cassandra yra vadinamieji „lengvi sandoriai“. Tačiau jie toli gražu nėra „tikrieji“ ACID sandoriai: iš tikrųjų tai yra galimybė tai padaryti CAS tik vieno įrašo duomenimis, remiantis konsensusu naudojant sunkiasvorį Paxos protokolą. Todėl tokių operacijų greitis yra mažas.

Ko mums trūko Kasandroje

Taigi, Cassandroje turėjome įgyvendinti tikras ACID operacijas. Juo pasinaudodami galėtume nesunkiai įdiegti dar dvi patogias klasikinių DBVS funkcijas: nuoseklius sparčiuosius indeksus, kurie leistų atlikti duomenų pasirinkimą ne tik pagal pirminį raktą, ir įprastą monotoniškų automatiškai didėjančių ID generatorių.

C*One

Taip gimė nauja DBVS C*One, sudarytas iš trijų tipų serverio mazgų:

  • Saugykla – (beveik) standartiniai Cassandra serveriai, atsakingi už duomenų saugojimą vietiniuose diskuose. Augant duomenų apkrovai ir apimčiai, jų kiekį galima nesunkiai padidinti iki dešimčių ir šimtų.
  • Sandorių koordinatoriai – užtikrina operacijų vykdymą.
  • Klientai yra taikomųjų programų serveriai, kurie įgyvendina verslo operacijas ir inicijuoja operacijas. Tokių klientų gali būti tūkstančiai.

NewSQL = NoSQL + ACID

Visų tipų serveriai yra bendro klasterio dalis, naudoja vidinį Cassandra pranešimų protokolą, kad galėtų bendrauti tarpusavyje ir paskalos keitimuisi klasterio informacija. Su Heartbeat serveriai sužino apie abipusius gedimus, palaiko vieną duomenų schemą – lenteles, jų struktūrą ir replikaciją; skaidymo schema, klasterių topologija ir kt.

Klientai

NewSQL = NoSQL + ACID

Vietoj standartinių tvarkyklių naudojamas Fat Client režimas. Toks mazgas nesaugo duomenų, bet gali veikti kaip užklausų vykdymo koordinatorius, tai yra, pats Klientas veikia kaip savo užklausų koordinatorius: užklausas dėl saugyklos replikų ir sprendžia konfliktus. Tai ne tik patikimesnė ir greitesnė nei standartinė tvarkyklė, kuriai reikalingas ryšys su nuotoliniu koordinatoriumi, bet ir leidžia valdyti užklausų perdavimą. Išskyrus kliento atidarytą operaciją, užklausos siunčiamos į saugyklas. Jei klientas atidarė operaciją, visos operacijos užklausos siunčiamos operacijos koordinatoriui.
NewSQL = NoSQL + ACID

C*One sandorių koordinatorius

Koordinatorius yra kažkas, ką mes įdiegėme C*One nuo nulio. Ji atsakinga už operacijų, užraktų ir operacijų taikymo eiliškumo valdymą.

Kiekvienai aptarnaujamai operacijai koordinatorius sukuria laiko žymą: kiekviena paskesnė operacija yra didesnė už ankstesnę operaciją. Kadangi „Cassandra“ konfliktų sprendimo sistema yra pagrįsta laiko žymomis (iš dviejų nesuderinamų įrašų, tas, kurio laiko žyma yra naujausia, laikomas galiojančiu), konfliktas visada bus sprendžiamas vėlesnės operacijos naudai. Taip mes įgyvendinome Lamport laikrodis - pigus būdas išspręsti konfliktus paskirstytoje sistemoje.

Spynos

Norėdami užtikrinti izoliaciją, nusprendėme panaudoti paprasčiausią metodą – pesimistines spynas pagal pirminį įrašo raktą. Kitaip tariant, operacijos metu įrašas pirmiausia turi būti užrakintas, tik tada nuskaitomas, modifikuojamas ir įrašomas. Tik po sėkmingo įsipareigojimo įrašas gali būti atrakintas, kad konkuruojančios operacijos galėtų jį naudoti.

Nepaskirstytoje aplinkoje tokį užrakinimą įgyvendinti paprasta. Paskirstytoje sistemoje yra dvi pagrindinės parinktys: arba įdiegti paskirstytą blokavimą klasteryje, arba paskirstyti operacijas, kad su tuo pačiu įrašu susijusias operacijas visada aptarnautų tas pats koordinatorius.

Kadangi mūsų atveju duomenys jau yra paskirstyti tarp vietinių operacijų grupių SQL, buvo nuspręsta priskirti vietines operacijų grupes koordinatoriams: vienas koordinatorius atlieka visas operacijas su žetonais nuo 0 iki 9, antrasis - su žetonais nuo 10 iki 19, ir taip toliau. Dėl to kiekvienas iš koordinatoriaus egzempliorių tampa operacijų grupės šeimininku.

Tada užraktai gali būti įdiegti banalaus HashMap pavidalu koordinatoriaus atmintyje.

Koordinatoriaus gedimai

Kadangi vienas koordinatorius išskirtinai aptarnauja operacijų grupę, labai svarbu greitai nustatyti jos nesėkmės faktą, kad antrasis bandymas įvykdyti operaciją pasibaigtų laikas. Kad tai būtų greita ir patikima, naudojome visiškai prijungtą kvorumo girdėjimo protokolą:

Kiekviename duomenų centre yra mažiausiai du koordinatoriaus mazgai. Periodiškai kiekvienas koordinatorius siunčia širdies plakimo pranešimą kitiems koordinatoriams ir informuoja juos apie jo veikimą, taip pat apie tai, kokius širdies plakimo pranešimus jis gavo paskutinius iš kurių grupės koordinatorių.

NewSQL = NoSQL + ACID

Gavęs panašią informaciją iš kitų kaip savo širdies plakimo pranešimų dalį, kiekvienas koordinatorius pats nusprendžia, kurie klasterio mazgai veikia, o kurie ne, vadovaudamiesi kvorumo principu: jei mazgas X gavo informaciją iš daugumos klasterio mazgų apie normalią. pranešimų gavimas iš mazgo Y, tada veikia , Y. Ir atvirkščiai, kai tik dauguma praneša apie trūkstamus pranešimus iš mazgo Y, tada Y atsisakė. Įdomu, kad jei kvorumas informuos mazgą X, kad jis nebegauna pranešimų iš jo, tada pats mazgas X laikys save žlugusiu.

Širdies plakimo pranešimai siunčiami dideliu dažniu, maždaug 20 kartų per sekundę, 50 ms periodu. „Java“ programoje sunku užtikrinti programos atsakymą per 50 ms, nes šiukšlių surinkėjas sukelia panašią pauzių trukmę. Šį reakcijos laiką galėjome pasiekti naudodami G1 šiukšlių rinktuvą, kuris leidžia nurodyti tikslą GC pauzių trukmei. Tačiau kartais, gana retai, kolektoriaus pauzės viršija 50 ms, todėl gali būti aptiktas klaidingas gedimas. Kad taip nenutiktų, koordinatorius nepraneša apie nuotolinio mazgo gedimą, kai iš jo dingsta pirmasis širdies plakimo pranešimas, tik tada, kai dingo keli iš eilės Taip pavyko aptikti koordinatoriaus mazgo gedimą 200 m ms.

Tačiau neužtenka greitai suprasti, kuris mazgas nustojo veikti. Turime ką nors padaryti šiuo klausimu.

Rezervacija

Klasikinė schema numato, kad meistro nesėkmės atveju pradedami nauji rinkimai naudojant vieną iš madinga Universalus algoritmai. Tačiau tokie algoritmai turi gerai žinomų problemų, susijusių su laiko konvergencija ir paties rinkimų proceso trukme. Mums pavyko išvengti tokių papildomų vėlavimų naudodami koordinatoriaus pakeitimo schemą visiškai prijungtame tinkle:

NewSQL = NoSQL + ACID

Tarkime, kad norime įvykdyti operaciją grupėje 50. Iš anksto nustatykime pakeitimo schemą, tai yra, kurie mazgai vykdys operacijas 50 grupėje sugedus pagrindiniam koordinatoriui. Mūsų tikslas – palaikyti sistemos funkcionalumą duomenų centro gedimo atveju. Nustatykime, kad pirmasis rezervas bus mazgas iš kito duomenų centro, o antrasis – trečiojo. Ši schema pasirenkama vieną kartą ir nesikeičia tol, kol nepasikeičia klasterio topologija, tai yra, kol į ją neįeis nauji mazgai (kas nutinka labai retai). Naujo aktyvaus meistro parinkimo procedūra, jei senasis sugenda, visada bus tokia: pirmasis rezervas taps aktyviuoju, o nustojus funkcionuoti, antrasis rezervas taps aktyviuoju.

Ši schema yra patikimesnė nei universalus algoritmas, nes norint suaktyvinti naują meistrą, pakanka nustatyti senojo gedimą.

Tačiau kaip klientai supras, kuris meistras dabar dirba? Neįmanoma išsiųsti informacijos tūkstančiams klientų per 50 ms. Galima situacija, kai klientas siunčia užklausą atidaryti operaciją, dar nežinodamas, kad šis master nebeveikia, ir užklausos laikas baigsis. Kad taip nenutiktų, klientai spekuliatyviai siunčia prašymą atidaryti sandorį grupės šeimininkui ir abiem jo rezervams vienu metu, tačiau į šį prašymą atsakys tik tas, kuris šiuo metu yra aktyvus šeimininkas. Klientas visą tolesnį bendravimą per operaciją vykdys tik su aktyviu šeimininku.

Atsarginių kopijų meistrai gautas užklausas dėl operacijų, kurios nepriklauso jiems, deda į negimusių operacijų eilę, kur jos kurį laiką saugomos. Jei aktyvus pagrindinis valdiklis miršta, naujasis pagrindinis apdoroja užklausas atidaryti operacijas iš jo eilės ir atsako klientui. Jei klientas jau atidarė operaciją su senuoju meistru, tada antrasis atsakymas yra ignoruojamas (ir, aišku, tokia operacija nebus baigta ir klientas ją pakartos).

Kaip vyksta sandoris

Tarkime, klientas nusiuntė koordinatoriui prašymą atidaryti sandorį tokiam ir tokiam subjektui su tokiu ir tokiu pirminiu raktu. Koordinatorius užrakina šį objektą ir įdeda jį į užrakinimo lentelę atmintyje. Jei reikia, koordinatorius nuskaito šį objektą iš saugyklos ir išsaugo gautus duomenis operacijos būsenoje koordinatoriaus atmintyje.

NewSQL = NoSQL + ACID

Kai klientas nori pakeisti operacijos duomenis, jis siunčia užklausą koordinatoriui modifikuoti objektą, o koordinatorius įdeda naujus duomenis į operacijos būsenos lentelę atmintyje. Taip įrašymas baigiamas – į saugyklą įrašymas nėra daromas.

NewSQL = NoSQL + ACID

Kai klientas prašo pakeisti savo duomenis kaip aktyvios operacijos dalį, koordinatorius elgiasi taip:

  • jei ID jau yra operacijoje, tada duomenys paimami iš atminties;
  • jei ID nėra atmintyje, tada trūkstami duomenys nuskaitomi iš saugojimo mazgų, sujungiami su jau esančiais atmintyje, o rezultatas perduodamas klientui.

Taigi, klientas gali skaityti savo pakeitimus, bet kiti klientai nemato šių pakeitimų, nes jie yra saugomi tik koordinatoriaus atmintyje, jų dar nėra Cassandra mazguose.

NewSQL = NoSQL + ACID

Kai klientas siunčia įsipareigojimą, būseną, kuri buvo paslaugos atmintyje, koordinatorius išsaugo užregistruotoje partijoje ir kaip užregistruotą paketą siunčia į Cassandra saugyklą. Parduotuvės daro viską, ko reikia, kad šis paketas būtų atomiškai (visiškai) pritaikytas, ir grąžina atsakymą koordinatoriui, kuris atleidžia užraktus ir patvirtina klientui sandorio sėkmę.

NewSQL = NoSQL + ACID

O norint atšaukti, koordinatoriui tereikia atlaisvinti operacijos būsenos užimtą atmintį.

Dėl minėtų patobulinimų įdiegėme ACID principus:

  • Atomiškumas. Tai yra garantija, kad sistemoje nebus iš dalies įrašyta jokia operacija arba nebus užbaigta nė viena. Šio principo laikomės registruodami siuntą Cassandroje.
  • Nuoseklumas. Kiekviena sėkminga operacija pagal apibrėžimą įrašo tik tinkamus rezultatus. Jei atidarius operaciją ir atlikus kai kurias operacijas nustatoma, kad rezultatas neteisingas, atliekamas atšaukimas.
  • Isolation. Kai sandoris vykdomas, tuo pačiu metu vykstantys sandoriai neturėtų turėti įtakos jos rezultatui. Konkuruojantys sandoriai yra izoliuojami naudojant pesimistinius koordinatoriaus užraktus. Skaitant ne operacijos metu, izoliacijos principas laikomasi perskaityto įsipareigojimo lygiu.
  • Pastovumas. Neatsižvelgiant į problemas žemesniuose lygiuose (sistemos užtemimas, aparatinės įrangos gedimas), sėkmingai užbaigtos operacijos pakeitimai turėtų likti išsaugoti, kai operacija atnaujinama.

Skaitymas pagal indeksus

Paimkime paprastą lentelę:

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

Jame yra ID (pagrindinis raktas), savininkas ir modifikavimo data. Turite pateikti labai paprastą užklausą - pasirinkite savininko duomenis su pakeitimo data „paskutinę dieną“.

SELECT *
WHERE owner=?
AND modified>?

Kad tokia užklausa būtų greitai apdorojama, klasikinėje SQL DBVS reikia sudaryti indeksą pagal stulpelius (savininkas, modifikuotas). Tai galime padaryti gana nesunkiai, nes dabar turime RŪGŠTIS garantijas!

Indeksai C*One

Yra šaltinio lentelė su nuotraukomis, kuriose įrašo ID yra pagrindinis raktas.

NewSQL = NoSQL + ACID

Indeksui C*One sukuria naują lentelę, kuri yra originalo kopija. Raktas yra toks pat kaip indekso išraiška, taip pat jis apima pirminį įrašo raktą iš šaltinio lentelės:

NewSQL = NoSQL + ACID

Dabar užklausą „paskutinės dienos savininkas“ galima perrašyti kaip pasirinkimą iš kitos lentelės:

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

Duomenų nuoseklumą šaltinio lentelės nuotraukose ir rodyklės lentelę i1 automatiškai palaiko koordinatorius. Remdamasis vien duomenų schema, gavęs pakeitimą, koordinatorius sugeneruoja ir išsaugo pakeitimą ne tik pagrindinėje lentelėje, bet ir kopijose. Indekso lentelėje neatliekami jokie papildomi veiksmai, žurnalai neskaitomi, nenaudojami užraktai. Tai reiškia, kad indeksų pridėjimas beveik nenaudoja išteklių ir praktiškai neturi įtakos modifikacijų taikymo greičiui.

Naudodami ACID, galėjome įdiegti į SQL panašius indeksus. Jie yra nuoseklūs, keičiamo dydžio, greiti, komponuojami ir integruoti į CQL užklausų kalbą. Norint palaikyti indeksus, nereikia keisti programos kodo. Viskas taip paprasta, kaip SQL. Ir svarbiausia, kad indeksai neturi įtakos pradinės operacijų lentelės modifikacijų vykdymo greičiui.

Kas nutiko

Prieš trejus metus sukūrėme C*One ir pradėjome jį naudoti komerciniais tikslais.

Ką galų gale gavome? Įvertinkime tai pasitelkę nuotraukų apdorojimo ir saugojimo posistemio pavyzdį – vieną iš svarbiausių socialinio tinklo duomenų rūšių. Kalbame ne apie pačių fotografijų kūnus, o apie visokią metainformaciją. Dabar Odnoklassniki turi apie 20 milijardų tokių įrašų, sistema apdoroja 80 tūkstančių skaitymo užklausų per sekundę, iki 8 tūkstančių ACID operacijų per sekundę, susijusių su duomenų modifikavimu.

Kai naudojome SQL su replikacijos koeficientu = 1 (bet RAID 10), nuotraukų metainformacija buvo saugoma labai prieinamame 32 įrenginių, kuriuose veikia Microsoft SQL Server, klasteryje (plius 11 atsarginių kopijų). Taip pat buvo skirta 10 serverių atsarginėms kopijoms saugoti. Iš viso 50 brangių automobilių. Tuo pačiu metu sistema veikė vardine apkrova, be rezervo.

Perėję į naują sistemą, gavome replikacijos koeficientą = 3 – kopiją kiekviename duomenų centre. Sistema susideda iš 63 Cassandra saugojimo mazgų ir 6 koordinatorių mašinų, iš viso 69 serveriai. Tačiau šios mašinos yra daug pigesnės, jų bendra kaina sudaro apie 30% SQL sistemos kainos. Tuo pačiu metu apkrova išlaikoma 30%.

Įdiegus C*One, vėlavimas taip pat sumažėjo: SQL rašymo operacija užtruko apie 4,5 ms. C*One – apie 1,6 ms. Operacijos trukmė vidutiniškai yra mažesnė nei 40 ms, įsipareigojimas baigiamas per 2 ms, skaitymo ir rašymo trukmė yra vidutiniškai 2 ms. 99 procentilis – tik 3-3,1 ms, pertraukų skaičius sumažėjo 100 kartų – visa tai dėl plačiai paplitusių spekuliacijų.

Iki šiol daugumos SQL serverio mazgų eksploatavimas buvo nutrauktas, tik naudojant C*One. Pritaikėme C*One dirbti mūsų debesyje vienas debesis, kuris leido paspartinti naujų klasterių diegimą, supaprastinti konfigūraciją ir automatizuoti veikimą. Be šaltinio kodo tai padaryti būtų daug sunkiau ir sudėtingiau.

Dabar mes stengiamės perkelti kitas saugyklas į debesį, tačiau tai yra visiškai kita istorija.

Šaltinis: www.habr.com

Добавить комментарий