NewSQL = NoSQL+ACID

NewSQL = NoSQL+ACID
Kuni viimase ajani salvestas Odnoklassniki SQL Serveris umbes 50 TB reaalajas töödeldud andmeid. Sellise mahu jaoks on SQL DBMS-i abil peaaegu võimatu pakkuda kiiret ja usaldusväärset ning isegi andmekeskuse rikketaluvat juurdepääsu. Tavaliselt kasutatakse sellistel juhtudel ühte NoSQL-i salvestusruumi, kuid kõike ei saa NoSQL-i üle kanda: mõned olemid nõuavad ACID-i tehingute garantiisid.

See viis meid NewSQL-i salvestusruumi ehk DBMS-i kasutamiseni, mis tagab NoSQL-süsteemide tõrketaluvuse, mastaapsuse ja jõudluse, kuid säilitab samal ajal klassikalistele süsteemidele tuttavad ACID-garantiid. Selle uue klassi töökorras tööstussüsteeme on vähe, seega juurutasime sellise süsteemi ise ja panime kommertskasutusele.

Kuidas see töötab ja mis juhtus – loe lõike alt.

Täna on Odnoklassniki igakuine vaatajaskond üle 70 miljoni unikaalse külastaja. Meie Oleme esiviisikus maailma suurimad sotsiaalvõrgustikud ja nende kahekümne saidi hulgas, millel kasutajad kõige rohkem aega veedavad. OK infrastruktuur käsitleb väga suuri koormusi: rohkem kui miljon HTTP-päringut sekundis esikülje kohta. Enam kui 8000 tükist koosneva serveripargi osad asuvad lähestikku – neljas Moskva andmekeskuses, mis võimaldab võrgu latentsusaega nende vahel alla 1 ms.

Oleme Cassandrat kasutanud alates 2010. aastast alates versioonist 0.6. Tänapäeval töötab mitukümmend klastrit. Kiireim klaster töötleb rohkem kui 4 miljonit toimingut sekundis ja suurimad salvestavad 260 TB.

Kuid need on kõik tavalised NoSQL-i klastrid, mida kasutatakse salvestamiseks nõrgalt koordineeritud andmeid. Tahtsime välja vahetada peamise ühtse salvestusruumi Microsoft SQL Serveri, mida on kasutatud alates Odnoklassniki asutamisest. Salvestus koosnes enam kui 300 SQL Server Standard Edition masinast, mis sisaldasid 50 TB andmeid – äriüksused. Neid andmeid muudetakse ACID-tehingute osana ja need nõuavad kõrge konsistents.

Andmete jaotamiseks SQL Serveri sõlmede vahel kasutasime nii vertikaalset kui ka horisontaalset eraldamine (killustamine). Ajalooliselt kasutasime lihtsat andmete jagamise skeemi: iga olem oli seotud märgiga – olemi ID funktsiooniga. Sama märgiga olemid paigutati samasse SQL-serverisse. Põhi-detailide suhe rakendati nii, et põhi- ja alamkirjete märgid langesid alati kokku ja asusid samas serveris. Sotsiaalvõrgustikus genereeritakse peaaegu kõik kirjed kasutaja nimel – see tähendab, et kõik ühe funktsionaalse alamsüsteemi kasutajaandmed salvestatakse ühte serverisse. See tähendab, et äritehing hõlmas peaaegu alati tabeleid ühest SQL-serverist, mis võimaldas tagada andmete järjepidevuse kohalike ACID-tehingute abil, ilma et oleks vaja aeglane ja ebausaldusväärne hajutatud ACID tehingud.

Tänu jagamisele ja SQL-i kiirendamisele:

  • Me ei kasuta võõrvõtme piiranguid, kuna jagamisel võib olemi ID asuda mõnes teises serveris.
  • Me ei kasuta salvestatud protseduure ja käivitajaid DBMS-i protsessori lisakoormuse tõttu.
  • Me ei kasuta JOIN-e kõige eelneva ja paljude juhuslike kettalt lugemiste tõttu.
  • Väljaspool tehingut kasutame ummikseisude vähendamiseks isolatsioonitaset Read Uncommitted.
  • Teostame ainult lühikesi tehinguid (keskmiselt lühemad kui 100 ms).
  • Mitmerealisi UPDATE ja DELETE me ei kasuta suure ummikute arvu tõttu – uuendame korraga vaid ühte kirjet.
  • Teeme päringuid alati ainult indeksitele – täieliku tabelikontrolli plaaniga päring tähendab meie jaoks andmebaasi ülekoormamist ja selle ebaõnnestumist.

Need sammud võimaldasid meil SQL-serveritelt peaaegu maksimaalse jõudluse välja pigistada. Probleemid muutusid aga üha arvukamaks. Vaatame neid.

Probleemid SQL-iga

  • Kuna kasutasime isekirjutatud shardingut, siis uute kildude lisamise tegid administraatorid käsitsi. Kogu selle aja skaleeritavad andmekoopiad ei teenindanud taotlusi.
  • Tabeli kirjete arvu kasvades väheneb sisestamise ja muutmise kiirus, olemasolevasse tabelisse indeksite lisamisel kiirus langeb teguri võrra, indeksite loomine ja uuesti loomine toimub koos seisakutega.
  • Väikese hulga Windows for SQL Server tootmine muudab infrastruktuuri haldamise keeruliseks

Kuid peamine probleem on

veataluvus

Klassikaline SQL-server on halva veataluvusega. Oletame, et teil on ainult üks andmebaasiserver ja see ebaõnnestub kord kolme aasta jooksul. Selle aja jooksul on sait 20 minutit maas, mis on vastuvõetav. Kui teil on 64 serverit, on sait iga kolme nädala järel maas. Ja kui teil on 200 serverit, siis sait ei tööta iga nädal. See on probleem.

Mida saab teha SQL-serveri veataluvuse parandamiseks? Wikipedia kutsub meid üles ehitama väga kättesaadav klaster: kus mõne komponendi rikke korral on olemas varukoopia.

Selleks on vaja kallite seadmete parki: arvukad dubleerimised, optiline kiud, jagatud salvestusruum ja reservi kaasamine ei tööta usaldusväärselt: umbes 10% lülitustest lõpeb varusõlme rikkega nagu rong põhisõlme taga.

Kuid sellise kõrge kättesaadavusega klastri peamiseks puuduseks on nullkäideldavus, kui andmekeskus, kus see asub, ebaõnnestub. Odnoklassnikis on neli andmekeskust ja me peame tagama toimimise juhul, kui ühes neist tekib täielik rike.

Selleks võiksime kasutada Multi-Master SQL Serverisse sisseehitatud replikatsioon. See lahendus on tarkvara kulukuse tõttu palju kallim ja kannatab tuntud replikatsiooniprobleemide all – ettearvamatud tehingute viivitused sünkroonse replikatsiooni korral ja viivitused replikatsioonide rakendamisel (ja sellest tulenevalt ka kaotatud modifikatsioonidel) asünkroonse replikatsiooniga. Eeldatav käsitsi konfliktide lahendamine muudab selle valiku meie jaoks täiesti kohaldamatuks.

Kõik need probleemid nõudsid radikaalset lahendust ja hakkasime neid üksikasjalikult analüüsima. Siin tuleb tutvuda sellega, millega SQL Server põhiliselt tegeleb – tehingutega.

Lihtne tehing

Vaatleme rakendusliku SQL-i programmeerija seisukohast kõige lihtsamat tehingut: foto lisamist albumisse. Albumeid ja fotosid hoitakse erinevatel plaatidel. Albumil on avalik fotoloendur. Seejärel jagatakse selline tehing järgmisteks etappideks:

  1. Lukustame albumi võtmega.
  2. Looge fototabelisse kirje.
  3. Kui fotol on avalik staatus, lisage albumisse avalik fotoloendur, värskendage kirjet ja tehke tehing.

Või pseudokoodis:

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

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

TX.commit();

Näeme, et kõige tavalisem äritehingu stsenaarium on lugeda andmebaasist andmed rakendusserveri mällu, muuta midagi ja salvestada uued väärtused tagasi andmebaasi. Tavaliselt uuendame sellise tehingu puhul mitut olemit, mitut tabelit.

Tehingu sooritamisel võib toimuda samaaegne teise süsteemi samade andmete muutmine. Näiteks võib Antispam otsustada, et kasutaja on kuidagi kahtlane ja seetõttu ei tohiks kõik kasutaja fotod enam avalikud olla, need tuleb saata modereerimisele, mis tähendab photo.status muutmist mõnele muule väärtusele ja vastavate loendurite väljalülitamist. Ilmselgelt, kui see toiming toimub ilma rakenduse aatomilisuse ja konkureerivate modifikatsioonide isoleerimiseta, nagu ACID, siis pole tulemus see, mida vaja – kas näitab fotoloendur vale väärtust või ei saadeta kõiki fotosid modereerimiseks.

Kogu Odnoklassniki eksisteerimise jooksul on kirjutatud palju sarnast koodi, mis manipuleerib ühe tehingu raames erinevaid äriüksusi. Põhineb NoSQL-ile migreerumise kogemusel Lõplik järjepidevus Teame, et suurim väljakutse (ja ajainvesteering) tuleneb koodi väljatöötamisest andmete järjepidevuse säilitamiseks. Seetõttu pidasime uue salvestusruumi peamiseks nõudeks rakendusloogika jaoks reaalsete ACID-tehingute võimaldamist.

Muud, mitte vähem olulised nõuded olid:

  • Kui andmekeskus ebaõnnestub, peab olema saadaval nii uude salvestusruumi lugemine kui ka kirjutamine.
  • Praeguse arengukiiruse säilitamine. See tähendab, et uue hoidlaga töötades peaks koodi kogus olema ligikaudu sama, hoidlasse ei tohiks midagi lisada, konfliktide lahendamise algoritme välja töötada, sekundaarsete indeksite säilitamiseks jne.
  • Uue salvestusruumi kiirus pidi olema üsna suur nii andmete lugemisel kui ka tehingute töötlemisel, mis tähendas sisuliselt seda, et akadeemiliselt ranged, universaalsed, kuid aeglased lahendused, nagu nt, ei olnud rakendatavad. kahefaasilised kohustused.
  • Automaatne skaleerimine lennu ajal.
  • Kasutades tavalisi odavaid servereid, ilma et oleks vaja osta eksootilist riistvara.
  • Ladustuse arendamise võimalus ettevõtte arendajate poolt. Teisisõnu eelistati patenteeritud või avatud lähtekoodiga lahendusi, eelistatavalt Javas.

Otsused, otsused

Võimalikke lahendusi analüüsides jõudsime kahe võimaliku arhitektuurivalikuni:

Esimene on võtta mis tahes SQL-server ja rakendada nõutav tõrketaluvus, skaleerimismehhanism, tõrkesiirdeklaster, konfliktide lahendamine ning hajutatud, usaldusväärsed ja kiired ACID-tehingud. Hindasime seda võimalust väga mittetriviaalseks ja töömahukaks.

Teine võimalus on võtta valmis NoSQL-i salvestusruum koos juurutatud skaleerimise, tõrkesiirdeklastri, konfliktide lahendamise ning ise tehingute ja SQL-i juurutamine. Esmapilgul tundub isegi SQL-i juurutamise ülesanne, rääkimata ACID tehingutest, aastatepikkuse ülesandena. Kuid siis mõistsime, et praktikas kasutatav SQL-i funktsioonide komplekt on ANSI SQL-ist sama kaugel Cassandra CQL kaugel ANSI SQL-ist. CQL-i veelgi lähemalt uurides mõistsime, et see on üsna lähedal sellele, mida vajame.

Cassandra ja CQL

Niisiis, mis on Cassandras huvitavat, millised võimalused sellel on?

Esiteks saate siin luua tabeleid, mis toetavad erinevaid andmetüüpe; primaarvõtmel saate teha SELECT või UPDATE.

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

Replica andmete järjepidevuse tagamiseks kasutab Cassandra kvoorumi lähenemine. Lihtsamal juhul tähendab see, et kui klastri erinevatesse sõlmedesse paigutatakse sama rea ​​kolm koopiat, loetakse kirjutamine õnnestunuks, kui enamus sõlmedest (st kaks kolmest) kinnitasid selle kirjutamistoimingu õnnestumist. . Reaandmeid peetakse järjepidevaks, kui lugemise käigus küsitleti ja kinnitati enamik sõlmedest. Seega on kolme koopiaga tagatud täielik ja vahetu andmete järjepidevus, kui üks sõlm ebaõnnestub. See lähenemisviis võimaldas meil rakendada veelgi usaldusväärsemat skeemi: saata päringud alati kõigile kolmele koopiale, oodates vastust kahelt kiireimalt. Kolmanda koopia hilinenud vastus jäetakse sel juhul kõrvale. Hilinenud reageerimisega sõlmel võivad olla tõsised probleemid – pidurid, prügikoristus JVM-is, otsemälu tagasinõudmine Linuxi tuumas, riistvaratõrge, võrguühenduse katkemine. See aga ei mõjuta kuidagi kliendi toiminguid ega andmeid.

Kutsutakse lähenemist, kui võtame ühendust kolme sõlmega ja saame vastuse kahelt spekulatsioon: lisakoopiate taotlus saadetakse juba enne, kui see "kukkub".

Veel üks Cassandra eelis on Batchlog, mehhanism, mis tagab, et teie tehtud muudatuste partii rakendatakse täielikult või ei rakendata neid üldse. See võimaldab meil lahendada A ACID-is – aatomilisus karbist väljas.

Cassandra tehingutele kõige lähemal on nn.kerged tehingud". Kuid need pole kaugeltki "päris" ACID tehingud: tegelikult on see võimalus seda teha CAS ainult ühe kirje andmetel, kasutades konsensust, kasutades raskekaalu Paxose protokolli. Seetõttu on selliste tehingute kiirus väike.

Millest meil Cassandras puudus oli

Niisiis pidime Cassandras rakendama tõelisi ACID-tehinguid. Seda kasutades saaksime hõlpsasti rakendada kahte muud klassikalise DBMS-i mugavat funktsiooni: järjepidevad kiired indeksid, mis võimaldaksid meil teha andmeid mitte ainult primaarvõtme järgi, ja tavaline monotoonsete automaatselt suurenevate ID-de generaator.

C*Üks

Nii sündis uus DBMS C*Üks, mis koosneb kolme tüüpi serverisõlmedest:

  • Salvestus – (peaaegu) standardsed Cassandra serverid, mis vastutavad andmete salvestamise eest kohalikele ketastele. Andmete koormuse ja mahu kasvades saab nende kogust kergesti skaleerida kümnetesse ja sadadesse.
  • Tehingute koordinaatorid – tagavad tehingute teostamise.
  • Kliendid on rakendusserverid, mis teostavad ärioperatsioone ja algatavad tehinguid. Selliseid kliente võib olla tuhandeid.

NewSQL = NoSQL+ACID

Igat tüüpi serverid on osa ühisest klastrist, kasutavad omavahel suhtlemiseks sisemist Cassandra sõnumiprotokolli ja lobamokk klastri teabe vahetamiseks. Heartbeati abil saavad serverid teada vastastikustest riketest, säilitada ühtset andmeskeemi – tabeleid, nende struktuuri ja replikatsiooni; partitsiooniskeem, klastri topoloogia jne.

Kliendid

NewSQL = NoSQL+ACID

Tavaliste draiverite asemel kasutatakse Fat Client režiimi. Selline sõlm ei salvesta andmeid, vaid võib toimida päringu täitmise koordinaatorina, see tähendab, et Klient ise tegutseb oma päringute koordinaatorina: pärib salvestuskoopiaid ja lahendab konflikte. See pole mitte ainult usaldusväärsem ja kiirem kui tavaline draiver, mis nõuab sidet kaugkoordinaatoriga, vaid võimaldab teil ka päringute edastamist juhtida. Väljaspool kliendil avatud tehingut saadetakse päringud hoidlatesse. Kui klient on tehingu avanud, saadetakse kõik tehingusisesed päringud tehingu koordinaatorile.
NewSQL = NoSQL+ACID

C*One tehingute koordinaator

Koordinaator on midagi, mille juurutasime C*One'i jaoks nullist. Ta vastutab tehingute, lukkude ja tehingute rakendamise järjekorra haldamise eest.

Iga teenindatava tehingu jaoks loob koordinaator ajatempli: iga järgnev tehing on suurem kui eelmine tehing. Kuna Cassandra konfliktide lahendamise süsteem põhineb ajatemplitel (kahe vastuolulise kirje puhul loetakse ajatempliga viimast), lahendatakse konflikt alati järgmise tehingu kasuks. Nii me rakendasime Lamport käekell - odav viis konfliktide lahendamiseks hajutatud süsteemis.

Lukud

Isolatsiooni tagamiseks otsustasime kasutada kõige lihtsamat meetodit – pessimistlikke lukke, mis põhinevad kirje primaarvõtmel. Teisisõnu, tehingus tuleb kirje esmalt lukustada, alles seejärel lugeda, muuta ja salvestada. Alles pärast edukat sidumist saab kirje avada, et konkureerivad tehingud saaksid seda kasutada.

Sellise lukustuse rakendamine on hajutamata keskkonnas lihtne. Hajutatud süsteemis on kaks peamist võimalust: kas rakendada klastris hajutatud lukustamist või levitada tehinguid nii, et sama kirjega seotud tehinguid teenindab alati sama koordinaator.

Kuna meie puhul on andmed SQL-is juba kohalike tehingute rühmade vahel jaotatud, otsustati koordinaatoritele määrata kohalikud tehingurühmad: üks koordinaator teostab kõik tehingud märkidega vahemikus 0 kuni 9, teine ​​- märkidega 10 kuni 19, ja nii edasi. Selle tulemusena saab igast koordinaatori eksemplarist tehingurühma peremees.

Seejärel saab lukke rakendada koordinaatori mällu banaalse HashMapi kujul.

Koordinaatori tõrked

Kuna üks koordinaator teenindab eranditult tehingute rühma, on väga oluline kiiresti kindlaks teha selle ebaõnnestumise fakt, et tehingu teine ​​​​katse aeguks. Selle kiireks ja usaldusväärseks muutmiseks kasutasime täielikult ühendatud kvoorumi kuulmisprotokolli:

Igas andmekeskuses on vähemalt kaks koordinaatorisõlme. Iga koordinaator saadab perioodiliselt teistele koordinaatoritele südamelöögiteate ja teavitab neid selle toimimisest ning samuti sellest, milliseid südamelöögiteateid ta viimati millistelt klastri koordinaatoritelt sai.

NewSQL = NoSQL+ACID

Saades teistelt oma südamelöögiteadete osana sarnast teavet, otsustab iga koordinaator ise, millised klastri sõlmed töötavad ja millised mitte, juhindudes kvoorumi põhimõttest: kui sõlm X on saanud enamikult klastri sõlmedelt teavet normaalse kohta. sõnumite vastuvõtmine sõlmest Y, siis , Y töötab. Ja vastupidi, niipea, kui enamus teatab sõlme Y kadunud sõnumitest, on Y keeldunud. On uudishimulik, et kui kvoorum teatab sõlmele X, et ta ei saa enam sellelt sõnumeid, siis loeb sõlm X ise end ebaõnnestunuks.

Südamelöögiteateid saadetakse kõrge sagedusega, umbes 20 korda sekundis, perioodiga 50 ms. Java puhul on raske tagada rakenduse reageerimist 50 ms jooksul, kuna prügikoguja põhjustatud pauside pikkus on võrreldav. Suutsime selle reageerimisaja saavutada, kasutades G1 prügikogujat, mis võimaldab meil määrata GC pauside kestuse eesmärgi. Kuid mõnikord, üsna harva, ületavad kollektori pausid 50 ms, mis võib viia vale veatuvastuseni. Et seda ei juhtuks, ei teata koordinaator kaugsõlme rikkest, kui sealt kaob esimene südamelöögiteade, vaid siis, kui mitu on järjest kadunud.Nii õnnestus meil 200. aastal tuvastada koordinaatorisõlme rike. Prl.

Kuid sellest ei piisa, et kiiresti mõista, milline sõlm on lakanud töötamast. Peame selles osas midagi ette võtma.

Reserveerimine

Klassikaline skeem hõlmab meistri ebaõnnestumise korral uute valimiste alustamist, kasutades ühte järgmistest moodne universaalne algoritmid. Sellistel algoritmidel on aga üldtuntud probleemid aja konvergentsi ja valimisprotsessi enda pikkusega. Suutsime selliseid täiendavaid viivitusi vältida, kasutades koordinaatori asendusskeemi täielikult ühendatud võrgus:

NewSQL = NoSQL+ACID

Oletame, et tahame sooritada tehingut rühmas 50. Määrame eelnevalt asendusskeemi, st millised sõlmed teostavad grupi 50 tehinguid peakoordinaatori rikke korral. Meie eesmärk on säilitada süsteemi funktsionaalsus andmekeskuse rikke korral. Teeme kindlaks, et esimene reserv on sõlm teisest andmekeskusest ja teine ​​reserv on kolmanda sõlm. See skeem valitakse üks kord ja see ei muutu enne, kui klastri topoloogia muutub, st kuni sellesse sisenevad uued sõlmed (mida juhtub väga harva). Uue aktiivse ülempea valimise kord, kui vana ebaõnnestub, on alati järgmine: esimene reserv saab aktiivseks ülemaks ja kui see on lakanud töötamast, saab teine ​​​​reservist aktiivne ülem.

See skeem on universaalsest algoritmist usaldusväärsem, kuna uue masteri aktiveerimiseks piisab vana rikke kindlaksmääramisest.

Aga kuidas saavad kliendid aru, milline meister praegu töötab? 50 ms jooksul on võimatu tuhandetele klientidele teavet saata. Võimalik on olukord, kus klient saadab tehingu avamise taotluse, teadmata veel, et see master enam ei tööta, ja päring aegub. Et seda ei juhtuks, saadavad kliendid spekulatiivselt tehingu avamise taotluse grupiülemale ja tema mõlemale reservile korraga, kuid sellele päringule vastab vaid see, kes on hetkel aktiivne ülem. Klient suhtleb kogu tehingusiseselt ainult aktiivse masteriga.

Varundusmeistrid asetavad vastuvõetud päringud tehingute kohta, mis ei kuulu neile, sündimata tehingute järjekorda, kus neid mõnda aega hoitakse. Kui aktiivne juht sureb, töötleb uus ülem oma järjekorrast tehingute avamise taotlusi ja vastab kliendile. Kui klient on vanameistriga tehingu juba avanud, siis teist vastust eiratakse (ja ilmselgelt sellist tehingut ei teostata ja klient seda kordab).

Kuidas tehing toimib

Oletame, et klient saatis koordinaatorile taotluse sellise ja sellise olemi jaoks sellise ja sellise primaarvõtmega tehingu avamiseks. Koordinaator lukustab selle olemi ja asetab selle mällu lukustustabelisse. Vajadusel loeb koordinaator selle olemi mälust ja salvestab saadud andmed tehinguolekus koordinaatori mällu.

NewSQL = NoSQL+ACID

Kui klient soovib tehingus andmeid muuta, saadab ta koordinaatorile taotluse olemi muutmiseks ja koordinaator paigutab uued andmed tehingu oleku tabelisse mällu. See lõpetab salvestamise – salvestusruumi ei tehta.

NewSQL = NoSQL+ACID

Kui klient taotleb aktiivse tehingu osana enda muudetud andmeid, toimib koordinaator järgmiselt:

  • kui ID on juba tehingus, siis võetakse andmed mälust;
  • kui ID-d mälus pole, siis loetakse puuduvad andmed salvestussõlmedest, kombineeritakse need juba mälus olevatega ja tulemus antakse kliendile.

Seega saab klient lugeda enda muudatusi, kuid teised kliendid neid muudatusi ei näe, kuna need on salvestatud ainult koordinaatori mällu, need pole veel Cassandra sõlmedes.

NewSQL = NoSQL+ACID

Kui klient saadab kohustuse, salvestab koordinaator teenuse mällu olnud oleku logitud partiina ja saadetakse logitud partiina Cassandra salvestusruumi. Kauplused teevad kõik vajaliku, et tagada selle paketi täielik (täielik) rakendamine ning tagastab vastuse koordinaatorile, kes vabastab lukud ja kinnitab kliendile tehingu õnnestumist.

NewSQL = NoSQL+ACID

Ja tagasipööramiseks peab koordinaator vabastama ainult tehinguoleku poolt hõivatud mälu.

Ülaltoodud täiustuste tulemusena rakendasime ACID põhimõtteid:

  • Aatomilisus. See on garantii, et ühtki tehingut süsteemis osaliselt ei salvestata, kas kõik selle alamtoimingud sooritatakse või ei jõua ükski. Peame sellest põhimõttest kinni Cassandra logitud partiide kaudu.
  • Järjepidevus. Iga edukas tehing salvestab definitsiooni järgi ainult kehtivaid tulemusi. Kui pärast tehingu avamist ja osa toimingute tegemist avastatakse, et tulemus on kehtetu, tehakse tagasipööramine.
  • Isolatsioon. Kui tehing sooritatakse, ei tohiks samaaegsed tehingud mõjutada selle tulemust. Konkureerivad tehingud eraldatakse koordinaatori pessimistlike lukkude abil. Tehinguväliste lugemiste puhul järgitakse isolatsioonipõhimõtet Read Committed tasemel.
  • Stabiilsus. Olenemata madalamatel tasanditel esinevatest probleemidest – süsteemikatkestus, riistvaratõrge – peaksid edukalt sooritatud tehinguga tehtud muudatused toimingute jätkamisel säilima.

Lugemine indeksite järgi

Võtame lihtsa tabeli:

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

Sellel on ID (esmane võti), omanik ja muutmise kuupäev. Peate esitama väga lihtsa taotluse - valige omaniku andmed muudatuse kuupäevaga "viimase päeva kohta".

SELECT *
WHERE owner=?
AND modified>?

Sellise päringu kiireks töötlemiseks peate klassikalises SQL DBMS-is looma indeksi veergude kaupa (omanik, muudetud). Saame seda teha üsna lihtsalt, kuna meil on nüüd HAPE garantii!

Indeksid C*One'is

Seal on fotodega lähtetabel, milles kirje ID on primaarvõti.

NewSQL = NoSQL+ACID

Indeksi jaoks loob C*One uue tabeli, mis on originaali koopia. Võti on sama mis indeksi avaldis ja see sisaldab ka lähtetabelis oleva kirje primaarvõtit:

NewSQL = NoSQL+ACID

Nüüd saab päringu "viimase päeva omanik" ümber kirjutada valikuna teisest tabelist:

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

Lähtetabeli fotode ja indekstabeli i1 andmete järjepidevust hoiab koordinaator automaatselt. Ainuüksi andmeskeemi põhjal genereerib ja salvestab koordinaator muudatuse vastuvõtmisel muudatuse mitte ainult põhitabelisse, vaid ka koopiatesse. Indeksitabelis ei tehta lisatoiminguid, logisid ei loeta ja lukke ei kasutata. See tähendab, et indeksite lisamine ei tarbi peaaegu mingeid ressursse ega mõjuta praktiliselt muudatuste rakendamise kiirust.

ACID-i abil saime rakendada SQL-i sarnaseid indekseid. Need on järjepidevad, skaleeritavad, kiired, komponeeritavad ja CQL-i päringukeelde sisse ehitatud. Indekside toetamiseks pole rakenduse koodi muutmine vajalik. Kõik on sama lihtne kui SQL-is. Ja mis kõige tähtsam, indeksid ei mõjuta algse tehingutabeli muudatuste täitmise kiirust.

Mis juhtus

Töötasime välja C*One’i kolm aastat tagasi ja käivitasime selle kommertskasutuseks.

Mida me lõpuks saime? Hindame seda fototöötluse ja -salvestuse alamsüsteemi näitel, mis on sotsiaalvõrgustiku üks olulisemaid andmetüüpe. Me ei räägi fotode kehadest endist, vaid kõikvõimalikust metainfost. Nüüd on Odnoklassnikis umbes 20 miljardit sellist kirjet, süsteem töötleb 80 tuhat lugemistaotlust sekundis, andmete muutmisega seotud kuni 8 tuhat ACID-tehingut sekundis.

Kui kasutasime SQL-i replikatsiooniteguriga = 1 (kuid RAID 10-s), salvestati fotode metateave väga kättesaadavasse klastrisse, mis koosnes 32 masinast, milles töötab Microsoft SQL Server (pluss 11 varukoopiat). Varukoopiate hoidmiseks eraldati ka 10 serverit. Kokku 50 kallist autot. Samal ajal töötas süsteem nimikoormusel, ilma reservita.

Pärast uuele süsteemile üleminekut saime replikatsiooniteguri = 3 – igas andmekeskuses koopia. Süsteem koosneb 63 Cassandra salvestussõlmest ja 6 koordinaatormasinast, kokku 69 serverist. Kuid need masinad on palju odavamad, nende kogumaksumus on umbes 30% SQL-süsteemi maksumusest. Samal ajal hoitakse koormust 30%.

C*One’i kasutuselevõtuga vähenes ka latentsusaeg: SQL-is võttis kirjutamisoperatsioon aega umbes 4,5 ms. C*One'is – umbes 1,6 ms. Tehingu kestus on keskmiselt alla 40 ms, commit sooritatakse 2 ms, lugemise ja kirjutamise kestus on keskmiselt 2 ms. 99. protsentiil - ainult 3-3,1 ms, ajalõppude arv on vähenenud 100 korda - kõik on tingitud spekulatsioonide laialdasest kasutamisest.

Praeguseks on enamus SQL Serveri sõlmedest kasutusest kõrvaldatud, uusi tooteid arendatakse ainult C*One’i abil. Kohandasime C*One'i oma pilves töötama ühe pilve, mis võimaldas kiirendada uute klastrite juurutamist, lihtsustada konfigureerimist ja automatiseerida tööd. Ilma lähtekoodita oleks selle tegemine palju keerulisem ja tülikam.

Nüüd töötame selle kallal, et viia oma muud salvestusruumid pilve – aga see on hoopis teine ​​lugu.

Allikas: www.habr.com

Lisa kommentaar