NewSQL = NoSQL+ACID

NewSQL = NoSQL+ACID
Frem til nylig har omkring 50 TB med data blitt behandlet i sanntid i Odnoklassniki, lagret i SQL Server. For sÄ mye data er det nesten umulig Ä sikre rask og pÄlitelig, samt feiltolerant tilgang til datasenteret ved hjelp av SQL-databaser. Vanligvis brukes en av NoSQL-lagringene i slike tilfeller, men ikke alt kan flyttes til NoSQL: enkelte entiteter krever garantier for ACID-transaksjoner.

Dette fÞrte oss til Ä bruke et NewSQL-lagringssystem, altsÄ en database som tilbyr feiltoleranse, skalerbarhet og ytelsen til NoSQL-systemer, samtidig som den opprettholder de kjente ACID-garantiene fra klassiske systemer. Det finnes fÄ fungerende industrielle systemer av denne nye klassen, sÄ vi implementerte et lignende system selv og tok det i drift.

Hvordan dette fungerer og hva vi fikk til – les mer under.

I dag er den mÄnedlige publikum pÄ «Odnoklassniki» over 70 millioner unike besÞkende. Vi er blant de fem stÞrste sosiale mediene i verden, og blant de tyve nettstedene der brukerne tilbringer mest tid. Infrastrukturene til «OK» hÄndterer svÊrt hÞye belastninger: over en million HTTP-forespÞrsel/s pÄ frontene. Deler av serverparken, som bestÄr av mer enn 8000 enheter, er plassert nÊrt hverandre - i fire datasilos i Moskva, noe som muliggjÞr en nettverksforsinkelse pÄ mindre enn 1 ms mellom dem.

Vi har brukt Cassandra siden 2010, fra versjon 0.6. I dag er flere titalls klynger i drift. Den raskeste klyngen behandler over 4 millioner operasjoner per sekund, og den stĂžrste lagrer 260 TB.

Imidlertid er alt dette vanlige NoSQL-klynger som brukes til lagring av svakt konsistente data. Vi Ăžnsket imidlertid Ă„ erstatte det primĂŠre konsistente lageret, Microsoft SQL Server, som har blitt brukt siden «Odnoklassniki» ble grunnlagt. Lagringssystemet besto av mer enn 300 SQL Server Standard Edition-maskiner, som inneholdt 50 TB med data – forretningsentiteter. Disse dataene modifiseres innenfor ACID-transaksjoner og krever hĂžy konsistens.

For distribusjon av data mellom nodene i SQL Server brukte vi bĂ„de vertikal og horisontal partisjonering (sharding). Historisk har vi brukt et enkelt datashardingskjema: hver entitet ble tildelt en token — en funksjon av entitetens ID. Entiteter med samme token ble plassert pĂ„ en SQL-server. Master-detail-forhold ble implementert slik at tokenene for hoved- og underpost alltid stemte overens og befant seg pĂ„ samme server. I sosiale nettverk genereres nesten alle poster pĂ„ vegne av brukeren — dermed lagres alle brukerdata innenfor et funksjonelt subsystem pĂ„ Ă©n server. Det vil si at i forretningstransaksjoner deltok nesten alltid tabeller fra Ă©n SQL-server, noe som gjorde det mulig Ă„ sikre datakonsistens ved hjelp av lokale ACID-transaksjoner, uten behov for Ă„ bruke langsomme og upĂ„litelige distribuerte ACID-transaksjoner.

Takket vÊre sharding og for Ä Þke hastigheten pÄ SQL:

  • Vi bruker ikke fremmednĂžkkelbegrensninger, siden entitetens ID kan befinne seg pĂ„ en annen server ved sharding.
  • Vi bruker ikke lagrede prosedyrer og triggere pĂ„ grunn av ekstra belastning pĂ„ database-CPU-en.
  • Vi bruker ikke JOINs pĂ„ grunn av alt det ovennevnte og mange tilfeldige disklesinger.
  • Utenfor transaksjoner for Ă„ redusere dĂždlĂ„s bruker vi isolasjonsnivĂ„et Read Uncommitted.
  • Vi utfĂžrer kun korte transaksjoner (i gjennomsnitt kortere enn 100 ms).
  • Vi bruker ikke flerader UPDATE og DELETE pĂ„ grunn av mange dĂždlĂ„ser — vi oppdaterer kun Ă©n post om gangen.
  • ForespĂžrslene utfĂžres alltid kun etter indekser — en forespĂžrsel med et fullt tabellvisningsplan betyr overbelastning av databasen og dens svikt.

Disse trinnene har gjort det mulig Ä presse nesten maksimal ytelse ut av SQL-serverne. Imidlertid ble problemene stadig flere. La oss se pÄ dem.

Problemer med SQL

  • Siden vi brukte egenutviklet sharding, ble tilfĂžrsel av nye shards utfĂžrt manuelt av administratorene. I hele denne perioden betjente ikke skalerbare datakopier forespĂžrslene.
  • Etter hvert som antallet poster i tabellen Ăžker, reduseres hastigheten for innsetting og modifikasjon; nĂ„r man legger til indekser til eksisterende tabeller, synker hastigheten drastisk, og opprettelse og gjenoppretting av indekser skjer med nedetid.
  • TilstedevĂŠrelsen av et lite antall Windows-servere for SQL Server i produksjon kompliserer hĂ„ndteringen av infrastrukturen.

Men det stþrste problemet er —

Feiltoleranse

En klassisk SQL-server har dÄrlig feiloverfÞringsevne. La oss si at du bare har én database-server, og den feiler hvert tredje Är. I lÞpet av denne tiden fungerer ikke nettstedet i 20 minutter, noe som er akseptabelt. Men hvis du har 64 servere, vil nettstedet ikke fungere hver tredje uke. Og hvis du har 200 servere, vil nettstedet ikke fungere hver uke. Dette er et problem.

Hva kan gjÞres for Ä forbedre feiloverfÞringsevnen til en SQL-server? Wikipedia foreslÄr at vi bygger et hÞytilgjengelig klynge: der det finnes en duplikat i tilfelle en av komponentene feiler.

Dette krever en park av kostbart utstyr: omfattende duplisering, fiberoptikk, felles lagring, og det Ă„ inkludere reserver fungerer usikkert: rundt 10 % av oppstartene ender med feil i den reserve noden i rĂŠkken bakerst.

Men den stÞrste ulempen med en slik hÞytilgjengelig klynge er null tilgjengelighet ved svikt i datasenteret den er plassert i. «Odnoklassniki» har fire datasentre, og vi mÄ sikre drift under total havari i ett av dem.

For dette kunne man bruke Multi-Master replikering, innebygd i SQL Server. Denne lĂžsningen er betydelig dyrere pĂ„ grunn av programvarekostnaden og sliter med godt kjente problemer med replikering – uforutsigbare forsinkelser i transaksjoner ved synkron replikering og forsinkelser i anvendelsen av replikeringene (og dermed tapte modifikasjoner) ved asynkron. Det underforstĂ„tte manuelle konfliktlĂžsninger gjĂžr dette alternativet helt ubrukelig for oss.

Alle disse problemene krevde en radikal lĂžsning, og vi begynte en detaljert analyse av dem. Her mĂ„ vi bli kjent med hva SQL Server fĂžrst og fremst gjĂžr – transaksjoner.

En enkel transaksjon

La oss se pÄ den enkleste transaksjonen, fra et praktisk SQL-programmerers perspektiv: legge til et bilde i et album. Album og bilder lagres i ulike tabeller. Et album har en teller for offentlige bilder. Da deles en slik transaksjon opp i fÞlgende trinn:

  1. Vi lÄser albumet med en nÞkkel.
  2. Vi oppretter en post i bildets tabell.
  3. Hvis bildet har offentlig status, Ăžker vi tellingen av offentlige bilder i albumet, oppdaterer posten og forplikter transaksjonen.

Eller i form av pseudokode:

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

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

TX.commit();

Vi ser at det mest vanlige scenariet for forretningstransaksjoner er Ă„ lese data fra databasen inn i applikasjonsserverens minne, gjĂžre noen endringer og lagre de nye verdiene tilbake i databasen. Vanligvis i en slik transaksjon oppdaterer vi flere entiteter, flere tabeller.

NÄr transaksjonen utfÞres, kan det skje konkurrerende modifikasjoner av de samme dataene fra et annet system. For eksempel kan Antispam vurdere en bruker som mistenkelig, og derfor skal alle bilder til den brukeren ikke lenger vÊre offentlige, de mÄ sendes til moderering, noe som betyr at photo.status mÄ endres til en annen verdi og de tilhÞrende tellerne mÄ justeres. Det er Äpenbart at hvis denne operasjonen skjer uten garantier for atomisk anvendelse og isolasjon av konkurrerende modifikasjoner, som i ACID, vil ikke resultatet bli det som er nÞdvendig - enten vil foto-telleren vise en feil verdi, eller ikke alle bilder vil bli sendt til moderering.

Det er skrevet mye liknende kode, som manipulerer forskjellige forretningsenheter innenfor en enkelt transaksjon, gjennom tiden til Eksperimentene. Basert pÄ erfaringene fra migrering til NoSQL med Eventual Consistency vet vi at de stÞrste utfordringene (og tidskostnadene) kommer fra behovet for Ä utvikle kode som opprettholder datakonsistens. Derfor var det viktigste kravet til det nye lagringssystemet Ä sikre ekte ACID-transaksjoner for applikasjonslogikken.

Andre, minst like viktige, krav var:

  • Ved sentersvikt mĂ„ bĂ„de lesing og skriving til det nye lagringssystemet vĂŠre tilgjengelig.
  • Opprettholde den nĂ„vĂŠrende hastigheten for utvikling. Det vil si at arbeidet med det nye lagringssystemet ikke skal kreve mye mer kode, og det skal ikke vĂŠre behov for Ă„ skrive noe nytt i lagringssystemet, utvikle algoritmer for konfliktlĂžsning, opprettholde sekundĂŠre indekser osv.
  • Hastigheten pĂ„ det nye lagringssystemet mĂ„ vĂŠre tilstrekkelig hĂžy, bĂ„de ved datauthenting og ved behandling av transaksjoner, noe som effektivt betyr at akademisk strenge, universelle, men langsomme lĂžsninger, som for eksempel to-fase committer, ikke vil vĂŠre anvendelige..
  • Automatisk skalering i sanntid.
  • Bruken av vanlige, billige servere, uten behov for Ă„ kjĂžpe eksotisk maskinvare.
  • Muligheten for utvikling av lagringssystemet av selskapets utviklere. Med andre ord, prioriteten ble gitt til egne eller Ă„pne lĂžsninger, helst pĂ„ Java.

LĂžsninger, lĂžsninger

Ved Ă„ analysere mulige lĂžsninger kom vi til to mulige valg av arkitektur:

Den fÞrste er Ä ta en hvilken som helst SQL-server og implementere nÞdvendig feilresistens, skaleringsmekanisme, feilresistent klynge, konfliktlÞsing og distribuerte, pÄlitelige og hurtige ACID-transaksjoner. Vi vurderte dette alternativet som ganske uvanlig og arbeidskrevende.

Det andre alternativet er Ä ta et ferdig NoSQL-lager med implementert skalerbarhet, feilresistent klynge, konfliktlÞsing og implementere transaksjoner og SQL selv. Ved fÞrste Þyekast virker selv oppgaven med Ä implementere SQL, for ikke Ä snakke om ACID-transaksjoner, som en oppgave for flere Är. Men sÄ skjÞnte vi at settet av SQL-muligheter som vi bruker i praksis, er langt fra ANSI SQL sÄ langt som Cassandra CQL er langt fra ANSI SQL. Ved Ä se nÊrmere pÄ CQL, innsÄ vi at det er nÊrt det vi trenger.

Cassandra og CQL

SĂ„ hva er det interessante med Cassandra, hvilke muligheter har den?

For det fÞrste kan du opprette tabeller som stÞtter ulike datatyper, og du kan gjÞre SELECT eller UPDATE basert pÄ primÊrnÞkkelen.

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

For Ă„ sikre datakonsistens mellom replikaene, bruker Cassandra et kvorumtilnĂŠrmingI det enkleste tilfellet betyr dette at nĂ„r man setter opp tre repliker av samme rad pĂ„ forskjellige noder i klyngen, anses skriveoperasjonen som vellykket hvis flertallet av nodene (dvs. to av tre) har bekreftet suksessen med denne skriveoperasjonen. Raddataene anses som konsistente hvis flertallet av nodene ble forespurt under lesingen og bekreftet dem. Dermed garanterer tilstedevĂŠrelsen av tre repliker fullstendig og umiddelbar datakonsistens ved feil av en node. Denne tilnĂŠrmingen har gjort det mulig for oss Ă„ implementere et enda mer pĂ„litelig system: alltid sende forespĂžrslene til alle tre repliker, og vente pĂ„ svar fra de to raskeste. Det forsinkede svaret fra den tredje replikaen blir i slike tilfeller forkastet. Den forsinkede noden kan ha alvorlige problemer — forsinkelser, sĂžppelinnsamling i JVM, direkte minnefrigivelse i linux-kjernen, maskinvarefeil, nettverksavbrudd. Men dette pĂ„virker ikke klientoperasjonen eller dataene pĂ„ noen mĂ„te.

TilnÊrmingen der vi henvender oss til tre noder og fÄr svar fra to, kalles speklulasjon: forespÞrselen til de ekstra replikaene sendes fÞr de faktisk 'faller av'.

En annen fordel med Cassandra er Batchlog — en mekanisme som garanterer enten fullstendig anvendelse eller fullstendig annullering av pakken med endringer du gjþr. Dette lar oss lþse A i ACID — atomisitet rett ut av boksen.

Det som er nÊrmest transaksjoner i Cassandra er det som kalleslettvekts-transaksjoner, men de er langt fra 'ekte' ACID-transaksjoner: i realiteten er dette muligheten til Ä gjÞre CAS pÄ dataene til kun én oppfÞring, ved hjelp av konsensus via den tunge protokollen Paxos. Derfor er hastigheten pÄ slike transaksjoner ikke hÞy.

Hva vi manglet i Cassandra

SÄ vi mÄtte implementere ekte ACID-transaksjoner i Cassandra. Med dem kunne vi lett realisere to andre praktiske funksjoner fra klassiske DBMS: konsistente raske indekser, som ville gjÞre det mulig for oss Ä hente data ikke bare etter primÊrnÞkkelen, og en vanlig generator av monotont autoinkrementerende ID-er.

C*One

SÄ ble en ny DBMS fÞdt, C*Onesom bestÄr av tre typer servernoder:

  • Lager — (nesten) standard Cassandra-servere ansvarlige for lagring av data pĂ„ lokale disker. Etter hvert som belastningen og datamengden Ăžker, kan antallet enkelt skaleres opp til titalls og hundrevis.
  • Transaksjonskoordinatorer — sĂžrger for gjennomfĂžring av transaksjoner.
  • Kunder — applikasjonsservere som utfĂžrer forretningsoperasjoner og initierer transaksjoner. Det kan vĂŠre tusenvis av slike kunder.

NewSQL = NoSQL+ACID

Servere av alle typer er en del av et felles klynge, bruker den interne meldingsprotokollen Cassandra for Ă„ kommunisere med hverandre og gossip for Ă„ dele klyngeinformasjon. Gjennom Heartbeat fĂ„r serverne informasjon om gjensidige fiaskoer, opprettholder en felles dataskjema — tabeller, deres struktur og replikasjon; partisjoneringsskjema, topologi av klyngen, osv.

Kunder

NewSQL = NoSQL+ACID

I stedet for standarddrivere brukes Fat Client-modus. En slik node lagrer ikke data, men kan fungere som koordinator for forespÞrselene, det vil si at klienten selv utfÞrer koordinatorfunksjonen for forespÞrslene sine: spÞr replikene i lagringssystemet og lÞser konflikter. Dette er ikke bare mer pÄlitelig og raskere enn den standarddriveren som krever kommunikasjon med en ekstern koordinator, men det muliggjÞr ogsÄ administrasjon av forespÞrselsoverfÞring. Utenfor en Äpen transaksjon hos klienten sendes forespÞrslene til lagringene. Hvis klienten derimot har Äpnet en transaksjon, sendes alle forespÞrslene innenfor transaksjonen til transaksjonskoordinatoren.
NewSQL = NoSQL+ACID

Transaksjonskoordinator C*One

Koordinator — det vi har implementert for C*One fra bunnen av. Han er ansvarlig for administrasjon av transaksjoner, lĂ„ser og rekkefĂžlge av anvendelse av transaksjoner.

For hver hĂ„ndtert transaksjon genererer koordinatoren et tidsstempel: hvert pĂ„fĂžlgende er stĂžrre enn det for den foregĂ„ende transaksjonen. Siden konfliktlĂžsningen i Cassandra-systemet er basert pĂ„ tidsstempler (blant to konfliktfylte poster anses den som aktuell med det senere tidsstempelet), vil konflikten alltid bli lĂžst til fordel for den pĂ„fĂžlgende transaksjonen. Dermed har vi implementert Lamports klokker — en billig mĂ„te Ă„ lĂžse konflikter i et distribuert system.

LÄser

For Ă„ sikre isolasjon valgte vi den enkleste metoden — pessimistiske lĂ„ser basert pĂ„ primĂŠrnĂžkkelen til oppfĂžringen. Med andre ord, i transaksjonen mĂ„ posten fĂžrst lĂ„ses, deretter leses, modifiseres og lagres. FĂžrst etter en vellykket commit kan posten lĂ„ses opp, slik at konkurrrerende transaksjoner kan bruke den.

Implementering av en slik lÄsing er enkel i et ufordelt miljÞ. I et fordelt system er det to hovedveier: enten implementere en distribuert lÄsing i klyngen, eller fordele transaksjoner slik at transaksjoner som involverer en enkelt post alltid hÄndteres av den samme koordinatoren.

Siden dataene i vÄrt tilfelle allerede er fordelt pÄ grupper av lokale transaksjoner i SQL, ble det besluttet Ä tildele koordinatorene grupper av lokale transaksjoner: én koordinator hÄndterer alle transaksjoner med en token fra 0 til 9, den andre - fra 10 til 19, og sÄ videre. Resultatet er at hver av koordinatorinstansene blir mester for sin gruppe av transaksjoner.

Da kan lÄsene implementeres i form av en enkel HashMap i minnet til koordinatoren.

Koordinatorfeil

Siden én koordinator utelukkende betjener en gruppe transaksjoner, er det avgjÞrende Ä raskt fastslÄ om den har sviktet, slik at gjenopptakelsen av transaksjonen kan skje innen tidsfristen. For Ä oppnÄ dette raskt og pÄlitelig, har vi anvendt en fullstendig koblet kvorum heartbeat-protokoll:

Hver datasenter har minst to koordinatornoder. Regelmessig sender hver koordinator heartbeat-meldinger til de andre koordinatorene for Ă„ informere dem om at den fungerer, samt hvilke heartbeat-meldinger fra andre koordinatorer i klyngen den sist mottok.

NewSQL = NoSQL+ACID

Ved Ä motta lignende informasjon fra de andre koordinatorene i sine heartbeat-meldinger, bestemmer hver koordinator hvilke noder i klyngen som fungerer, og hvilke som ikke gjÞr det, basert pÄ kvorumprinsippet: hvis node X har fÄtt bekreftelse fra flertallet av nodene i klyngen om at meldinger fra node Y er mottatt normalt, fungerer Y. Og omvendt, sÄ snart flertallet rapporterer om tap av meldinger fra node Y, har Y sviktet. Det er interessant at hvis kvorum informerer node X om at den ikke har fÄtt meldinger fra den, vil node X selv anse seg som sviktet.

Heartbeat-meldinger sendes med hÞy frekvens, omtrent 20 ganger i sekundet, med en periode pÄ 50 ms. I Java er det vanskelig Ä garantere applikasjonens respons innen 50 ms pÄ grunn av sammenlignbare pauser forÄrsaket av sÞppelsamleren. Vi klarte Ä oppnÄ en slik respons tid ved Ä bruke G1-sÞppelsamler, som gjÞr det mulig Ä spesifisere mÄl for varigheten av GC-pauser. Imidlertid, av og til, sjeldent, overskrider pausesÞppelsamlerens tid 50 ms, noe som kan fÞre til falsk feiloppdagelse. For Ä unngÄ dette, varsler ikke koordinatoren om feil pÄ en ekstern node ved tap av den fÞrste heartbeat-meldingen fra den, men kun hvis flere meldinger gÄr tapt pÄ rad. PÄ den mÄten klarte vi Ä oppnÄ oppdagelse av feil pÄ koordinatoren innen 200 ms.

Men det er ikke nok Ä raskt forstÄ hvilken node som har sluttet Ä fungere. Det mÄ gjÞres noe med det.

Redundans

Den klassiske ordningen innebÊrer i tilfelle av feil hos master Ä starte valg av en ny med hjelp av en av de populÊre universelle algoritmene. Imidlertid har slike algoritmer godt kjente problemer med konvergens over tid og varigheten av selve valgprosessen. Slike ekstra forsinkelser klarte vi Ä unngÄ ved Ä bruke en erstatningsordning for koordinatorer i et fullt sammenkoblet nettverk:

NewSQL = NoSQL+ACID

La oss si at vi vil utfĂžre en transaksjon i gruppe 50. Vi definerer pĂ„ forhĂ„nd erstatningsordningen, det vil si hvilke noder som skal utfĂžre transaksjoner for gruppe 50 i tilfelle hovedkoordinatoren feiler. MĂ„let vĂ„rt er Ă„ opprettholde systemets funksjonalitet ved et datacenters svikt. Vi definerer at det fĂžrste reserve vil vĂŠre en node fra et annet datacenter, og det andre reserve vil vĂŠre en node fra det tredje. Denne ordningen velges en gang og endres ikke sĂ„ lenge klustertopologien ikke endres, det vil si sĂ„ lenge det ikke legges til nye noder i den (noe som skjer svĂŠrt sjeldent). Rangeringen for valg av en ny aktiv master ved feil hos den gamle vil alltid vĂŠre slik: den aktive masteren blir det fĂžrste reserve, og hvis det ogsĂ„ har sluttet Ă„ fungere – det andre reserve.

Denne ordningen er mer pÄlitelig enn den universelle algoritmen, ettersom det er tilstrekkelig Ä fastslÄ faktum om feilen til den gamle for Ä aktivere den nye masteren.

Men klientene forstÄr ikke hvilken av mesterne som jobber akkurat nÄ? PÄ 50 ms er det umulig Ä sende informasjon til tusenvis av kunder. Det kan oppstÄ en situasjon der kunden sender en forespÞrsel om Ä Äpne en transaksjon, uten Ä vite at den mesteren allerede ikke fungerer, og forespÞrselen vil henge pÄ time-out. For Ä unngÄ dette, sender kundene spekulativt forespÞrselen om Ä Äpne transaksjonen til gruppens mester og begge hans reservemestrene, men det vil bare vÊre den aktive mesteren som svarer pÄ denne forespÞrselen i Þyeblikket. All videre kommunikasjon i forbindelse med transaksjonen vil kunden ha bare med den aktive mesteren.

Reserve-mestrene plasserer forespÞrslene de mottar om andre mestres transaksjoner i kÞen for ikke-fÞdte transaksjoner, hvor de oppbevares en stund. Hvis den aktive mesteren dÞr, sÄ behandler den nye mesteren forespÞrslene om Ä Äpne transaksjoner fra sin egen kÞ og svarer kunden. Hvis kunden allerede har klart Ä Äpne en transaksjon med den gamle mesteren, sÄ ignoreres det andre svaret (og denne transaksjonen vil Äpenbart ikke fullfÞres og mÄ gjentas av kunden).

Hvordan fungerer en transaksjon

La oss si at kunden har sendt en forespÞrsel til koordinatoren om Ä Äpne en transaksjon for en bestemt enhet med en viss primÊrnÞkkel. Koordinatoren lÄser denne enheten og plasserer den i blokkeringstabellen i minnet. Om nÞdvendig leser koordinatoren denne enheten fra lagringen og lagrer de mottatte dataene i transaksjonens tilstand i koordinatorens minne.

NewSQL = NoSQL+ACID

NĂ„r kunden Ăžnsker Ă„ endre dataene i transaksjonen, sender han en forespĂžrsel til koordinatoren om Ă„ modifisere enheten, som da plasserer de nye dataene i tabellen for transaksjonsstatus i minnet. PĂ„ dette punktet er oppfĂžrselen fullfĂžrt – ingen skriving til lagringen finner sted.

NewSQL = NoSQL+ACID

NÄr kunden anmoder om egne endrede data innenfor en aktiv transaksjon, handler koordinatoren slik:

  • hvis ID allerede finnes i transaksjonen, tas dataene fra minnet;
  • hvis ID ikke finnes i minnet, leses de manglende dataene fra nodene i lagringen, kombineres med de allerede tilgjengelige i minnet, og resultatet returneres til kunden.

Dermed kan kunden lese sine egne endringer, mens andre kunder ikke ser disse endringene, fordi de bare lagres i koordinatorens minne; i nodene i Cassandra finnes de ikke ennÄ.

NewSQL = NoSQL+ACID

NÄr en klient sender en commit, blir tilstanden som har vÊrt i minnet hos tjenesten, lagret av koordinatoren i logged batch, og i form av logged batch sendes den til Cassandra-lagerne. Lagringene gjÞr alt nÞdvendig for at denne pakken skal bli atomisk (fullstendig) anvendt, og returnerer et svar til koordinatoren, som deretter frigjÞr lÄser og bekrefter transaksjonens suksess til klienten.

NewSQL = NoSQL+ACID

Og for Ă„ rulle tilbake trenger koordinatoren bare Ă„ frigjĂžre minnet som ble brukt til transaksjonens tilstand.

Som et resultat av de ovennevnte forbedringene har vi implementert ACID-prinsippene:

  • Atomisitet. Dette er en garanti for at ingen transaksjoner vil bli delvis bekreftet i systemet; enten blir alle deloperasjoner utfĂžrt, eller ingen av dem. I vĂ„rt tilfelle overholdes dette prinsippet ved hjelp av logged batch i Cassandra.
  • Konsistens. Hver vellykket transaksjon registrerer per definisjon bare gyldige resultater. Hvis det etter Ă„pning av en transaksjon og utfĂžrelse av deler av operasjonene oppdages at resultatet er ugyldig, utfĂžres en rulling tilbake.
  • Isolasjon. NĂ„r en transaksjon utfĂžres, mĂ„ parallelle transaksjoner ikke pĂ„virke dens resultat. Konkurrerende transaksjoner er isolert ved hjelp av pessimistiske lĂ„ser pĂ„ koordinatoren. For leseoperasjoner utenfor transaksjonen overholdes isolasjonsprinsippet pĂ„ nivĂ„et Read Committed.
  • Motstandskraft. Uavhengig av problemer pĂ„ lavere nivĂ„er — strĂžmbrudd eller maskinvarefeil — mĂ„ endringene gjort av en vellykket avsluttet transaksjon forbli lagret etter at funksjonen er gjenopptatt.

Lesing etter indekser

La oss ta en enkel tabell:

CREATE TABLE photos (
id bigint primary key,
eier bigint,
modifisert timestamp,

)

Den har en ID (primĂŠrnĂžkkel), eier og endringsdato. Vi mĂ„ gjĂžre en veldig enkel forespĂžrsel — velge data etter eier med endringsdato "de siste 24 timer".

SELECT *
WHERE owner=?
AND modified>?

For at en slik forespÞrsel skal kjÞre raskt, mÄ vi i en klassisk SQL-database bygge et indeks pÄ kolonnene (owner, modified). Dette kan vi gjÞre ganske enkelt, siden vi nÄ har garantier for ACID!

Indekser i C*One

Det finnes en original tabell med bilder, hvor ID-en til posten er primĂŠrnĂžkkelen.

NewSQL = NoSQL+ACID

For indeksen lager C*One en ny tabell som er en kopi av originalen. NÞkkelen samsvarer med indeksuttrykket, samtidig som den ogsÄ inkluderer den primÊre nÞkkelen fra originaltabellen:

NewSQL = NoSQL+ACID

NĂ„ kan spĂžrringen "eier for de siste 24 timene" skrives om som select fra en annen tabell:

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

Konsistensen til den opprinnelige tabellen photos og indekstabellen i1 opprettholdes automatisk av koordinatoren. Ut fra bare dataskjemaet, nÄr det gjÞres endringer, genererer og husker koordinatoren endringer ikke bare i hovedtabellen, men ogsÄ endringer i kopiene. Ingen ekstra handlinger med indeks-tabellen utfÞres, logger leses ikke, lÄsing brukes ikke. Det vil si at tillegg av indekser nesten ikke bruker ressurser og har praktisk talt ingen innvirkning pÄ hastigheten av modifikasjoner.

Med ACID klarte vi Ä implementere indekser "som i SQL". De har konsistens, kan skaleres, jobber raskt, kan vÊre sammensatte og integrert i CQL-spÞrresprÄket. For Ä stÞtte indekser er det ikke nÞdvendig med endringer i anvendt kode. Alt er enkelt, som i SQL. Og det viktigste, indeksene pÄvirker ikke hastigheten pÄ utfÞrelsen av modifikasjoner i transaksjons tabellen.

Hva fikk vi?

Vi utviklet C*One for tre Är siden og satte det i produksjon.

Hva fikk vi ut av dette? La oss vurdere det ved hjelp av delsystemet for behandling og lagring av bilder, en av de viktigste datatypene i sosiale nettverk. Det handler ikke om selve bildene, men om all slags metadata. NÄ er det omtrent 20 milliarder slike poster i "Odnoklassniki", systemet hÄndterer 80 000 lesespÞrringer i sekundet, opptil 8 000 ACID-transaksjoner i sekundet knyttet til datamodifikasjoner.

Da vi brukte SQL med replication factor = 1 (men i RAID 10), ble metadataene om bildene lagret pÄ en hÞyt tilgjengelig klynge bestÄende av 32 maskiner med Microsoft SQL Server (pluss 11 backup-maskiner). Det ble ogsÄ avsatt 10 servere for Ä lagre sikkerhetskopier. Totalt 50 kostbare maskiner. Systemet fungerte med nominell belastning, uten margin.

Etter migrering til det nye systemet fikk vi replication factor = 3 — en kopi i hvert datasenter. Systemet bestĂ„r av 63 Cassandra-lagringsnoder og 6 koordinatormaskiner, totalt 69 servere. Disse maskinene er imidlertid betydelig billigere, den totale kostnaden er omtrent 30 % av kostnaden ved SQL-systemet. Samtidig holdes belastningen pĂ„ nivĂ„et 30 %.

Med innfĂžringen av C*One har bĂ„de forsinkelser redusert: i SQL tok skriveoperasjonen omtrent 4,5 ms. I C*One — omtrent 1,6 ms. Gjennomsnittlig transaksjonstid er mindre enn 40 ms, commiten utfĂžres pĂ„ 2 ms, lese- og skrivevarigheten er i gjennomsnitt 2 ms. 99. percentil er bare 3-3,1 ms, antall tidsavbrudd har redusert med 100 ganger — alt takket vĂŠre det brede bruken av spekulasjoner.

Per nÄ er det meste av SQL Server-nodene tatt ut av drift, nye produkter utvikles kun ved bruk av C*One. Vi har tilpasset C*One for drift i vÄrt sky. one-cloud, noe som har gjort det mulig Ä akselerere opprettelsen av nye klynger, forenkle konfigurasjonen og automatisere driften. Uten kildekoden ville dette vÊrt betydelig mer komplisert og klÞnete.

Akkurat nĂ„ jobber vi med Ă„ flytte andre datalagre til skyen — men det er en helt annen historie.

Kilde: habr.com

KjĂžp pĂ„litelig hosting for nettsteder med DDoS-beskyttelse, VPS VDS-servere đŸ”„ KjĂžp pĂ„litelig hosting for nettsteder med DDoS-beskyttelse, VPS VDS-servere | ProHoster