NewSQL = NoSQL+ACID

NewSQL = NoSQL+ACID
Do nedavnega je Odnoklassniki shranil približno 50 TB podatkov, obdelanih v realnem času v SQL Server. Za takšno količino je skoraj nemogoče zagotoviti hiter in zanesljiv ter celo dostop do podatkovnega centra, toleranten na napake, z uporabo SQL DBMS. Običajno se v takšnih primerih uporabi ena od shramb NoSQL, vendar ni mogoče vsega prenesti v NoSQL: nekatere entitete zahtevajo garancije transakcij ACID.

To nas je pripeljalo do uporabe shrambe NewSQL, to je DBMS, ki zagotavlja toleranco na napake, razširljivost in zmogljivost sistemov NoSQL, hkrati pa ohranja ACID garancije, ki jih poznajo klasični sistemi. Delujočih industrijskih sistemov tega novega razreda je malo, zato smo tak sistem implementirali sami in ga dali v komercialno uporabo.

Kako deluje in kaj se je zgodilo - preberite pod rezom.

Danes je mesečno občinstvo Odnoklassnikov več kot 70 milijonov edinstvenih obiskovalcev. mi Smo med prvih pet največje družbeno omrežje na svetu in med dvajsetimi spletnimi mesti, na katerih uporabniki preživijo največ časa. Infrastruktura OK obvladuje zelo visoke obremenitve: več kot milijon zahtev HTTP/s na fronto. Deli strežniške flote, ki šteje več kot 8000 kosov, se nahajajo blizu drug drugega – v štirih moskovskih podatkovnih centrih, kar omogoča omrežno zakasnitev med njimi manj kot 1 ms.

Cassandro uporabljamo od leta 2010, začenši z različico 0.6. Danes deluje več deset grozdov. Najhitrejša gruča obdela več kot 4 milijone operacij na sekundo, največja pa shrani 260 TB.

Vendar so to vse običajne gruče NoSQL, ki se uporabljajo za shranjevanje slabo usklajeni podatke. Želeli smo zamenjati glavno konsistentno shrambo, Microsoft SQL Server, ki se uporablja od ustanovitve Odnoklassnikov. Shrambo je sestavljalo več kot 300 strojev SQL Server Standard Edition, ki so vsebovali 50 TB podatkov – poslovnih subjektov. Ti podatki so spremenjeni kot del transakcij ACID in zahtevajo visoka konsistenca.

Za distribucijo podatkov po vozliščih SQL Server smo uporabili navpično in vodoravno pregrajevanje (drobljenje). V preteklosti smo uporabljali preprosto shemo razčlenjevanja podatkov: vsaka entiteta je bila povezana z žetonom – funkcijo ID-ja entitete. Entitete z istim žetonom so bile postavljene na isti strežnik SQL. Razmerje glavni-podrobnosti je bilo implementirano tako, da so se žetoni glavnega in podrejenega zapisa vedno ujemali in se nahajali na istem strežniku. V socialnem omrežju se skoraj vsi zapisi generirajo v imenu uporabnika – kar pomeni, da so vsi uporabniški podatki znotraj enega funkcionalnega podsistema shranjeni na enem strežniku. To pomeni, da je poslovna transakcija skoraj vedno vključevala tabele iz enega strežnika SQL, kar je omogočilo zagotavljanje konsistentnosti podatkov z uporabo lokalnih transakcij ACID, ne da bi bilo treba uporabljati počasen in nezanesljiv porazdeljene transakcije ACID.

Zahvaljujoč razdeljevanju in pospešitvi SQL:

  • Ne uporabljamo omejitev tujih ključev, saj se lahko pri razčlenjevanju ID entitete nahaja na drugem strežniku.
  • Ne uporabljamo shranjenih procedur in sprožilcev zaradi dodatne obremenitve CPU DBMS.
  • Zaradi vsega zgoraj navedenega in veliko naključnih branj z diska ne uporabljamo JOIN-ov.
  • Zunaj transakcije uporabljamo izolacijsko raven Read Uncommitted za zmanjšanje zastojev.
  • Izvajamo le kratke transakcije (povprečno krajše od 100 ms).
  • Večvrstičnih UPDATE in DELETE ne uporabljamo zaradi velikega števila zastojev - posodabljamo samo en zapis naenkrat.
  • Vedno izvajamo poizvedbe samo po indeksih – poizvedba s celotnim načrtom skeniranja tabele za nas pomeni preobremenitev baze podatkov in povzroči njen neuspeh.

Ti koraki so nam omogočili, da iz strežnikov SQL iztisnemo skoraj največjo zmogljivost. Težav pa je bilo vse več. Poglejmo jih.

Težave z SQL

  • Ker smo uporabili samonapisano razdeljevanje, so skrbniki ročno dodajali nove drobce. Ves ta čas razširljive replike podatkov niso servisirale zahtev.
  • Z naraščanjem števila zapisov v tabeli se hitrost vstavljanja in spreminjanja zmanjšuje; pri dodajanju indeksov v obstoječo tabelo se hitrost zmanjša za faktor; ustvarjanje in ponovno ustvarjanje indeksov poteka z izpadi.
  • Majhna količina sistema Windows za SQL Server v proizvodnji otežuje upravljanje infrastrukture

Toda glavna težava je

toleranca napak

Klasični SQL strežnik ima slabo toleranco napak. Recimo, da imate samo en strežnik baze podatkov in ta odpove enkrat na tri leta. V tem času stran ne deluje 20 minut, kar je sprejemljivo. Če imate 64 strežnikov, potem stran ne deluje enkrat na tri tedne. In če imate 200 strežnikov, potem spletno mesto ne deluje vsak teden. To je problem.

Kaj je mogoče storiti za izboljšanje tolerance napak strežnika SQL? Wikipedia nas vabi h gradnji visoko razpoložljiva gruča: kjer je v primeru okvare katere od komponent rezervna.

To zahteva floto drage opreme: številna podvajanja, optična vlakna, skupno shranjevanje in vključitev rezerve ne deluje zanesljivo: približno 10 % preklopov se konča z odpovedjo rezervnega vozlišča kot vlak za glavnim vozliščem.

Toda glavna pomanjkljivost tako visoko razpoložljivega grozda je ničelna razpoložljivost, če podatkovni center, v katerem se nahaja, odpove. Odnoklassniki ima štiri podatkovne centre in moramo zagotoviti delovanje v primeru popolne okvare enega od njih.

Za to bi lahko uporabili Multi-Master podvajanje, vgrajeno v SQL Server. Ta rešitev je veliko dražja zaradi stroškov programske opreme in ima znane težave s podvajanjem - nepredvidljive zamude transakcij pri sinhronem podvajanju in zamude pri uporabi podvajanj (in posledično izgubljenih sprememb) pri asinhronem podvajanju. Implicitno ročno reševanje konfliktov naredi to možnost za nas popolnoma neuporabno.

Vsi ti problemi so zahtevali radikalno rešitev in začeli smo jih podrobno analizirati. Tukaj se moramo seznaniti s tem, kaj SQL Server v glavnem počne - transakcije.

Enostavna transakcija

Oglejmo si najpreprostejšo transakcijo z vidika uporabnega programerja SQL: dodajanje fotografije v album. Albumi in fotografije so shranjeni v različnih ploščah. Album ima javni števec fotografij. Nato je takšna transakcija razdeljena na naslednje korake:

  1. Album blokiramo s ključem.
  2. Ustvarite vnos v tabeli fotografij.
  3. Če ima fotografija javni status, dodajte števec javnih fotografij v album, posodobite zapis in potrdite transakcijo.

Ali v psevdo kodi:

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

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

TX.commit();

Vidimo, da je najpogostejši scenarij za poslovno transakcijo prebrati podatke iz baze podatkov v pomnilnik aplikacijskega strežnika, nekaj spremeniti in shraniti nove vrednosti nazaj v bazo podatkov. Običajno pri takšni transakciji posodobimo več entitet, več tabel.

Pri izvajanju transakcije lahko pride do sočasne spremembe istih podatkov iz drugega sistema. Na primer, Antispam se lahko odloči, da je uporabnik nekako sumljiv in zato vse njegove fotografije ne bi smele biti več javne, temveč jih je treba poslati v moderiranje, kar pomeni, da spremeni photo.status v neko drugo vrednost in izklopi ustrezne števce. Očitno, če se ta operacija zgodi brez zagotovil o atomičnosti aplikacije in izolaciji konkurenčnih modifikacij, kot v KISLINA, potem rezultat ne bo tak, kot je potreben - bodisi bo števec fotografij pokazal napačno vrednost ali pa vse fotografije ne bodo poslane v moderiranje.

V celotnem obstoju Odnoklassnikov je bilo napisanih veliko podobnih kod, ki manipulirajo z različnimi poslovnimi subjekti znotraj ene transakcije. Na podlagi izkušenj s selitvami na NoSQL iz Končna doslednost Vemo, da največji izziv (in naložba v čas) prihaja iz razvoja kode za ohranjanje doslednosti podatkov. Zato smo menili, da je glavna zahteva za novo shrambo zagotavljanje resničnih transakcij ACID za logiko aplikacije.

Druge, nič manj pomembne zahteve so bile:

  • Če podatkovni center odpove, morata biti na voljo tako branje kot pisanje v novo shrambo.
  • Ohranjanje trenutne hitrosti razvoja. To pomeni, da mora biti pri delu z novim repozitorijem količina kode približno enaka; v repozitorij ne bi smelo biti treba ničesar dodajati, razvijati algoritmov za reševanje konfliktov, vzdrževanje sekundarnih indeksov itd.
  • Hitrost novega pomnilnika je morala biti precej visoka, tako pri branju podatkov kot pri obdelavi transakcij, kar je dejansko pomenilo, da akademsko stroge, univerzalne, a počasne rešitve, kot je npr. dvofazne zaveze.
  • Samodejno skaliranje na letenju.
  • Uporaba običajnih poceni strežnikov, brez potrebe po nakupu eksotične strojne opreme.
  • Možnost razvoja skladišča s strani razvijalcev podjetja. Z drugimi besedami, prednost so imele lastniške ali odprtokodne rešitve, po možnosti v Javi.

Odločitve, odločitve

Z analizo možnih rešitev smo prišli do dveh možnih arhitekturnih izbir:

Prvi je, da vzamete kateri koli strežnik SQL in implementirate zahtevano toleranco napak, mehanizem skaliranja, gručo za preklop, razrešitev konfliktov in porazdeljene, zanesljive in hitre transakcije ACID. To možnost smo ocenili kot zelo netrivialno in delovno intenzivno.

Druga možnost je, da vzamete že pripravljeno shrambo NoSQL z implementiranim skaliranjem, failover clusterjem, razreševanjem konfliktov in sami implementirate transakcije in SQL. Na prvi pogled je celo naloga implementacije SQL, da ne omenjamo transakcij ACID, videti kot naloga, ki bo trajala leta. Potem pa smo ugotovili, da je nabor funkcij SQL, ki ga uporabljamo v praksi, tako daleč od ANSI SQL Cassandra CQL daleč od ANSI SQL. Ko smo še podrobneje pogledali CQL, smo ugotovili, da je precej blizu temu, kar potrebujemo.

Cassandra in CQL

Torej, kaj je zanimivo pri Cassandri, kakšne zmožnosti ima?

Prvič, tukaj lahko ustvarite tabele, ki podpirajo različne vrste podatkov; na primarnem ključu lahko izvedete SELECT ali UPDATE.

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

Za zagotovitev konsistentnosti podatkov replike Cassandra uporablja kvorumski pristop. V najpreprostejšem primeru to pomeni, da ko so tri replike iste vrstice postavljene na različna vozlišča gruče, se pisanje šteje za uspešno, če je večina vozlišč (to je dve od treh) potrdila uspeh te operacije pisanja. . Podatki vrstice se štejejo za konsistentne, če je bila pri branju večina vozlišč vprašanih in jih je potrdila. Tako je s tremi replikami zagotovljena popolna in takojšnja konsistentnost podatkov, če eno vozlišče odpove. Ta pristop nam je omogočil implementacijo še bolj zanesljive sheme: vedno pošiljajte zahteve vsem trem replikam in čakajte na odgovor dveh najhitrejših. Pozen odgovor tretje replike je v tem primeru zavržen. Vozlišče, ki se pozno odzove, ima lahko resne težave - zavore, zbiranje smeti v JVM, neposredno pridobivanje pomnilnika v jedru Linuxa, okvara strojne opreme, prekinitev povezave z omrežjem. Vendar to na noben način ne vpliva na delovanje ali podatke stranke.

Imenuje se pristop, ko vzpostavimo stik s tremi vozlišči in prejmemo odgovor od dveh špekulacije: zahteva za dodatne replike je poslana še preden »odpade«.

Druga prednost Cassandre je Batchlog, mehanizem, ki zagotavlja, da je serija sprememb, ki jih naredite, v celoti uporabljena ali pa se sploh ne uporabi. To nam omogoča, da rešimo A v ACID - atomičnost takoj po namestitvi.

Najbližje transakcijam v Cassandri so t.i.lahke transakcije". Vendar so daleč od "pravih" transakcij ACID: pravzaprav je to priložnost za izvedbo CAS na podatkih iz samo enega zapisa z uporabo soglasja z uporabo težkega protokola Paxos. Zato je hitrost takšnih transakcij nizka.

Kaj smo pogrešali v Cassandri

Tako smo morali implementirati prave transakcije ACID v Cassandri. Z njegovo uporabo bi lahko enostavno implementirali dve drugi priročni funkciji klasičnega DBMS: konsistentne hitre indekse, ki bi nam omogočili izvajanje izbire podatkov ne samo po primarnem ključu, in redni generator monotonih samodejnih inkrementacijskih ID-jev.

C*Ena

Tako se je rodil nov DBMS C*Ena, sestavljen iz treh vrst strežniških vozlišč:

  • Storage – (skoraj) standardni strežniki Cassandra, odgovorni za shranjevanje podatkov na lokalnih diskih. Z naraščanjem obremenitve in obsega podatkov je mogoče njihovo količino enostavno povečati na desetine in stotine.
  • Koordinatorji transakcij - zagotavljajo izvedbo transakcij.
  • Odjemalci so aplikacijski strežniki, ki izvajajo poslovne operacije in sprožajo transakcije. Takih strank je lahko na tisoče.

NewSQL = NoSQL+ACID

Strežniki vseh vrst so del skupne gruče, uporabljajo interni protokol sporočil Cassandra za medsebojno komunikacijo in gossip za izmenjavo informacij o grozdu. S Heartbeat strežniki spoznavajo medsebojne napake, vzdržujejo enotno podatkovno shemo – tabele, njihovo strukturo in replikacijo; razdelitvena shema, topologija gruče itd.

Odjemalci

NewSQL = NoSQL+ACID

Namesto standardnih gonilnikov se uporablja način Fat Client. Takšno vozlišče ne shranjuje podatkov, lahko pa deluje kot koordinator za izvajanje zahtev, to pomeni, da naročnik sam deluje kot koordinator svojih zahtev: poizveduje po kopijah shranjevanja in rešuje konflikte. To ni samo bolj zanesljivo in hitrejše od standardnega gonilnika, ki zahteva komunikacijo z oddaljenim koordinatorjem, ampak omogoča tudi nadzor prenosa zahtev. Zunaj transakcije, odprte na odjemalcu, se zahteve pošljejo v repozitorije. Če je stranka odprla transakcijo, se vse zahteve znotraj transakcije pošljejo koordinatorju transakcij.
NewSQL = NoSQL+ACID

C*One Transaction Coordinator

Koordinator je nekaj, kar smo implementirali za C*One iz nič. Odgovoren je za upravljanje transakcij, zaklepanja in vrstnega reda uporabe transakcij.

Za vsako servisirano transakcijo koordinator ustvari časovni žig: vsaka naslednja transakcija je večja od prejšnje transakcije. Ker Cassandrin sistem reševanja sporov temelji na časovnih žigih (od dveh nasprotujočih si zapisov se tisti z zadnjim časovnim žigom šteje za trenutnega), bo konflikt vedno rešen v korist naslednje transakcije. Tako smo implementirali Lamportova ura - poceni način reševanja konfliktov v porazdeljenem sistemu.

Ključavnice

Da bi zagotovili izolacijo, smo se odločili za najpreprostejšo metodo - pesimistične ključavnice na podlagi primarnega ključa zapisa. Z drugimi besedami, v transakciji je treba zapis najprej zakleniti, šele nato prebrati, spremeniti in shraniti. Šele po uspešni potrditvi je mogoče zapis odkleniti, tako da ga lahko uporabljajo konkurenčne transakcije.

Izvedba takega zaklepanja je preprosta v nerazdeljenem okolju. V porazdeljenem sistemu obstajata dve glavni možnosti: implementacija porazdeljenega zaklepanja v gruči ali porazdelitev transakcij tako, da transakcije, ki vključujejo isti zapis, vedno servisira isti koordinator.

Ker so v našem primeru podatki že razdeljeni med skupine lokalnih transakcij v SQL, je bilo odločeno, da koordinatorjem dodelimo lokalne transakcijske skupine: en koordinator izvaja vse transakcije z žetoni od 0 do 9, drugi - z žetoni od 10 do 19, in tako naprej. Posledično postane vsak od primerkov koordinatorja glavni v transakcijski skupini.

Nato se lahko zaklepanja implementirajo v obliki banalnega HashMapa v pomnilniku koordinatorja.

Napake koordinatorja

Ker en koordinator služi izključno skupini transakcij, je zelo pomembno, da hitro ugotovite dejstvo njegovega neuspeha, tako da se drugi poskus izvedbe transakcije izteče. Da bi bilo to hitro in zanesljivo, smo uporabili popolnoma povezan protokol quorum hearbeat:

Vsak podatkovni center gosti vsaj dve koordinatorski vozlišči. Vsak koordinator periodično pošlje srčni utrip drugim koordinatorjem in jih obvesti o svojem delovanju ter katera srčna sporočila je nazadnje prejel od katerih koordinatorjev v gruči.

NewSQL = NoSQL+ACID

Ob prejemanju podobnih informacij od drugih kot del svojih srčnih sporočil se vsak koordinator sam odloči, katera vozlišča gruče delujejo in katera ne, pri čemer se ravna po načelu kvoruma: če je vozlišče X prejelo informacije od večine vozlišč v gruči o normalnem prejemanje sporočil iz vozlišča Y, potem , Y deluje. In obratno, takoj ko večina poroča o manjkajočih sporočilih iz vozlišča Y, je Y zavrnil. Nenavadno je, da če kvorum obvesti vozlišče X, da od njega ne prejema več sporočil, bo samo vozlišče X menilo, da je neuspešno.

Sporočila srčnega utripa se pošiljajo z visoko frekvenco, približno 20-krat na sekundo, s periodo 50 ms. V Javi je težko zagotoviti odziv aplikacije v 50 ms zaradi primerljive dolžine premorov, ki jih povzroča zbiralnik smeti. Ta odzivni čas smo lahko dosegli z uporabo zbiralnika smeti G1, ki nam omogoča, da določimo cilj za trajanje pavz GC. Včasih pa zelo redko pavze kolektorja presegajo 50 ms, kar lahko privede do lažne zaznave napake. Da se to ne bi zgodilo, koordinator ne javlja okvare oddaljenega vozlišča, ko izgine prvo sporočilo srčnega utripa z njega, le če jih je izginilo več zaporedoma.Tako nam je uspelo zaznati okvaro koordinatorskega vozlišča v 200 gospa.

Vendar ni dovolj, da bi hitro razumeli, katero vozlišče je prenehalo delovati. Nekaj ​​moramo storiti glede tega.

Rezervacija

Klasična shema vključuje, v primeru glavne napake, začetek novih volitev z uporabo enega od modno univerzalno algoritmi. Imajo pa takšni algoritmi znane težave s časovno konvergenco in dolžino samega volilnega procesa. Takšnim dodatnim zamudam smo se lahko izognili s shemo zamenjave koordinatorja v popolnoma povezanem omrežju:

NewSQL = NoSQL+ACID

Recimo, da želimo izvesti transakcijo v skupini 50. Vnaprej določimo nadomestno shemo, to je, katera vozlišča bodo v primeru okvare glavnega koordinatorja izvajala transakcije v skupini 50. Naš cilj je ohraniti funkcionalnost sistema v primeru okvare podatkovnega centra. Določimo, da bo prva rezerva vozlišče iz drugega podatkovnega centra, druga rezerva pa bo vozlišče iz tretjega. Ta shema je izbrana enkrat in se ne spremeni, dokler se ne spremeni topologija gruče, torej dokler vanjo ne vstopijo nova vozlišča (kar se zgodi zelo redko). Postopek izbire novega aktivnega masterja, če stari odpove, bo vedno sledeč: prva rezerva postane aktivni master, če je ta prenehal delovati, pa postane aktivni master druga rezerva.

Ta shema je bolj zanesljiva od univerzalnega algoritma, saj je za aktiviranje novega glavnega dovolj, da se ugotovi napaka starega.

Toda kako bodo stranke razumele, kateri mojster zdaj dela? Nemogoče je v 50 ms poslati informacije na tisoče strank. Možna je situacija, ko stranka pošlje zahtevo za odprtje transakcije, ne da bi vedela, da ta master ne deluje več, in zahteva bo potekla. Da se to ne bi zgodilo, stranke špekulativno pošljejo zahtevo za odprtje transakcije poveljniku skupine in obema njegovima rezervama naenkrat, vendar se na to zahtevo odzove le tisti, ki je trenutno aktivni poveljnik. Naročnik bo vso nadaljnjo komunikacijo znotraj transakcije izvajal samo z aktivnim masterjem.

Rezervni mojstri prejete zahteve za transakcije, ki niso njihove, postavijo v čakalno vrsto nerojenih transakcij, kjer so shranjene nekaj časa. Če aktivni master umre, novi master obdela zahteve za odpiranje transakcij iz svoje čakalne vrste in odgovori odjemalcu. Če je stranka že odprla transakcijo s starim mojstrom, se drugi odgovor ignorira (in seveda takšna transakcija ne bo dokončana in jo bo stranka ponovila).

Kako poteka transakcija

Recimo, da je stranka koordinatorju poslala zahtevo za odprtje transakcije za to in to entiteto s tem in tem primarnim ključem. Koordinator zaklene to entiteto in jo postavi v tabelo zaklepanja v pomnilniku. Če je potrebno, koordinator prebere to entiteto iz pomnilnika in shrani nastale podatke v stanju transakcije v koordinatorjev pomnilnik.

NewSQL = NoSQL+ACID

Ko želi odjemalec spremeniti podatke v transakciji, pošlje koordinatorju zahtevo za spremembo entitete, koordinator pa postavi nove podatke v tabelo stanja transakcije v pomnilnik. S tem je snemanje končano - v pomnilnik se ne snema.

NewSQL = NoSQL+ACID

Ko stranka zahteva lastne spremenjene podatke kot del aktivne transakcije, koordinator ravna na naslednji način:

  • če je ID že v transakciji, se podatki vzamejo iz pomnilnika;
  • če v pomnilniku ni ID-ja, se manjkajoči podatki preberejo iz pomnilniških vozlišč v kombinaciji s tistimi, ki so že v pomnilniku, rezultat pa se posreduje odjemalcu.

Tako lahko odjemalec bere svoje spremembe, drugi odjemalci pa teh sprememb ne vidijo, ker so shranjene samo v pomnilniku koordinatorja, niso pa še v vozliščih Cassandra.

NewSQL = NoSQL+ACID

Ko odjemalec pošlje potrditev, koordinator shrani stanje, ki je bilo v pomnilniku storitve, v zabeleženi paket in se pošlje kot zabeležen paket v shrambo Cassandra. Trgovine naredijo vse, kar je potrebno, da se ta paket atomsko (popolnoma) uporabi, in vrnejo odgovor koordinatorju, ki sprosti ključavnice in stranki potrdi uspešnost transakcije.

NewSQL = NoSQL+ACID

Za vrnitev nazaj mora koordinator sprostiti samo pomnilnik, ki ga zaseda stanje transakcije.

Kot rezultat zgornjih izboljšav smo uvedli načela ACID:

  • Atomičnost. To je zagotovilo, da nobena transakcija ne bo delno zabeležena v sistemu; ali bodo vse njene podoperacije zaključene ali pa nobena. Tega načela se držimo prek zabeležene serije v Cassandri.
  • Doslednost. Vsaka uspešna transakcija po definiciji beleži samo veljavne rezultate. Če se po odprtju transakcije in izvedbi dela operacij ugotovi, da rezultat ni veljaven, se izvede povrnitev.
  • Izolacija. Ko se transakcija izvede, sočasne transakcije ne smejo vplivati ​​na njen izid. Konkurenčne transakcije so izolirane s pesimističnimi zaklepi na koordinatorju. Za branja zunaj transakcije se načelo izolacije upošteva na ravni Read Committed.
  • Stabilnost. Ne glede na težave na nižjih ravneh – izpad sistema, okvara strojne opreme – bi morale spremembe, narejene z uspešno zaključeno transakcijo, ostati ohranjene, ko se operacije nadaljujejo.

Branje po indeksih

Vzemimo preprosto tabelo:

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

Ima ID (primarni ključ), lastnika in datum spremembe. Vložiti morate zelo preprosto zahtevo - izberite podatke o lastniku z datumom spremembe "za zadnji dan".

SELECT *
WHERE owner=?
AND modified>?

Da bi bila taka poizvedba hitro obdelana, morate v klasičnem SQL DBMS zgraditi indeks po stolpcih (lastnik, spremenjen). To lahko storimo zelo enostavno, saj imamo zdaj garancije ACID!

Indeksi v C*One

Obstaja izvorna tabela s fotografijami, v kateri je ID zapisa primarni ključ.

NewSQL = NoSQL+ACID

Za indeks C*One ustvari novo tabelo, ki je kopija izvirnika. Ključ je enak izrazu indeksa in vključuje tudi primarni ključ zapisa iz izvorne tabele:

NewSQL = NoSQL+ACID

Zdaj lahko poizvedbo za "lastnik za zadnji dan" prepišete kot izbiro iz druge tabele:

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

Konsistentnost podatkov v izvorni tabeli fotografij in indeksni tabeli i1 samodejno vzdržuje koordinator. Samo na podlagi podatkovne sheme koordinator ob prejeti spremembi generira in shrani spremembo ne samo v glavni tabeli, ampak tudi v kopijah. Na indeksni tabeli se ne izvajajo nobena dodatna dejanja, dnevniki se ne berejo in ne uporabljajo se nobene ključavnice. To pomeni, da dodajanje indeksov ne porabi skoraj nobenih virov in praktično ne vpliva na hitrost uporabe sprememb.

Z uporabo ACID smo lahko implementirali indekse, podobne SQL. So dosledni, razširljivi, hitri, sestavljivi in ​​vgrajeni v poizvedovalni jezik CQL. Za podporo indeksov niso potrebne nobene spremembe kode aplikacije. Vse je tako preprosto kot v SQL. In kar je najpomembnejše, indeksi ne vplivajo na hitrost izvajanja sprememb prvotne transakcijske tabele.

Kaj se je zgodilo

C*One smo razvili pred tremi leti in ga dali v komercialno uporabo.

Kaj smo na koncu dobili? Ovrednotimo to na primeru podsistema za obdelavo in shranjevanje fotografij, enega najpomembnejših vrst podatkov v družbenem omrežju. Ne govorimo o telesih samih fotografij, ampak o vseh vrstah metainformacij. Zdaj ima Odnoklassniki približno 20 milijard takih zapisov, sistem obdela 80 tisoč zahtev za branje na sekundo, do 8 tisoč transakcij ACID na sekundo, povezanih s spreminjanjem podatkov.

Ko smo uporabili SQL s faktorjem podvajanja = 1 (vendar v RAID 10), so bile metainformacije o fotografijah shranjene v visoko razpoložljivi gruči 32 strojev, ki izvajajo Microsoft SQL Server (plus 11 varnostnih kopij). Za shranjevanje varnostnih kopij je bilo dodeljenih tudi 10 strežnikov. Skupaj 50 dragih avtomobilov. Hkrati je sistem deloval z nazivno obremenitvijo, brez rezerve.

Po prehodu na nov sistem smo prejeli replikacijski faktor = 3 - kopijo v vsakem podatkovnem centru. Sistem je sestavljen iz 63 pomnilniških vozlišč Cassandra in 6 usklajevalnih strojev, kar je skupno 69 strežnikov. Toda ti stroji so veliko cenejši, njihov skupni strošek je približno 30% stroškov sistema SQL. Hkrati se obremenitev ohranja na 30%.

Z uvedbo C*One se je zmanjšala tudi zakasnitev: v SQL je operacija pisanja trajala približno 4,5 ms. V C*One - približno 1,6 ms. Trajanje transakcije je v povprečju manj kot 40 ms, potrditev je končana v 2 ms, trajanje branja in pisanja je v povprečju 2 ms. 99. percentil - samo 3-3,1 ms, število časovnih omejitev se je zmanjšalo za 100-krat - vse zaradi široke uporabe špekulacij.

Do zdaj je bila večina vozlišč SQL Server umaknjena iz uporabe; novi izdelki se razvijajo samo z uporabo C*One. C*One smo prilagodili za delo v našem oblaku en oblak, kar je omogočilo pospešitev uvajanja novih gruč, poenostavitev konfiguracije in avtomatizacijo delovanja. Brez izvorne kode bi bilo to veliko težje in okorneje.

Zdaj delamo na prenosu naših drugih prostorov za shranjevanje v oblak – a to je povsem druga zgodba.

Vir: www.habr.com

Dodaj komentar