NewSQL = NoSQL+ACID

NewSQL = NoSQL+ACID
Inntil nylig lagret Odnoklassniki omtrent 50 TB med data behandlet i sanntid i SQL Server. For et slikt volum er det nesten umulig å gi rask og pålitelig, og til og med datasenterfeiltolerant tilgang ved hjelp av en SQL DBMS. Vanligvis, i slike tilfeller, brukes en av NoSQL-lagringene, men ikke alt kan overføres til NoSQL: noen enheter krever ACID-transaksjonsgarantier.

Dette førte oss til bruk av NewSQL-lagring, det vil si et DBMS som gir feiltoleranse, skalerbarhet og ytelse av NoSQL-systemer, men som samtidig opprettholder ACID-garantiene som er kjent for klassiske systemer. Det er få industrielle systemer av denne nye klassen, så vi implementerte et slikt system selv og satte det i kommersiell drift.

Hvordan det fungerer og hva som skjedde - les under kuttet.

I dag er det månedlige publikummet til Odnoklassniki mer enn 70 millioner unike besøkende. Vi Vi er blant de fem beste største sosiale nettverk i verden, og blant de tjue nettstedene som brukerne bruker mest tid på. OK-infrastrukturen håndterer svært høye belastninger: mer enn en million HTTP-forespørsler/sek. per front. Deler av en serverflåte på mer enn 8000 stykker er plassert nær hverandre - i fire Moskva-datasentre, noe som gjør det mulig å sikre en nettverksforsinkelse på mindre enn 1 ms mellom dem.

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

Dette er imidlertid alle vanlige NoSQL-klynger som brukes til lagring svakt koordinert data. Vi ønsket å erstatte hovedlagringen, Microsoft SQL Server, som har blitt brukt siden grunnleggelsen av Odnoklassniki. Lagringen besto av mer enn 300 SQL Server Standard Edition-maskiner, som inneholdt 50 TB med data - forretningsenheter. Disse dataene er modifisert som en del av ACID-transaksjoner og krever høy konsistens.

For å distribuere data på tvers av SQL Server-noder brukte vi både vertikale og horisontale oppdeling (skjæring). Historisk sett brukte vi et enkelt datadelingsskjema: hver enhet var assosiert med et token - en funksjon av enhets-IDen. Enheter med samme token ble plassert på samme SQL-server. Hoved-detalj-forholdet ble implementert slik at tokenene til hoved- og underordnede poster alltid samsvarte og var plassert på samme server. I et sosialt nettverk genereres nesten alle poster på vegne av brukeren – noe som betyr at alle brukerdata innenfor ett funksjonelt delsystem er lagret på én server. Det vil si at en forretningstransaksjon nesten alltid involverte tabeller fra én SQL-server, noe som gjorde det mulig å sikre datakonsistens ved hjelp av lokale ACID-transaksjoner, uten behov for bruk sakte og upålitelige distribuerte ACID-transaksjoner.

Takket være sharding og for å øke hastigheten på SQL:

  • Vi bruker ikke begrensninger for fremmednøkkel, siden under deling kan enhets-ID-en være plassert på en annen server.
  • Vi bruker ikke lagrede prosedyrer og triggere på grunn av den ekstra belastningen på DBMS CPU.
  • Vi bruker ikke JOINs på grunn av alt det ovennevnte og mange tilfeldige lesinger fra disk.
  • Utenfor en transaksjon bruker vi isolasjonsnivået Read Uncommitted for å redusere vranglåser.
  • Vi utfører kun korte transaksjoner (i gjennomsnitt kortere enn 100 ms).
  • Vi bruker ikke UPDATE og SLETT med flere rader på grunn av det store antallet vranglåser - vi oppdaterer kun én post om gangen.
  • Vi utfører alltid spørringer kun på indekser - en spørring med en full tabellskanningsplan for oss betyr overbelastning av databasen og få den til å mislykkes.

Disse trinnene tillot oss å presse nesten maksimal ytelse ut av SQL-servere. Problemene ble imidlertid flere og flere. La oss se på dem.

Problemer med SQL

  • Siden vi brukte selvskrevet sharding, ble det lagt til nye shards manuelt av administratorer. Hele denne tiden var ikke skalerbare datareplikaer tjenesteforespørsler.
  • Etter hvert som antallet poster i tabellen vokser, synker hastigheten på innsetting og modifikasjon; når du legger til indekser til en eksisterende tabell, synker hastigheten med en faktor; opprettelse og gjenoppretting av indekser skjer med nedetid.
  • Å ha en liten mengde Windows for SQL Server i produksjon gjør infrastrukturadministrasjon vanskelig

Men hovedproblemet er

feiltoleranse

Den klassiske SQL-serveren har dårlig feiltoleranse. La oss si at du bare har én databaseserver, og den svikter en gang hvert tredje år. I løpet av denne tiden er siden nede i 20 minutter, noe som er akseptabelt. Hvis du har 64 servere, er siden nede en gang hver tredje uke. Og hvis du har 200 servere, fungerer ikke siden hver uke. Dette er et problem.

Hva kan gjøres for å forbedre feiltoleransen til en SQL-server? Wikipedia inviterer oss til å bygge svært tilgjengelig klynge: der i tilfelle feil på noen av komponentene er det en sikkerhetskopi.

Dette krever en flåte av dyrt utstyr: tallrike dupliseringer, optisk fiber, delt lagring og inkludering av en reserve fungerer ikke pålitelig: omtrent 10 % av vekslingene ender med svikt i backupnoden som et tog bak hovednoden.

Men den største ulempen med en slik høy tilgjengelig klynge er null tilgjengelighet hvis datasenteret den er plassert i svikter. Odnoklassniki har fire datasentre, og vi må sørge for drift i tilfelle fullstendig feil i ett av dem.

Til dette kunne vi bruke Multi-Master replikering innebygd i SQL Server. Denne løsningen er mye dyrere på grunn av kostnadene for programvare og lider av velkjente problemer med replikering - uforutsigbare transaksjonsforsinkelser med synkron replikering og forsinkelser i påføring av replikasjoner (og som et resultat tapte modifikasjoner) med asynkron replikering. Det underforståtte manuell konfliktløsning gjør dette alternativet helt ubrukelig for oss.

Alle disse problemene krevde en radikal løsning, og vi begynte å analysere dem i detalj. Her må vi sette oss inn i hva SQL Server hovedsakelig gjør – transaksjoner.

Enkel transaksjon

La oss vurdere den enkleste transaksjonen, fra synspunktet til en anvendt SQL-programmerer: å legge til et bilde i et album. Album og fotografier er lagret i forskjellige plater. Albumet har en offentlig fototeller. Deretter er en slik transaksjon delt inn i følgende trinn:

  1. Vi låser albumet med nøkkel.
  2. Opprett en oppføring i fototabellen.
  3. Hvis bildet har en offentlig status, legg til en offentlig fototeller i albumet, oppdater posten og foreta transaksjonen.

Eller i 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 vanligste scenariet for en forretningstransaksjon er å lese data fra databasen inn i minnet til applikasjonsserveren, endre noe og lagre de nye verdiene tilbake til databasen. Vanligvis i en slik transaksjon oppdaterer vi flere enheter, flere tabeller.

Når du utfører en transaksjon, kan det forekomme samtidig endring av de samme dataene fra et annet system. For eksempel kan Antispam bestemme at brukeren på en eller annen måte er mistenksom, og derfor skal ikke alle brukerens bilder lenger være offentlige, de må sendes til moderering, noe som betyr at photo.status endres til en annen verdi og de tilsvarende tellerne slås av. Åpenbart, hvis denne operasjonen skjer uten garantier for atomitet ved bruk og isolasjon av konkurrerende modifikasjoner, som i ACID, da blir ikke resultatet det som skal til - enten vil fototelleren vise feil verdi, eller så vil ikke alle bildene bli sendt til moderering.

Mye lignende kode, som manipulerer ulike forretningsenheter i en transaksjon, har blitt skrevet gjennom hele Odnoklassnikis eksistens. Basert på erfaring med migreringer til NoSQL fra Eventuell konsistens Vi vet at den største utfordringen (og tidsinvesteringen) kommer fra å utvikle kode for å opprettholde datakonsistens. Derfor vurderte vi hovedkravet for den nye lagringen til å være tilrettelegging for ekte ACID-transaksjoner for applikasjonslogikk.

Andre, ikke mindre viktige, krav var:

  • Hvis datasenteret svikter, må både lesing og skriving til den nye lagringen være tilgjengelig.
  • Opprettholde gjeldende utviklingshastighet. Det vil si at når du arbeider med et nytt depot, bør mengden kode være omtrent den samme; det skal ikke være behov for å legge til noe i depotet, utvikle algoritmer for å løse konflikter, vedlikeholde sekundære indekser, etc.
  • Hastigheten på den nye lagringen måtte være ganske høy, både ved lesing av data og ved behandling av transaksjoner, noe som effektivt betydde at akademisk strenge, universelle, men langsomme løsninger, som f.eks. to-fase forpliktelser.
  • Automatisk on-the-fly skalering.
  • Bruker vanlige billige servere, uten å måtte kjøpe eksotisk maskinvare.
  • Mulighet for lagringsutvikling av bedriftsutviklere. Med andre ord ble proprietære eller åpen kildekode-løsninger prioritert, fortrinnsvis i Java.

Vedtak, vedtak

Ved å analysere mulige løsninger kom vi frem til to mulige arkitekturvalg:

Den første er å ta en hvilken som helst SQL-server og implementere den nødvendige feiltoleransen, skaleringsmekanismen, failover-klyngen, konfliktløsning og distribuerte, pålitelige og raske ACID-transaksjoner. Vi vurderte dette alternativet som svært lite trivielt og arbeidskrevende.

Det andre alternativet er å ta en ferdig NoSQL-lagring med implementert skalering, en failover-klynge, konfliktløsning og implementere transaksjoner og SQL selv. Ved første øyekast ser selv oppgaven med å implementere SQL, for ikke å snakke om ACID-transaksjoner, ut som en oppgave som vil ta årevis. Men så innså vi at SQL-funksjonssettet vi bruker i praksis er like langt fra ANSI SQL som Cassandra CQL langt fra ANSI SQL. Da vi tok en enda nærmere titt på CQL, innså vi at den var ganske nær det vi trengte.

Cassandra og CQL

Så, hva er interessant med Cassandra, hvilke evner har den?

For det første kan du her lage tabeller som støtter ulike datatyper; du kan gjøre SELECT eller UPDATE på primærnøkkelen.

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

For å sikre konsistens i replikadata, bruker Cassandra quorums tilnærming. I det enkleste tilfellet betyr dette at når tre replikaer av samme rad plasseres på forskjellige noder i klyngen, anses skrivingen som vellykket hvis flertallet av nodene (det vil si to av tre) bekreftet suksessen til denne skriveoperasjonen . Raddataene anses å være konsistente hvis flertallet av nodene ble spurt og bekreftet under lesing. Dermed, med tre replikaer, er fullstendig og umiddelbar datakonsistens garantert hvis en node svikter. Denne tilnærmingen tillot oss å implementere et enda mer pålitelig opplegg: send alltid forespørsler til alle tre replikaene, mens du venter på svar fra de to raskeste. Det sene svaret til den tredje kopien forkastes i dette tilfellet. En node som er sent ute med å svare kan ha alvorlige problemer - bremser, søppelinnsamling i JVM, direkte minnegjenvinning i Linux-kjernen, maskinvarefeil, frakobling fra nettverket. Dette påvirker imidlertid ikke klientens drift eller data på noen måte.

Tilnærmingen når vi kontakter tre noder og får svar fra to kalles spekulasjon: en forespørsel om ekstra kopier sendes allerede før den "faller av".

En annen fordel med Cassandra er Batchlog, en mekanisme som sikrer at en gruppe endringer du gjør, enten blir brukt fullt ut eller ikke i det hele tatt. Dette gjør at vi kan løse A i ACID - atomitet ut av esken.

Det som er nærmest transaksjoner i Cassandra er de såkalte "lette transaksjoner". Men de er langt fra "ekte" ACID-transaksjoner: faktisk er dette en mulighet å gjøre CAS på data fra bare én post, ved å bruke konsensus ved bruk av tungvekts Paxos-protokollen. Derfor er hastigheten på slike transaksjoner lav.

Det vi manglet i Cassandra

Så vi måtte implementere ekte ACID-transaksjoner i Cassandra. Ved å bruke denne kunne vi enkelt implementere to andre praktiske funksjoner i klassisk DBMS: konsekvente raske indekser, som ville tillate oss å utføre datavalg ikke bare med primærnøkkelen, og en vanlig generator av monotone auto-inkrementerende IDer.

Kjegle

Dermed ble et nytt DBMS født Kjegle, bestående av tre typer servernoder:

  • Lagring – (nesten) standard Cassandra-servere som er ansvarlige for å lagre data på lokale disker. Etter hvert som belastningen og volumet av data vokser, kan mengden deres enkelt skaleres til titalls og hundrevis.
  • Transaksjonskoordinatorer - sørge for gjennomføring av transaksjoner.
  • Klienter er applikasjonsservere som implementerer forretningsdrift og initierer transaksjoner. Det kan være tusenvis av slike kunder.

NewSQL = NoSQL+ACID

Servere av alle typer er en del av en felles klynge, bruker den interne Cassandra-meldingsprotokollen for å kommunisere med hverandre og sladder for utveksling av klyngeinformasjon. Med Heartbeat lærer servere om gjensidige feil, vedlikeholder ett enkelt dataskjema - tabeller, deres struktur og replikering; partisjonsskjema, klyngetopologi, etc.

Klienter

NewSQL = NoSQL+ACID

I stedet for standard drivere brukes Fat Client-modus. En slik node lagrer ikke data, men kan fungere som en koordinator for forespørselsutførelse, det vil si at klienten selv fungerer som en koordinator for sine forespørsler: den spør etter lagringsreplikaer og løser konflikter. Dette er ikke bare mer pålitelig og raskere enn standarddriveren, som krever kommunikasjon med en ekstern koordinator, men lar deg også kontrollere overføringen av forespørsler. Utenfor en transaksjon som er åpen på klienten, sendes forespørsler til repositories. Hvis klienten har åpnet en transaksjon, sendes alle forespørsler innenfor transaksjonen til transaksjonskoordinatoren.
NewSQL = NoSQL+ACID

C*One Transaction Coordinator

Koordinatoren er noe vi implementerte for C*One fra bunnen av. Det er ansvarlig for å administrere transaksjoner, låser og rekkefølgen transaksjoner blir brukt i.

For hver betjent transaksjon genererer koordinatoren et tidsstempel: hver påfølgende transaksjon er større enn den forrige transaksjonen. Siden Cassandras konfliktløsningssystem er basert på tidsstempler (av to motstridende poster, den med det siste tidsstemplet regnes som gjeldende), vil konflikten alltid bli løst til fordel for den påfølgende transaksjonen. Dermed implementerte vi Lamport klokke - en billig måte å løse konflikter i et distribuert system.

Låser

For å sikre isolasjon bestemte vi oss for å bruke den enkleste metoden - pessimistiske låser basert på hovednøkkelen til posten. Med andre ord, i en transaksjon må en post først låses, først deretter leses, endres og lagres. Først etter en vellykket forpliktelse kan en post låses opp slik at konkurrerende transaksjoner kan bruke den.

Implementering av slik låsing er enkel i et ikke-distribuert miljø. I et distribuert system er det to hovedalternativer: enten implementere distribuert låsing på klyngen, eller distribuere transaksjoner slik at transaksjoner som involverer samme post alltid betjenes av samme koordinator.

Siden i vårt tilfelle dataene allerede er distribuert mellom grupper av lokale transaksjoner i SQL, ble det besluttet å tildele lokale transaksjonsgrupper til koordinatorer: en koordinator utfører alle transaksjoner med tokens fra 0 til 9, den andre - med tokens fra 10 til 19, og så videre. Som et resultat blir hver av koordinatorforekomstene master for transaksjonsgruppen.

Deretter kan låser implementeres i form av et banalt HashMap i minnet til koordinatoren.

Koordinatorfeil

Siden en koordinator utelukkende betjener en gruppe transaksjoner, er det svært viktig å raskt finne ut om dens feil, slik at det andre forsøket på å utføre transaksjonen vil tidsavbrytes. For å gjøre dette raskt og pålitelig, brukte vi en fullt tilkoblet quorum hearbeat-protokoll:

Hvert datasenter er vert for minst to koordinatornoder. Med jevne mellomrom sender hver koordinator en hjerteslagmelding til de andre koordinatorene og informerer om hvordan den fungerer, samt hvilke hjerteslagmeldinger den mottok fra hvilke koordinatorer i klyngen sist gang.

NewSQL = NoSQL+ACID

Ved å motta lignende informasjon fra andre som en del av deres hjerteslagmeldinger, bestemmer hver koordinator selv hvilke klyngenoder som fungerer og hvilke som ikke fungerer, veiledet av quorum-prinsippet: hvis node X har mottatt informasjon fra flertallet av noder i klyngen om normalen. mottak av meldinger fra node Y, så fungerer Y. Og omvendt, så snart flertallet rapporterer manglende meldinger fra node Y, så har Y nektet. Det er merkelig at hvis quorumet informerer node X om at det ikke lenger mottar meldinger fra det, så vil node X selv anse seg for å ha mislyktes.

Hjerteslagmeldinger sendes med høy frekvens, omtrent 20 ganger per sekund, med en periode på 50 ms. I Java er det vanskelig å garantere applikasjonsrespons innen 50 ms på grunn av den sammenlignbare lengden på pauser forårsaket av søppeloppsamleren. Vi var i stand til å oppnå denne responstiden ved å bruke G1 søppeloppsamleren, som lar oss spesifisere et mål for varigheten av GC-pauser. Noen ganger, ganske sjelden, overskrider imidlertid kollektorpausene 50 ms, noe som kan føre til en falsk feildeteksjon. For å forhindre at dette skjer melder ikke koordinator om svikt i en ekstern node når første hjerteslagmelding fra denne forsvinner, kun hvis flere har forsvunnet på rad Slik klarte vi å oppdage en svikt i koordinatornoden i 200 ms.

Men det er ikke nok å raskt forstå hvilken node som har sluttet å fungere. Dette må vi gjøre noe med.

Reservasjon

Det klassiske opplegget innebærer, ved en mastersvikt, å starte et nyvalg ved bruk av en av fasjonable universell algoritmer. Slike algoritmer har imidlertid velkjente problemer med tidskonvergens og lengden på selve valgprosessen. Vi var i stand til å unngå slike ekstra forsinkelser ved å bruke en koordinatorerstatningsordning i et fullt tilkoblet nettverk:

NewSQL = NoSQL+ACID

La oss si at vi ønsker å utføre en transaksjon i gruppe 50. La oss på forhånd bestemme erstatningsordningen, det vil si hvilke noder som skal utføre transaksjoner i gruppe 50 i tilfelle svikt hos hovedkoordinatoren. Vårt mål er å opprettholde systemfunksjonalitet i tilfelle datasenterfeil. La oss bestemme at den første reserven vil være en node fra et annet datasenter, og den andre reserven vil være en node fra en tredje. Dette skjemaet velges én gang og endres ikke før topologien til klyngen endres, det vil si før nye noder kommer inn i den (noe som skjer svært sjelden). Prosedyren for å velge en ny aktiv master hvis den gamle mislykkes vil alltid være som følger: den første reserven vil bli den aktive masteren, og hvis den har sluttet å fungere, vil den andre reserven bli den aktive masteren.

Denne ordningen er mer pålitelig enn den universelle algoritmen, siden for å aktivere en ny master er det nok å fastslå feilen til den gamle.

Men hvordan vil kundene forstå hvilken master som jobber nå? Det er umulig å sende informasjon til tusenvis av klienter på 50 ms. En situasjon er mulig når en klient sender en forespørsel om å åpne en transaksjon, uten å vite at denne masteren ikke lenger fungerer, og forespørselen vil tidsavbrytes. For å forhindre at dette skjer, sender klienter spekulativt en forespørsel om å åpne en transaksjon til gruppemesteren og begge reservene hans samtidig, men bare den som er den aktive masteren for øyeblikket vil svare på denne forespørselen. Kunden vil gjøre all påfølgende kommunikasjon innenfor transaksjonen kun med den aktive masteren.

Backup-mastere plasserer mottatte forespørsler om transaksjoner som ikke er deres, i køen av ufødte transaksjoner, hvor de lagres i noen tid. Hvis den aktive masteren dør, behandler den nye masteren forespørsler om å åpne transaksjoner fra køen og svarer klienten. Hvis klienten allerede har åpnet en transaksjon med den gamle masteren, ignoreres det andre svaret (og åpenbart vil en slik transaksjon ikke fullføres og gjentas av klienten).

Hvordan transaksjonen fungerer

La oss si at en klient sendte en forespørsel til koordinatoren om å åpne en transaksjon for en slik og en enhet med en slik og en primærnøkkel. Koordinatoren låser denne enheten og plasserer den i låsetabellen i minnet. Om nødvendig leser koordinatoren denne enheten fra lagringen og lagrer de resulterende dataene i en transaksjonstilstand i koordinatorens minne.

NewSQL = NoSQL+ACID

Når en klient ønsker å endre data i en transaksjon, sender den en forespørsel til koordinatoren om å endre enheten, og koordinatoren plasserer de nye dataene i transaksjonsstatustabellen i minnet. Dette fullfører opptaket - ingen opptak gjøres til lageret.

NewSQL = NoSQL+ACID

Når en klient ber om sine egne endrede data som en del av en aktiv transaksjon, handler koordinatoren som følger:

  • hvis ID-en allerede er i transaksjonen, blir dataene tatt fra minnet;
  • hvis ID-en ikke er i minnet, leses de manglende dataene fra lagringsnodene, kombinert med de som allerede er i minnet, og resultatet blir gitt til klienten.

Dermed kan klienten lese sine egne endringer, men andre klienter ser ikke disse endringene, fordi de bare er lagret i minnet til koordinatoren; de er ennå ikke i Cassandra-nodene.

NewSQL = NoSQL+ACID

Når klienten sender commit, lagres tilstanden som var i tjenestens minne av koordinatoren i en logget batch, og sendes som en logget batch til Cassandra-lageret. Butikkene gjør alt som er nødvendig for å sikre at denne pakken er atomært (fullstendig) brukt, og returnerer et svar til koordinatoren, som frigjør låsene og bekrefter suksessen til transaksjonen til kunden.

NewSQL = NoSQL+ACID

Og for å rulle tilbake, trenger koordinatoren bare å frigjøre minnet som er okkupert av transaksjonstilstanden.

Som et resultat av forbedringene ovenfor implementerte vi ACID-prinsippene:

  • Atomitet. Dette er en garanti for at ingen transaksjon vil bli delvis registrert i systemet; enten vil alle dets underoperasjoner bli fullført, eller ingen vil bli fullført. Vi følger dette prinsippet gjennom logget batch i Cassandra.
  • Konsistens. Hver vellykket transaksjon, per definisjon, registrerer kun gyldige resultater. Hvis det etter å ha åpnet en transaksjon og utført deler av operasjonene oppdages at resultatet er ugyldig, utføres en tilbakerulling.
  • Isolering. Når en transaksjon utføres, bør ikke samtidige transaksjoner påvirke resultatet. Konkurrerende transaksjoner isoleres ved hjelp av pessimistiske låser på koordinatoren. For avlesninger utenfor en transaksjon følges isolasjonsprinsippet på Read Committed-nivået.
  • Stabilitet. Uavhengig av problemer på lavere nivåer – systemavbrudd, maskinvarefeil – bør endringer som er gjort etter en vellykket gjennomført transaksjon forbli bevart når driften gjenopptas.

Lesing etter indekser

La oss ta en enkel tabell:

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

Den har en ID (primærnøkkel), eier og endringsdato. Du må gjøre en veldig enkel forespørsel - velg data om eieren med endringsdatoen "for siste dag".

SELECT *
WHERE owner=?
AND modified>?

For at en slik spørring skal behandles raskt, må du i en klassisk SQL DBMS bygge en indeks etter kolonner (eier, modifisert). Vi kan gjøre dette ganske enkelt, siden vi nå har SYRE-garantier!

Indekser i C*One

Det er en kildetabell med fotografier der post-IDen er primærnøkkelen.

NewSQL = NoSQL+ACID

For en indeks oppretter C*One en ny tabell som er en kopi av originalen. Nøkkelen er den samme som indeksuttrykket, og den inkluderer også primærnøkkelen til posten fra kildetabellen:

NewSQL = NoSQL+ACID

Nå kan søket etter "eier for siste dag" skrives om som et utvalg fra en annen tabell:

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

Konsistensen av dataene i kildetabellbildene og indekstabellen i1 opprettholdes automatisk av koordinatoren. Basert på dataskjemaet alene, når en endring mottas, genererer og lagrer koordinatoren en endring ikke bare i hovedtabellen, men også i kopier. Ingen ekstra handlinger utføres på indekstabellen, logger leses ikke, og ingen låser brukes. Det vil si at å legge til indekser bruker nesten ingen ressurser og har praktisk talt ingen effekt på hastigheten på å bruke modifikasjoner.

Ved å bruke ACID var vi i stand til å implementere SQL-lignende indekser. De er konsistente, skalerbare, raske, komponerbare og innebygd i CQL-spørringsspråket. Ingen endringer i applikasjonskoden er nødvendig for å støtte indekser. Alt er like enkelt som i SQL. Og viktigst av alt, indekser påvirker ikke utførelseshastigheten til endringer i den opprinnelige transaksjonstabellen.

Hva skjedde

Vi utviklet C*One for tre år siden og lanserte den i kommersiell drift.

Hva fikk vi til slutt? La oss evaluere dette ved å bruke eksemplet med bildebehandlings- og lagringsundersystemet, en av de viktigste typene data i et sosialt nettverk. Vi snakker ikke om kroppene til fotografiene i seg selv, men om all slags metainformasjon. Nå har Odnoklassniki omtrent 20 milliarder slike poster, systemet behandler 80 tusen leseforespørsler per sekund, opptil 8 tusen ACID-transaksjoner per sekund assosiert med datamodifisering.

Da vi brukte SQL med replikeringsfaktor = 1 (men i RAID 10), ble fotometainformasjonen lagret på en svært tilgjengelig klynge med 32 maskiner som kjørte Microsoft SQL Server (pluss 11 sikkerhetskopier). 10 servere ble også tildelt for lagring av sikkerhetskopier. Totalt 50 dyre biler. Samtidig opererte systemet med nominell belastning, uten reserve.

Etter migrering til det nye systemet fikk vi replikeringsfaktor = 3 - en kopi i hvert datasenter. Systemet består av 63 Cassandra lagringsnoder og 6 koordinatormaskiner, for totalt 69 servere. Men disse maskinene er mye billigere, deres totale kostnad er omtrent 30% av kostnadene for et SQL-system. Samtidig holdes belastningen på 30 %.

Med introduksjonen av C*One ble ventetiden også redusert: i SQL tok en skriveoperasjon omtrent 4,5 ms. I C*One - ca. 1,6 ms. Transaksjonsvarigheten er i gjennomsnitt mindre enn 40 ms, forpliktelsen er fullført på 2 ms, lese- og skrivevarigheten er i gjennomsnitt 2 ms. 99. persentil - bare 3-3,1 ms, antall timeouts har gått ned med 100 ganger - alt på grunn av den utbredte bruken av spekulasjoner.

Nå er de fleste av SQL Server-nodene tatt ut av drift; nye produkter utvikles kun ved bruk av C*One. Vi tilpasset C*One til å fungere i skyen vår én-sky, som gjorde det mulig å fremskynde utrullingen av nye klynger, forenkle konfigurasjonen og automatisere driften. Uten kildekoden ville det vært mye vanskeligere og mer tungvint å gjøre dette.

Nå jobber vi med å overføre våre andre lagringsfasiliteter til skyen – men det er en helt annen historie.

Kilde: www.habr.com

Legg til en kommentar