NewSQL = NoSQL+ACID

NewSQL = NoSQL+ACID
Tot onlangs in Odnoklassniki is ongeveer 50 TB data wat intyds verwerk is in SQL Server gestoor. Vir so 'n volume is dit byna onmoontlik om vinnige en betroubare, en selfs foutverdraagsame datasentrumtoegang met behulp van SQL DBMS te verskaf. Gewoonlik word in sulke gevalle een van die NoSQL-winkels gebruik, maar nie alles kan na NoSQL oorgedra word nie: sommige entiteite vereis ACID-transaksiewaarborge.

Dit het ons gelei tot die gebruik van NewSQL-berging, dit wil sê 'n DBMS wat fouttoleransie, skaalbaarheid en werkverrigting van NoSQL-stelsels bied, maar terselfdertyd die ACID-waarborge behou wat bekend is aan klassieke stelsels. Daar is min werkende industriële stelsels van hierdie nuwe klas, so ons het so 'n stelsel self geïmplementeer en dit in kommersiële bedryf gestel.

Hoe dit werk en wat gebeur het – lees onder die snit.

Vandag is die maandelikse gehoor van Odnoklassniki meer as 70 miljoen unieke besoekers. Ons betree die top vyf grootste sosiale netwerke ter wêreld, en in die top twintig webwerwe waarop gebruikers die meeste tyd spandeer. Die “OK”-infrastruktuur hanteer baie hoë vragte: meer as 'n miljoen HTTP-versoeke/sek per front. Dele van die bedienerpark in die hoeveelheid van meer as 8000 1 stukke is naby mekaar geleë - in vier Moskou-datasentrums, wat dit moontlik maak om 'n netwerkvertraging van minder as XNUMX ms tussen hulle te verskaf.

Ons gebruik Cassandra sedert 2010, begin met weergawe 0.6. Vandag is 'n paar dosyn groepe in werking. Die vinnigste groep verwerk meer as 4 miljoen bewerkings per sekonde, terwyl die grootste een 260 TB stoor.

Dit is egter almal gewone NoSQL-klusters wat gebruik word om te stoor swak gekoördineer data. Ons wou ook die belangrikste konsekwente berging, Microsoft SQL Server, wat sedert die stigting van Odnoklassniki gebruik is, vervang. Die berging het bestaan ​​uit meer as 300 SQL Server Standard Edition-masjiene, wat 50 TB data bevat het – sake-entiteite. Hierdie data word gewysig as deel van ACID-transaksies en vereis hoë konsekwentheid.

Om data oor SQL Server-nodusse te versprei, het ons beide vertikaal en horisontaal gebruik afskorting (skerp). Histories het ons 'n eenvoudige dataversplinteringskema gebruik: elke entiteit is geassosieer met 'n teken - 'n funksie van die entiteit-ID. Entiteite met dieselfde teken is op dieselfde SQL-bediener geplaas. Die meester-detail-verhouding is op so 'n manier geïmplementeer dat die tokens van die hoof- en kindrekords altyd ooreenstem en op dieselfde bediener geleë is. In 'n sosiale netwerk word byna alle rekords namens die gebruiker gegenereer, wat beteken dat alle gebruikersdata binne een funksionele substelsel op een bediener gestoor word. Dit wil sê, 'n besigheidstransaksie het byna altyd tabelle van een SQL-bediener behels, wat dit moontlik gemaak het om datakonsekwentheid te verseker deur gebruik te maak van plaaslike ACID-transaksies, sonder dat dit nodig was om te gebruik stadig en onbetroubaar verspreide ACID-transaksies.

Danksy sharding en om SQL te bespoedig:

  • Ons gebruik nie Buitelandse sleutelbeperkings nie, aangesien die entiteit-ID op 'n ander bediener geleë kan wees wanneer dit gesny word.
  • Ons gebruik nie gestoorde prosedures en snellers nie as gevolg van die bykomende las op die DBMS SVE.
  • Ons gebruik nie JOINs nie as gevolg van al die bogenoemde en baie ewekansige lees vanaf skyf.
  • Buite 'n transaksie gebruik ons ​​die Lees Uncommitted-isolasievlak om dooiepunte te verminder.
  • Ons voer slegs kort transaksies uit (gemiddeld minder as 100ms).
  • Ons gebruik nie multi-ry UPDATE en DELETE nie as gevolg van 'n groot aantal dooiepunte - ons werk slegs een rekord op 'n slag op.
  • Navrae word altyd slegs deur indekse uitgevoer - 'n navraag met 'n volledige tabelskanderingsplan beteken vir ons 'n oorlading van die databasis en die mislukking daarvan.

Hierdie stappe het ons in staat gestel om byna die maksimum werkverrigting uit SQL-bedieners te druk. Die probleme het egter al hoe meer geword. Kom ons kyk na hulle.

Probleme met SQL

  • Aangesien ons selfgeskrewe versnippering gebruik het, is die byvoeging van nuwe skerwe handmatig deur administrateurs gedoen. Al hierdie tyd het skaalbare data replikas nie versoeke gedien nie.
  • Soos die aantal rekords in die tabel groei, neem die spoed van invoeging en wysiging af, wanneer indekse by 'n bestaande tabel gevoeg word, daal die spoed met 'n veelvoud, die skepping en herskepping van indekse neem 'n stilstand.
  • Om 'n klein hoeveelheid Windows vir SQL Server in produksie te hê, maak infrastruktuurbestuur moeilik

Maar die grootste probleem is

fout verdraagsaamheid

Klassieke SQL Server het swak fouttoleransie. Kom ons sê jy het net een databasisbediener en dit misluk elke drie jaar. Op hierdie tydstip is die webwerf vir 20 minute af, dit is aanvaarbaar. As u 64 bedieners het, is die webwerf een keer elke drie weke af. En as u 200 bedieners het, werk die webwerf nie elke week nie. Dit is 'n probleem.

Wat kan gedoen word om die fouttoleransie van die SQL-bediener te verbeter? Wikipedia nooi ons uit om te bou hoogs beskikbare cluster: waar in geval van mislukking van enige van die komponente daar 'n rugsteun is.

Dit vereis 'n vloot duur toerusting: talle duplisering, optiesevesel, gedeelde berging, en die insluiting van die reservaat werk nie betroubaar nie: ongeveer 10% van die insluitings eindig in die mislukking van die rugsteunnodus met 'n trein agter die hoofnodus.

Maar die grootste nadeel van so 'n hoogs beskikbare groepering is nul beskikbaarheid in die geval van mislukking van die datasentrum waarin dit geleë is. Odnoklassniki het vier datasentrums, en ons moet verseker werk in een van hulle in die geval van 'n volledige mislukking.

Hiervoor kan 'n mens aansoek doen Multi Meester replikasie ingebou in SQL Server. Hierdie oplossing is baie duurder as gevolg van die koste van sagteware en ly aan bekende replikasieprobleme - onvoorspelbare transaksievertragings met sinchroniese replikasie en vertragings in die toepassing van replikasie (en as gevolg daarvan verlore wysigings) met asinchroniese. geïmpliseer handmatige konflikoplossing maak hierdie opsie heeltemal ontoepasbaar vir ons.

Al hierdie probleme het 'n kardinale oplossing vereis en ons het tot hul gedetailleerde ontleding voortgegaan. Hier moet ons kennis maak met wat SQL Server basies doen - transaksies.

Eenvoudige transaksie

Oorweeg die eenvoudigste transaksie vanuit die oogpunt van 'n toegepaste SQL-programmeerder: die byvoeging van 'n foto by 'n album. Albums en foto's word in verskillende plate gestoor. Die album het 'n publieke fototoonbank. Dan word so 'n transaksie in die volgende stappe verdeel:

  1. Ons blokkeer die album per sleutel.
  2. Skep 'n inskrywing in die fototabel.
  3. As die foto 'n publieke status het, wikkel ons die toonbank van publieke foto's in die album op, werk die rekord op en pleeg die transaksie.

Of in pseudokode:

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

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

TX.commit();

Ons sien dat die mees algemene scenario vir 'n besigheidstransaksie is om data van die databasis in die geheue van die toepassingsbediener te lees, iets te verander en die nuwe waardes terug na die databasis te stoor. Gewoonlik in so 'n transaksie werk ons ​​verskeie entiteite, verskeie tabelle.

Wanneer 'n transaksie uitgevoer word, kan 'n gelyktydige wysiging van dieselfde data vanaf 'n ander stelsel plaasvind. Byvoorbeeld, Antispam kan besluit dat die gebruiker op een of ander manier agterdogtig is en daarom moet alle foto's van die gebruiker nie meer publiek wees nie, hulle moet gestuur word vir moderering, wat beteken dat foto.status na 'n ander waarde verander word en die ooreenstemmende tellers afgeskakel word. Natuurlik, as hierdie operasie sal plaasvind sonder waarborge van atomiteit van toepassing en isolasie van mededingende wysigings, soos in ACID, dan sal die resultaat nie wees wat jy nodig het nie - óf die fototeller sal die verkeerde waarde wys, óf nie alle foto's sal vir moderering gestuur word nie.

Baie van sulke kodes wat verskeie sake-entiteite binne 'n enkele transaksie manipuleer, is deur die hele bestaan ​​van Odnoklassniki geskryf. Volgens die ervaring van migrasies na NoSQL met Gebeurteniskonsekwentheid ons weet dat die grootste uitdaging (en tydrowend) die behoefte is om kode te ontwikkel om datakonsekwentheid te handhaaf. Daarom het ons die hoofvereiste vir die nuwe berging beskou as die voorsiening vir die toepassingslogika van regte ACID-transaksies.

Ander ewe belangrike vereistes was:

  • In die geval van 'n datasentrumfout, moet beide lees en skryf na die nuwe berging beskikbaar wees.
  • Die handhawing van die huidige spoed van ontwikkeling. Dit wil sê, wanneer daar met 'n nuwe bewaarplek gewerk word, moet die hoeveelheid kode ongeveer dieselfde wees, dit behoort nie nodig te wees om iets by die bewaarplek te voeg nie, algoritmes te ontwikkel om konflikte op te los, sekondêre indekse in stand te hou, ens.
  • Die spoed van die nuwe berging moet vinnig genoeg wees, beide wanneer data lees en wanneer transaksies verwerk word, wat effektief beteken het dat akademies streng, algemene, maar stadige oplossings, soos bv. twee-fase commits.
  • Outomatiese skaal op die vlieg.
  • Gebruik gewone goedkoop bedieners, sonder dat dit nodig is om eksotiese stukke yster te koop.
  • Moontlikheid van stoorontwikkeling deur die maatskappy se ontwikkelaars. Met ander woorde, prioriteit is gegee aan eie of oopbronoplossings, verkieslik in Java.

Besluite, besluite

Deur moontlike oplossings te ontleed, het ons met twee moontlike argitektuurkeuses vorendag gekom:

Die eerste is om enige SQL-bediener te neem en die vereiste fouttoleransie, skaalmeganisme, failover-groepering, konflikoplossing en verspreide, betroubare en vinnige ACID-transaksies te implementeer. Ons het hierdie opsie as hoogs nie-triviaal en tydrowend beoordeel.

Die tweede opsie is om 'n klaargemaakte NoSQL-berging te neem met geïmplementeerde skaal, failover-groepering, konflikoplossing en transaksies en SQL self te implementeer. Met die eerste oogopslag lyk selfs die taak om SQL te implementeer, om nie te praat van ACID-transaksies nie, jare lank na 'n taak. Maar toe besef ons dat die stel SQL-kenmerke wat ons in die praktyk gebruik so ver van ANSI SQL af is Cassandra CQL ver van ANSI SQL af. Met 'n nader kyk na CQL, het ons besef dat dit naby genoeg is aan wat ons nodig het.

Cassandra en CQL

So, wat is interessant aan Cassandra, watter kenmerke het dit?

Eerstens, hier kan u tabelle skep met ondersteuning vir verskillende datatipes, u kan SELECT of UPDATE volgens primêre sleutel doen.

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

Om konsekwentheid van replikadata te verseker, gebruik Cassandra kworumbenadering. In die eenvoudigste geval beteken dit dat wanneer drie replikas van dieselfde ry op verskillende nodusse van die groep geplaas word, die skryf as suksesvol beskou word as die meerderheid van die nodusse (d.i. twee uit drie) die sukses van hierdie skryfbewerking bevestig het. Die data van 'n reeks word as konsekwent beskou as, tydens lees, die meeste van die nodusse gepeil is en dit bevestig is. Dus, as daar drie replikas is, word volle en onmiddellike datakonsekwentheid gewaarborg as een nodus misluk. Hierdie benadering het ons in staat gestel om 'n selfs meer betroubare skema te implementeer: stuur altyd versoeke na al drie replikas en wag vir 'n antwoord van die twee vinnigstes. Die laat reaksie van die derde replika word dan weggegooi. Terselfdertyd kan 'n nodus wat laat is met 'n reaksie ernstige probleme hê - remme, vullisversameling in die JVM, direkte geheue-herwinning in die linux-kern, hardeware-fout, ontkoppeling van die netwerk. Kliëntbedrywighede en -data word egter op geen manier geraak nie.

Die benadering wanneer ons toegang tot drie nodusse kry en 'n antwoord van twee ontvang, word genoem spekulasie: 'n versoek vir ekstra replikas word gestuur voordat dit "afval".

Nog 'n voordeel van Cassandra is Batchlog, 'n meganisme wat verseker dat veranderinge wat jy maak óf volledig toegepas word óf nie heeltemal op die pakket toegepas word nie. Dit stel ons in staat om A in ACID - atomiteit uit die boks op te los.

Die naaste ding aan transaksies in Cassandra is die sogenaamde "ligte transaksies". Maar dit is ver van "regte" SUUR-transaksies: in werklikheid is dit 'n geleentheid om te maak CAS op die data van slegs een rekord, met behulp van die Paxos swaargewig protokol konsensus. Daarom is die spoed van sulke transaksies laag.

Wat ons gemis het in Cassandra

Dus, ons moes regte ACID-transaksies in Cassandra implementeer. Met die hulp daarvan kan ons maklik twee ander gerieflike kenmerke van die klassieke DBMS implementeer: konsekwente vinnige indekse, wat ons in staat sal stel om data nie net deur die primêre sleutel te kies nie, en die gewone genereerder van eentonige outo-inkrement-ID's.

Keël

So is die nuwe DBMS gebore Keël, bestaande uit drie tipes bediener nodusse:

  • Bergings is (amper) standaard Cassandra-bedieners wat verantwoordelik is vir die stoor van data op plaaslike dryf. Soos die vrag en volume van data groei, kan hul getal maklik tot tiene en honderde opgeskaal word.
  • Transaksiekoördineerders - verseker die uitvoering van transaksies.
  • Kliënte is toepassingsbedieners wat sakebedrywighede implementeer en transaksies inisieer. Daar kan duisende sulke kliënte wees.

NewSQL = NoSQL+ACID

Bedieners van alle soorte is in 'n gemeenskaplike groepering, gebruik Cassandra se interne boodskapprotokol om met mekaar te kommunikeer en skinder vir die uitruil van groepinligting. Met die hulp van Heartbeat leer bedieners oor wedersydse mislukkings, handhaaf 'n enkele dataskema - tabelle, hul struktuur en replikasie; partisieskema, cluster topologie, ens.

Kliënte

NewSQL = NoSQL+ACID

In plaas van standaardbestuurders word Fat Client-modus gebruik. So 'n nodus stoor nie data nie, maar kan as 'n versoekuitvoeringskoördineerder optree, dit wil sê, die Kliënt tree self op as 'n koördineerder van sy versoeke: dit ondersoek stoorreplikas en los konflikte op. Dit is nie net meer betroubaar en vinniger as die standaardbestuurder, wat kommunikasie met 'n afstandskoördineerder vereis nie, maar laat jou ook toe om die oordrag van versoeke te beheer. Buiten 'n transaksie wat op die kliënt oop is, word versoeke na stoorplekke gestuur. As die kliënt 'n transaksie oopgemaak het, word alle versoeke binne die transaksie na die transaksiekoördineerder gestuur.
NewSQL = NoSQL+ACID

C*Een Transaksiekoördineerder

Die koördineerder is wat ons van nuuts af vir C*One geïmplementeer het. Dit is verantwoordelik vir die bestuur van transaksies, slotte en die volgorde waarin transaksies toegepas word.

Vir elke gediensde transaksie genereer die koördineerder 'n tydstempel: elke daaropvolgende een is groter as dié van die vorige transaksie. Aangesien die konflikoplossingstelsel in Cassandra op tydstempels gebaseer is (van twee botsende rekords word die jongste tydstempel as relevant beskou), sal die konflik altyd ten gunste van die daaropvolgende transaksie opgelos word. So het ons geïmplementeer lamport horlosie is 'n goedkoop manier om konflikte in 'n verspreide stelsel op te los.

Slotte

Om isolasie te verseker, het ons besluit om die maklikste manier te gebruik - pessimistiese slotte op die primêre sleutel van die rekord. Met ander woorde, in 'n transaksie moet 'n rekord eers gesluit word, eers dan gelees, gewysig en gestoor word. Eers na 'n suksesvolle commit kan 'n rekord ontsluit word sodat mededingende transaksies dit kan gebruik.

Die implementering van so 'n slot is eenvoudig in 'n nie-verspreide omgewing. In 'n verspreide stelsel is daar twee hoof maniere: óf implementeer verspreide sluiting op 'n kluster, óf versprei transaksies sodat transaksies wat dieselfde rekord behels altyd deur dieselfde koördineerder bedien word.

Aangesien die data in ons geval reeds onder groepe plaaslike transaksies in SQL versprei is, is daar besluit om groepe plaaslike transaksies aan die koördineerders toe te wys: een koördineerder voer alle transaksies uit met 'n teken van 0 tot 9, die tweede - met 'n teken van 10 tot 19, ensovoorts. Gevolglik word elkeen van die gevalle van die koördineerder die meester van die transaksiegroep.

Dan kan slotte as 'n banale HashMap in die geheue van die koördineerder geïmplementeer word.

Weieringe van koördineerders

Aangesien een koördineerder uitsluitlik 'n groep transaksies bedien, is dit baie belangrik om vinnig die feit van die mislukking daarvan te bepaal sodat die herhaalde poging om die transaksie uit te voer binne die tydsverloop is. Om dit vinnig en betroubaar te maak, het ons 'n volledig gemaskeerde kworum-hoorklopprotokol gebruik:

Elke datasentrum huisves ten minste twee koördineerder nodusse. Elke koördineerder stuur periodiek 'n hartklopboodskap aan die ander koördineerders en lig hulle in oor die funksionering daarvan, asook oor die laaste keer dat dit hartklopboodskappe ontvang het van watter koördineerders in die groepering.

NewSQL = NoSQL+ACID

Deur soortgelyke inligting van die res as deel van hul hartklopboodskappe te ontvang, besluit elke koördineerder self watter nodusse van die groepering funksioneer en watter nie, gelei deur die kworumbeginsel: as nodus X inligting van die meerderheid nodusse in die groep ontvang het oor die normale ontvangs van boodskappe vanaf node Y, dan werk Y. Omgekeerd, sodra die meerderheid ontbrekende boodskappe van nodus Y rapporteer, dan het Y misluk. Vreemd genoeg, as die kworum node X vertel dat dit nie meer boodskappe van hom af ontvang nie, dan sal node X self homself as misluk beskou.

Hartklopboodskappe word teen 'n hoë frekwensie gestuur, ongeveer 20 keer per sekonde, met 'n tydperk van 50 ms. In Java is dit moeilik om 'n toepassingsreaksie binne 50ms te waarborg as gevolg van vergelykbare pousetye wat deur die vullisverwyderaar veroorsaak word. Ons kon hierdie reaksietyd bereik deur die G1-vullisverwyderaar te gebruik, wat jou toelaat om 'n teiken vir die duur van GC-pouses te spesifiseer. Soms, redelik selde, gaan die versamelaarpouse egter verder as 50 ms, wat kan lei tot 'n vals foutopsporing. Om dit te vermy, rapporteer die koördineerder nie die mislukking van die afgeleë nodus wanneer die eerste hartklopboodskap daarvan verlore is nie, slegs as verskeie in 'n ry ontbreek. Ons het dus daarin geslaag om die mislukking van die koördineerdernodus in 200 ms op te spoor.

Maar dit is nie genoeg om vinnig te verstaan ​​watter nodus opgehou het om te funksioneer nie. Iets moet daaraan gedoen word.

Bespreking

Die klassieke skema veronderstel dat in die geval van 'n versuim van die meester om die verkiesing van 'n nuwe een te loods deur een van die modieus universeel algoritmes. Sulke algoritmes het egter welbekende probleme met konvergensie in tyd en die duur van die verkiesingsproses self. Ons het daarin geslaag om sulke bykomende vertragings te vermy deur die koördineerdervervangingskema in 'n volledig gekoppelde netwerk te gebruik:

NewSQL = NoSQL+ACID

Kom ons sê ons wil 'n transaksie in groep 50 uitvoer. Kom ons definieer vooraf 'n vervangingskema, dit wil sê watter nodusse groep 50 transaksies sal uitvoer in geval van mislukking van die hoofkoördineerder. Ons doelwit is om die stelsel aan die gang te hou in die geval van 'n datasentrumfout. Kom ons definieer dat die eerste reserwe 'n nodus van 'n ander datasentrum sal wees, en die tweede reserwe sal 'n nodus van die derde wees. Hierdie skema word een keer gekies en verander nie totdat die topologie van die groep verander nie, dit wil sê totdat nuwe nodusse dit binnekom (wat baie selde gebeur). Die volgorde van die keuse van 'n nuwe aktiewe meester in die geval van mislukking van die ou een sal altyd soos volg wees: die eerste reserwe sal die aktiewe meester word, en as dit ophou funksioneer, sal die tweede reserwe word.

So 'n skema is meer betroubaar as 'n universele algoritme, want om 'n nuwe meester te aktiveer, is dit genoeg om die feit van die mislukking van die ou een te bepaal.

Maar hoe sal klante verstaan ​​watter van die meesters tans werk? Dit is onmoontlik om inligting binne 50 ms aan duisende kliënte te stuur. Dit is moontlik dat 'n kliënt 'n versoek stuur om 'n transaksie oop te maak, terwyl hy nog nie weet dat hierdie meester nie meer funksioneer nie, en die versoek sal op 'n time-out hang. Om te verhoed dat dit gebeur, stuur kliënte spekulatief 'n versoek om 'n transaksie te open aan die meester van die groep en albei sy reserwes op een slag, maar slegs die een wat op die oomblik die aktiewe meester is, sal op hierdie versoek reageer. Alle daaropvolgende kommunikasie binne die transaksie sal slegs deur die kliënt met die aktiewe meester uitgevoer word.

Die bystandmeesters plaas die ontvangde versoeke vir transaksies wat nie hul eie is nie in die tou van ongebore transaksies, waar dit vir 'n geruime tyd gestoor word. As die aktiewe meester sterf, verwerk die nuwe meester versoeke om transaksies uit sy tou oop te maak en reageer op die kliënt. As die kliënt reeds daarin geslaag het om 'n transaksie met die ou meester oop te maak, word die tweede reaksie geïgnoreer (en natuurlik sal so 'n transaksie nie voltooi word nie en sal dit deur die kliënt herhaal word).

Hoe 'n transaksie werk

Gestel die kliënt het 'n versoek aan die koördineerder gestuur om 'n transaksie vir so en so 'n entiteit met so en so 'n primêre sleutel oop te maak. Die koördineerder sluit hierdie entiteit en plaas dit in die slottabel in die geheue. Indien nodig, lees die koördineerder hierdie entiteit uit die winkel en stoor die ontvangde data in 'n transaksionele toestand in die koördineerder se geheue.

NewSQL = NoSQL+ACID

Wanneer 'n kliënt die data in 'n transaksie wil verander, stuur dit 'n versoek aan die koördineerder om die entiteit te wysig, en die koördineerder plaas die nuwe data in die transaksietoestandtabel in die geheue. Dit voltooi die opname - daar word nie na die berging geskryf nie.

NewSQL = NoSQL+ACID

Wanneer 'n kliënt sy eie gewysigde data as deel van 'n aktiewe transaksie aanvra, tree die koördineerder soos volg op:

  • as die ID reeds in die transaksie is, dan word die data uit die geheue geneem;
  • as daar geen ID in die geheue is nie, word die ontbrekende data vanaf die stoornodusse gelees, gekombineer met dié wat reeds in die geheue is, en die resultaat word aan die kliënt teruggestuur.

Die kliënt kan dus sy eie veranderinge lees, en ander kliënte sien nie hierdie veranderinge nie, want hulle word slegs in die geheue van die koördineerder gestoor, hulle is nog nie in die Cassandra-nodes nie.

NewSQL = NoSQL+ACID

Wanneer die kliënt 'n commit stuur, word die toestand wat die diens in die geheue gehad het, deur die koördineerder in 'n aangetekende bondel gestoor, en as 'n aangetekende bondel na die Cassandra-winkels gestuur. Die bewaarplekke doen alles wat nodig is vir hierdie pakket om atomies (ten volle) toegepas te word, en stuur 'n antwoord aan die koördineerder, wat die slotte vrystel en die sukses van die transaksie aan die kliënt bevestig.

NewSQL = NoSQL+ACID

En vir terugrol hoef die koördineerder net die geheue wat deur die toestand van die transaksie beset word, vry te stel.

As gevolg van die verbeterings wat hierbo beskryf is, het ons die beginsels van ACID geïmplementeer:

  • Atomiteit. Dit is 'n waarborg dat geen transaksie gedeeltelik in die stelsel vasgestel sal word nie, óf al sy sub-operasies sal uitgevoer word, óf nie een van hulle sal uitgevoer word nie. In ons geval word hierdie beginsel nagekom as gevolg van die aangetekende bondel in Cassandra.
  • Konsekwentheid. Elke suksesvolle transaksie, per definisie, verbind slegs geldige resultate. As daar na die opening van 'n transaksie en die uitvoering van sommige van die bewerkings gevind word dat die resultaat ongeldig is, word 'n terugrol uitgevoer.
  • isolasie. Wanneer 'n transaksie uitgevoer word, behoort parallelle transaksies nie die resultaat daarvan te beïnvloed nie. Gelyktydige transaksies word geïsoleer met pessimistiese slotte op die koördineerder. Vir lesings buite 'n transaksie word die beginsel van isolasie op die Read Committed-vlak gerespekteer.
  • Volhoubaarheid. Ongeag probleme op die laer vlakke - 'n stelsel-verduistering, 'n hardeware-fout - moet die veranderinge wat deur 'n suksesvol voltooide transaksie gemaak is, gestoor bly na die hervatting van funksionering.

Lees deur indekse

Kom ons neem 'n eenvoudige tabel:

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

Dit het 'n ID (primêre sleutel), 'n eienaar en 'n wysigingsdatum. U moet 'n baie eenvoudige versoek rig - kies data oor die eienaar met die datum van verandering "vir die laaste dag".

SELECT *
WHERE owner=?
AND modified>?

Om so 'n navraag vinnig te verwerk, in 'n klassieke SQL DBMS, moet jy 'n indeks op die kolomme bou (eienaar, gewysig). Ons kan dit baie eenvoudig doen, aangesien ons nou SUUR-waarborge het!

Indekse in C*One

Daar is 'n aanvanklike tabel met foto's waarin rekord-ID 'n primêre sleutel is.

NewSQL = NoSQL+ACID

Vir 'n indeks skep C*One 'n nuwe tabel wat 'n kopie van die oorspronklike tabel is. Die sleutel is dieselfde as die indeksuitdrukking, maar dit sluit ook die primêre sleutel van die rekord van die brontabel in:

NewSQL = NoSQL+ACID

Nou kan die navraag vir "eienaar in die afgelope XNUMX uur" herskryf word as 'n keuse uit 'n ander tabel:

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

Datakonsekwentheid tussen die brontabelfoto's en indeks i1 word outomaties deur die koördineerder gehandhaaf. Op grond van die dataskema alleen, wanneer 'n verandering ontvang word, genereer en onthou die koördineerder die verandering nie net van die hooftabel nie, maar ook die veranderinge van die kopieë. Geen bykomende aksies word met die indekstabel uitgevoer nie, logs word nie gelees nie, slotte word nie gebruik nie. Dit wil sê, die byvoeging van indekse verbruik amper nie hulpbronne nie en beïnvloed feitlik nie die spoed van die toepassing van wysigings nie.

Met behulp van ACID het ons daarin geslaag om indekse "soos in SQL" te implementeer. Hulle is konsekwent, skaalbaar, vinnig, saamstelbaar en ingebou in die CQL-navraagtaal. Indeksondersteuning vereis geen veranderinge aan die toepassingskode nie. Alles is eenvoudig, soos in SQL. En die belangrikste is dat indekse nie die spoed van uitvoering van wysigings aan die oorspronklike transaksietabel beïnvloed nie.

Wat het gebeur

Ons het C*One drie jaar gelede ontwikkel en dit in kommersiële bedryf gestel.

Waarmee het ons geëindig? Kom ons evalueer dit aan die hand van die voorbeeld van 'n substelsel vir die verwerking en berging van foto's, een van die belangrikste tipes data in 'n sosiale netwerk. Dit gaan nie oor die liggame van die foto's self nie, maar oor allerlei meta-inligting. Nou in Odnoklassniki is daar ongeveer 20 miljard sulke rekords, die stelsel verwerk 80 duisend leesversoeke per sekonde, tot 8 duisend ACID-transaksies per sekonde wat verband hou met datamodifikasie.

Toe ons SQL met replikasiefaktor = 1 (maar in RAID 10) gebruik het, is die foto-meta-inligting op 'n hoogs beskikbare groep van 32 Microsoft SQL Server-masjiene (plus 11 onderdele) gestoor. Ook is 10 bedieners toegeken vir die stoor van rugsteun. Totaal 50 duur motors. Terselfdertyd het die stelsel teen gegradeerde vrag gewerk, sonder 'n marge.

Nadat ons na 'n nuwe stelsel migreer het, het ons replikasiefaktor = 3 gekry - 'n kopie in elke datasentrum. Die stelsel bestaan ​​uit 63 Cassandra-bergingsnodes en 6 koördineerdermasjiene, vir 'n totaal van 69 bedieners. Maar hierdie masjiene is baie goedkoper, altesaam sowat 30% van die koste van 'n SQL-stelsel. In hierdie geval word die las op die vlak van 30% gehou.

Met die bekendstelling van C*One het latensie ook afgeneem: in SQL het 'n skryfbewerking ongeveer 4,5 ms. In C * Een - ongeveer 1,6 ms. Die duur van 'n transaksie is gemiddeld minder as 40 ms, die commit word in 2 ms voltooi, die duur van lees en skryf is gemiddeld 2 ms. Die 99ste persentiel is slegs 3-3,1 ms, die aantal time-outs het met 100 keer afgeneem – alles as gevolg van die wydverspreide gebruik van spekulasie.

Tot op datum is die meeste van die SQL Server-nodusse uit diens gestel, nuwe produkte word slegs ontwikkel met behulp van C * One. Ons het C*One aangepas om in ons wolk te werk een-wolk, wat dit moontlik gemaak het om die ontplooiing van nuwe groepe te versnel, konfigurasie te vereenvoudig en werking te outomatiseer. Sonder die bronkode sou dit baie moeiliker en afgesaag wees.

Nou werk ons ​​daaraan om ons ander bergings na die wolk oor te dra - maar dit is 'n heeltemal ander storie.

Bron: will.com

Voeg 'n opmerking