NewSQL = NoSQL+ACID

NewSQL = NoSQL+ACID
Do nedavno je u Odnoklassniki oko 50 TB podataka obrađenih u realnom vremenu bilo pohranjeno u SQL Server. Za takav volumen, gotovo je nemoguće osigurati brz i pouzdan, pa čak i tolerantan na greške pristup podatkovnom centru koristeći SQL DBMS. Obično se u takvim slučajevima koristi jedno od NoSQL skladišta, ali ne može se sve prenijeti u NoSQL: neki entiteti zahtijevaju ACID transakcijske garancije.

To nas je dovelo do upotrebe NewSQL skladišta, odnosno DBMS-a koji obezbeđuje toleranciju grešaka, skalabilnost i performanse NoSQL sistema, ali u isto vreme zadržava ACID garancije poznate klasičnim sistemima. Radnih industrijskih sistema ove nove klase je malo, pa smo takav sistem sami implementirali i pustili u komercijalni rad.

Kako to funkcioniše i šta se dogodilo - pročitajte ispod reza.

Danas je mjesečna publika Odnoklassnika više od 70 miliona jedinstvenih posjetilaca. Mi uđite u prvih pet najveće društvene mreže na svijetu, te u prvih dvadeset stranica na kojima korisnici provode najviše vremena. „OK“ infrastruktura podnosi veoma velika opterećenja: preko milion HTTP zahteva/sek po fronti. Dijelovi serverskog parka u količini od više od 8000 komada nalaze se blizu jedan drugom - u četiri moskovska data centra, što omogućava da se između njih obezbijedi kašnjenje mreže manje od 1 ms.

Cassandru koristimo od 2010. godine, počevši od verzije 0.6. Danas je u funkciji nekoliko desetina klastera. Najbrži klaster obrađuje preko 4 miliona operacija u sekundi, dok najveći skladišti 260 TB.

Međutim, ovo su sve obični NoSQL klasteri koji se koriste za pohranjivanje slabo koordinisane podaci. Takođe smo želeli da zamenimo glavno konzistentno skladište, Microsoft SQL Server, koji se koristi od osnivanja Odnoklassniki. Skladište se sastojalo od više od 300 SQL Server Standard Edition mašina, koje su sadržavale 50 TB podataka – poslovnih subjekata. Ovi podaci se mijenjaju kao dio ACID transakcija i zahtijevaju visoka konzistencija.

Za distribuciju podataka kroz čvorove SQL Servera koristili smo i vertikalne i horizontalne particioniranje (sharding). Istorijski gledano, koristili smo jednostavnu šemu dijeljenja podataka: svaki entitet je bio povezan s tokenom – funkcijom ID-a entiteta. Entiteti sa istim tokenom postavljeni su na isti SQL server. Odnos master-detail implementiran je na način da se tokeni glavnog i podređenog zapisa uvijek podudaraju i nalaze se na istom serveru. U društvenoj mreži skoro svi zapisi se generišu u ime korisnika, što znači da se svi korisnički podaci unutar jednog funkcionalnog podsistema pohranjuju na jednom serveru. Odnosno, poslovna transakcija je gotovo uvijek uključivala tablice jednog SQL servera, što je omogućilo da se osigura konzistentnost podataka korištenjem lokalnih ACID transakcija, bez potrebe za korištenjem spor i nepouzdan distribuirane ACID transakcije.

Zahvaljujući dijeljenju i ubrzanju SQL-a:

  • Ne koristimo ograničenja stranog ključa, jer prilikom dijeljenja, ID entiteta može biti lociran na drugom serveru.
  • Ne koristimo pohranjene procedure i okidače zbog dodatnog opterećenja na DBMS CPU-u.
  • Ne koristimo JOIN-ove zbog svega gore navedenog i mnogo nasumičnih čitanja s diska.
  • Izvan transakcije, koristimo nivo izolacije Read Uncommitted da smanjimo zastoje.
  • Izvršavamo samo kratke transakcije (manje od 100ms u prosjeku).
  • Ne koristimo više redova UPDATE i DELETE zbog velikog broja zastoja - ažuriramo samo jedan zapis u isto vrijeme.
  • Upite uvijek izvršavaju samo indeksi - upit s punim planom skeniranja tablice znači za nas preopterećenje baze podataka i njen neuspjeh.

Ovi koraci su nam omogućili da iz SQL servera izvučemo gotovo maksimalnu performansu. Međutim, problemi su postajali sve veći. Hajde da ih pogledamo.

Problemi sa SQL-om

  • S obzirom da smo koristili samostalno pisano šardiranje, dodavanje novih dijelova je ručno radili administratori. Sve ovo vrijeme, skalabilne replike podataka nisu opsluživale zahtjeve.
  • Kako broj zapisa u tabeli raste, brzina umetanja i modifikacije se smanjuje, pri dodavanju indeksa postojećoj tabeli, brzina se višestruko smanjuje, kreiranje i ponovno kreiranje indeksa oduzima vreme.
  • Imati malu količinu Windows-a za SQL Server u proizvodnji otežava upravljanje infrastrukturom

Ali glavni problem je

tolerancije grešaka

Klasični SQL Server ima lošu toleranciju grešaka. Recimo da imate samo jedan server baze podataka i on otkaže svake tri godine. U ovom trenutku, stranica ne radi 20 minuta, to je prihvatljivo. Ako imate 64 servera, onda je stranica isključena jednom u tri sedmice. A ako imate 200 servera, onda stranica ne radi svake sedmice. Ovo je problem.

Šta se može učiniti da se poboljša tolerancija grešaka SQL servera? Wikipedia nas poziva da gradimo visoko dostupan klaster: gdje u slučaju kvara bilo koje komponente postoji rezervna kopija.

Za to je potrebna flota skupe opreme: brojna umnožavanja, optička vlakna, zajednička pohrana, a uključivanje rezerve ne radi pouzdano: oko 10% uključivanja završava kvarom rezervnog čvora sa vlakom iza glavnog čvora.

Ali glavni nedostatak tako visoko dostupnog klastera je nula dostupnost u slučaju kvara podatkovnog centra u kojem se nalazi. Odnoklassniki ima četiri data centra i moramo osigurati rad u jednom od njih u slučaju potpunog kvara.

Za ovo bi se moglo koristiti Multi Master replikacija ugrađena u SQL Server. Ovo rješenje je mnogo skuplje zbog cijene softvera i pati od dobro poznatih problema replikacije - nepredvidivih kašnjenja transakcija kod sinhrone replikacije i kašnjenja u primjeni replikacije (i, kao rezultat, izgubljenih modifikacija) kod asinhrone. implicirano ručno rješavanje sukoba čini ovu opciju potpuno neprimjenjivom za nas.

Svi ovi problemi zahtijevali su kardinalno rješenje i prešli smo na njihovu detaljnu analizu. Ovdje se trebamo upoznati sa onim što SQL Server u osnovi radi - transakcijama.

Jednostavna transakcija

Razmotrite najjednostavniju transakciju sa stanovišta primijenjenog SQL programera: dodavanje fotografije u album. Albumi i fotografije pohranjeni su u različitim pločama. Album ima javni brojač fotografija. Zatim se takva transakcija dijeli na sljedeće korake:

  1. Blokiramo album po ključu.
  2. Napravite unos u tabeli sa fotografijama.
  3. Ako fotografija ima javni status, tada namotavamo brojač javnih fotografija u album, ažuriramo zapis i izvršavamo transakciju.

Ili u pseudokodu:

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 najčešći scenario za poslovnu transakciju čitanje podataka iz baze podataka u memoriju poslužitelja aplikacija, promjena nečega i pohranjivanje novih vrijednosti natrag u bazu podataka. Obično u takvoj transakciji ažuriramo nekoliko entiteta, nekoliko tabela.

Kada se transakcija izvrši, može doći do istovremene modifikacije istih podataka iz drugog sistema. Na primjer, Antispam može odlučiti da je korisnik na neki način sumnjiv i stoga sve fotografije korisnika više ne bi trebale biti javne, potrebno ih je poslati na moderaciju, što podrazumijeva promjenu photo.statusa na neku drugu vrijednost i isključivanje odgovarajućih brojača. Očigledno, ako će se ova operacija dogoditi bez garancija atomičnosti primjene i izolacije konkurentskih modifikacija, kao u ACID, tada rezultat neće biti ono što vam treba - ili će brojač fotografija pokazati pogrešnu vrijednost ili neće sve fotografije biti poslane na moderiranje.

Mnogo takvog koda koji manipuliše različitim poslovnim subjektima u okviru jedne transakcije napisano je tokom postojanja Odnoklassniki. Prema iskustvu migracija na NoSQL sa Dosljednost događaja znamo da je najveći izazov (i dugotrajan) potreba za razvojem koda za održavanje konzistentnosti podataka. Stoga smo smatrali da je glavni zahtjev za novu memoriju osiguravanje aplikativne logike stvarnih ACID transakcija.

Drugi jednako važni zahtjevi bili su:

  • U slučaju kvara podatkovnog centra, mora biti dostupno i čitanje i pisanje u novu memoriju.
  • Održavanje trenutne brzine razvoja. Odnosno, kada radite s novim spremištem, količina koda bi trebala biti približno ista, ne bi trebalo biti potrebe da se nešto dodaje u spremište, razvijaju algoritmi za rješavanje sukoba, održavanje sekundarnih indeksa itd.
  • Brzina novog skladišta trebala bi biti dovoljno brza, kako pri čitanju podataka tako i pri obradi transakcija, što je zapravo značilo da akademski rigorozna, općenamjenska, ali spora rješenja, kao npr. dvofazna urezivanja.
  • Automatsko skaliranje u hodu.
  • Koristeći obične jeftine servere, bez potrebe za kupovinom egzotičnih komada željeza.
  • Mogućnost razvoja skladišta od strane programera kompanije. Drugim riječima, prioritet je dat vlasničkim ili otvorenim rješenjima, po mogućnosti u Javi.

Odluke, odluke

Analizirajući moguća rješenja, došli smo do dva moguća izbora arhitekture:

Prvi je da uzmete bilo koji SQL server i implementirate potrebnu toleranciju grešaka, mehanizam skaliranja, grupisanje za nadilaženje greške, rješavanje sukoba i distribuirane, pouzdane i brze ACID transakcije. Ovu opciju smo ocijenili kao vrlo netrivijalnu i dugotrajnu.

Druga opcija je da uzmete gotovu NoSQL memoriju sa implementiranim skaliranjem, klasterizacijom preko greške, rješavanjem sukoba i sami implementirajte transakcije i SQL. Na prvi pogled, čak i zadatak implementacije SQL-a, a da ne spominjemo ACID transakcije, izgleda kao zadatak godinama. Ali tada smo shvatili da je skup SQL karakteristika koje koristimo u praksi daleko od ANSI SQL-a Cassandra CQL daleko od ANSI SQL-a. Gledajući bliže CQL, shvatili smo da je dovoljno blizu onome što nam je potrebno.

Cassandra i CQL

Dakle, šta je zanimljivo kod Cassandre, koje karakteristike ima?

Prvo, ovdje možete kreirati tabele s podrškom za različite tipove podataka, možete napraviti SELECT ili UPDATE pomoću primarnog ključa.

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

Kako bi osigurala konzistentnost podataka replike, Cassandra koristi pristup kvoruma. U najjednostavnijem slučaju, to znači da se prilikom postavljanja tri replike istog reda na različite čvorove klastera upisivanje smatra uspješnim ako je većina čvorova (tj. dva od tri) potvrdila uspjeh ove operacije pisanja. Podaci serije se smatraju konzistentnim ako je, prilikom čitanja, većina čvorova ispitana i potvrđena. Dakle, ako postoje tri replike, potpuna i trenutna konzistentnost podataka je zagarantovana ako jedan čvor pokvari. Ovaj pristup nam je omogućio da implementiramo još pouzdaniju šemu: uvijek šaljite zahtjeve na sve tri replike, čekajući odgovor od dvije najbrže. Kasni odgovor treće replike se tada odbacuje. U isto vrijeme, čvor koji kasni s odgovorom može imati ozbiljne probleme - kočnice, sakupljanje smeća u JVM-u, direktno vraćanje memorije u jezgru linuxa, kvar hardvera, prekid veze s mrežom. Međutim, ni na koji način to ne utiče na rad i podatke klijenta.

Pristup kada pristupamo trima čvorovima i dobijemo odgovor od dva se zove spekulacije: zahtjev za dodatnim replikama se šalje prije nego što "otpadne".

Još jedna prednost Cassandre je Batchlog, mehanizam koji osigurava da se promjene koje napravite u potpunosti ili ne primjenjuju u potpunosti na paket. Ovo nam omogućava da iz kutije riješimo A u ACID - atomičnost.

Najbliža stvar transakcijama u Cassandri je tzv.lake transakcije". Ali one su daleko od “pravih” ACID transakcija: u stvari, ovo je prilika za ostvarivanje CAS na podacima samo jednog zapisa, koristeći konsenzus Paxos teškog protokola. Stoga je brzina takvih transakcija niska.

Ono što smo propustili u Cassandri

Dakle, morali smo da implementiramo prave ACID transakcije u Cassandri. Uz pomoć kojih bismo lako mogli implementirati još dvije zgodne karakteristike klasičnog DBMS-a: konzistentne brze indekse, koji bi nam omogućili odabir podataka ne samo po primarnom ključu, i uobičajeni generator monotonih ID-ova za automatsko povećanje.

Kornet

Tako je rođen novi DBMS Kornet, koji se sastoji od tri tipa serverskih čvorova:

  • Skladišta su (skoro) standardni Cassandra serveri odgovorni za skladištenje podataka na lokalnim diskovima. Kako raste opterećenje i obim podataka, njihov se broj lako može povećati na desetine i stotine.
  • Koordinatori transakcija - osiguravaju izvršenje transakcija.
  • Klijenti su aplikacijski serveri koji implementiraju poslovne operacije i pokreću transakcije. Takvih klijenata može biti na hiljade.

NewSQL = NoSQL+ACID

Serveri svih tipova su u zajedničkom klasteru, koriste Kasandrin interni protokol za poruke da komuniciraju jedni s drugima i trač za razmjenu informacija klastera. Uz pomoć Heartbeat-a, serveri uče o međusobnim kvarovima, održavaju jedinstvenu šemu podataka – tabele, njihovu strukturu i replikaciju; shema particioniranja, topologija klastera, itd.

Klijenti

NewSQL = NoSQL+ACID

Umjesto standardnih drajvera, koristi se način Fat Client. Takav čvor ne pohranjuje podatke, ali može djelovati kao koordinator izvršavanja zahtjeva, odnosno sam Klijent djeluje kao koordinator svojih zahtjeva: proziva replike skladišta i rješava konflikte. Ovo ne samo da je pouzdanije i brže od standardnog drajvera, koji zahtijeva komunikaciju s udaljenim koordinatorom, već vam omogućava i kontrolu prijenosa zahtjeva. Izvan transakcije otvorene na klijentu, zahtjevi se šalju u skladišta. Ako je klijent otvorio transakciju, tada se svi zahtjevi unutar transakcije šalju koordinatoru transakcije.
NewSQL = NoSQL+ACID

C*One Transaction Coordinator

Koordinator je ono što smo implementirali za C*One od nule. Odgovoran je za upravljanje transakcijama, zaključavanjem i redoslijedom po kojem se transakcije primjenjuju.

Za svaku servisiranu transakciju, koordinator generiše vremensku oznaku: svaka naredna je veća od one prethodne transakcije. Pošto je sistem za rešavanje sukoba u Cassandri zasnovan na vremenskim oznakama (od dva konfliktna zapisa, poslednja vremenska oznaka se smatra relevantnom), konflikt će uvek biti rešen u korist naredne transakcije. Tako smo implementirali lampport sat je jeftin način za rješavanje sukoba u distribuiranom sistemu.

Brave

Kako bismo osigurali izolaciju, odlučili smo koristiti najlakši način - pesimističku bravu na primarnom ključu zapisa. Drugim riječima, u transakciji, zapis prvo mora biti zaključan, tek onda pročitan, izmijenjen i sačuvan. Tek nakon uspješnog urezivanja, zapis se može otključati tako da ga konkurentske transakcije mogu koristiti.

Implementacija takve brave je jednostavna u nedistribuiranom okruženju. U distribuiranom sistemu, postoje dva glavna načina: ili implementirati distribuirano zaključavanje na klaster, ili distribuirati transakcije tako da transakcije koje uključuju isti zapis uvijek servisira isti koordinator.

Budući da su u našem slučaju podaci već raspoređeni po grupama lokalnih transakcija u SQL-u, odlučeno je da se koordinatorima dodijele grupe lokalnih transakcija: jedan koordinator obavlja sve transakcije s tokenom od 0 do 9, drugi - sa tokenom od 10 do 19, i tako dalje. Kao rezultat, svaka od instanci koordinatora postaje gospodar grupe transakcija.

Tada se zaključavanja mogu implementirati kao banalni HashMap u memoriji koordinatora.

Odbijanja koordinatora

Budući da jedan koordinator isključivo opslužuje grupu transakcija, vrlo je važno brzo utvrditi činjenicu njenog neuspjeha kako bi ponovljeni pokušaj izvršenja transakcije bio unutar vremenskog ograničenja. Da bismo ovo učinili brzim i pouzdanim, koristili smo potpuno isprepleteni kvorum Heartbeat protokol:

Svaki centar podataka ima najmanje dva koordinatorska čvora. Periodično, svaki koordinator šalje poruku otkucaja srca ostalim koordinatorima i obavještava ih o svom funkcionisanju, kao i o tome kada je zadnji put primio poruke otkucaja srca od kojih koordinatora u klasteru.

NewSQL = NoSQL+ACID

Primajući slične informacije od ostalih kao dio svojih srčanih poruka, svaki koordinator za sebe odlučuje koji čvorovi klastera funkcionišu, a koji ne, vodeći se principom kvoruma: ako je čvor X primio informaciju od većine čvorova u klasteru o normalno primanje poruka od čvora Y, zatim , Y radi. Suprotno tome, čim većina prijavi nedostajuće poruke sa čvora Y, onda Y nije uspio. Zanimljivo je da ako kvorum kaže čvoru X da više ne prima poruke od njega, onda će sam čvor X sebe smatrati neuspjelim.

Poruke otkucaja srca šalju se visokom frekvencijom, oko 20 puta u sekundi, sa periodom od 50 ms. U Javi je teško garantirati odgovor aplikacije unutar 50 ms zbog uporedivih vremena pauze uzrokovanih sakupljačem smeća. Uspjeli smo postići ovo vrijeme odgovora pomoću G1 sakupljača smeća, koji vam omogućava da odredite cilj za vrijeme trajanja GC pauza. Međutim, ponekad, prilično rijetko, pauze kolektora prelaze 50 ms, što može dovesti do lažne detekcije kvara. Da bi se to izbjeglo, koordinator ne prijavljuje kvar udaljenog čvora kada se izgubi prva poruka otkucaja srca s njega, samo ako nedostaje nekoliko u nizu, tako da smo uspjeli otkriti kvar koordinacionog čvora za 200 ms.

Ali nije dovoljno brzo shvatiti koji je čvor prestao funkcionirati. Nešto treba učiniti u vezi s tim.

Rezervacija

Klasična shema pretpostavlja da u slučaju neuspjeha gospodara da pokrene izbor novog koristeći jedan od moderan univerzalna algoritmi. Međutim, takvi algoritmi imaju dobro poznate probleme sa konvergencijom u vremenu i trajanjem samog izbornog procesa. Uspjeli smo izbjeći takva dodatna kašnjenja koristeći šemu zamjene koordinatora u potpuno povezanoj mreži:

NewSQL = NoSQL+ACID

Recimo da želimo da izvršimo transakciju u grupi 50. Hajde da unapred definišemo šemu zamene, odnosno koji čvorovi će izvršiti transakcije grupe 50 u slučaju kvara glavnog koordinatora. Naš cilj je održati sistem u funkciji u slučaju kvara podatkovnog centra. Definirajmo da će prva rezerva biti čvor iz drugog data centra, a druga rezerva će biti čvor iz trećeg. Ova šema se bira jednom i ne menja se sve dok se topologija klastera ne promeni, odnosno dok u nju ne uđu novi čvorovi (što se dešava veoma retko). Redoslijed odabira novog aktivnog mastera u slučaju kvara starog uvijek će biti sljedeći: prva rezerva će postati aktivni master, a ako je prestala s radom, druga rezerva će postati.

Takva shema je pouzdanija od univerzalnog algoritma, jer je za aktiviranje novog mastera dovoljno utvrditi činjenicu kvara starog.

Ali kako će kupci razumjeti koji od majstora trenutno radi? Nemoguće je poslati informaciju hiljadama klijenata za 50 ms. Moguće je da klijent pošalje zahtjev za otvaranje transakcije, a da još ne zna da ovaj master više ne funkcioniše, a zahtjev će visi na tajm-autu. Da se to ne bi dogodilo, klijenti špekulativno šalju zahtjev za otvaranje transakcije masteru grupe i obje njene rezerve odjednom, ali će na taj zahtjev odgovoriti samo onaj koji je trenutno aktivni master. Svu naknadnu komunikaciju unutar transakcije klijent će obavljati samo sa aktivnim masterom.

Primljene zahtjeve za transakcije koje nisu njihove, standby master stavljaju u red nerođenih transakcija, gdje se pohranjuju neko vrijeme. Ako aktivni master umre, novi master obrađuje zahtjeve za otvaranje transakcija iz svog reda i odgovara klijentu. Ako je klijent već uspio otvoriti transakciju sa starim masterom, onda se drugi odgovor zanemaruje (i, očito, takva transakcija se neće dovršiti i klijent će je ponoviti).

Kako transakcija funkcionira

Pretpostavimo da je klijent poslao zahtjev koordinatoru da otvori transakciju za taj i takav entitet sa tim i takvim primarnim ključem. Koordinator zaključava ovaj entitet i stavlja ga u tabelu zaključavanja u memoriji. Ako je potrebno, koordinator čita ovaj entitet iz skladišta i sprema primljene podatke u transakcijsko stanje u memoriji koordinatora.

NewSQL = NoSQL+ACID

Kada klijent želi promijeniti podatke u transakciji, on šalje zahtjev koordinatoru da modificira entitet, a koordinator stavlja nove podatke u tablicu stanja transakcije u memoriju. Ovim je snimanje završeno - u memoriju se ne upisuje.

NewSQL = NoSQL+ACID

Kada klijent zatraži svoje modificirane podatke kao dio aktivne transakcije, koordinator djeluje na sljedeći način:

  • ako je ID već u transakciji, tada se podaci preuzimaju iz memorije;
  • ako u memoriji nema ID-a, tada se podaci koji nedostaju čitaju iz skladišnih čvorova, kombiniraju s onima koji su već u memoriji, a rezultat se vraća klijentu.

Tako klijent može čitati svoje promjene, a drugi klijenti te promjene ne vide, jer su pohranjene samo u memoriji koordinatora, još nisu u Cassandra čvorovima.

NewSQL = NoSQL+ACID

Kada klijent pošalje urezivanje, stanje koje je usluga imala u memoriji pohranjuje koordinator u evidentiranom paketu i šalje u Cassandra skladišta kao evidentirani paket. Repozitorijumi rade sve što je potrebno da se ovaj paket atomski (potpuno) primijeni i vraćaju odgovor koordinatoru, koji otpušta zaključavanje i potvrđuje uspjeh transakcije klijentu.

NewSQL = NoSQL+ACID

A za vraćanje, koordinator treba samo da oslobodi memoriju koju zauzima stanje transakcije.

Kao rezultat gore opisanih poboljšanja, implementirali smo principe ACID-a:

  • Atomičnost. Ovo je garancija da nijedna transakcija neće biti djelimično fiksirana u sistemu, ili će se izvršiti sve njegove podoperacije ili nijedna od njih neće biti izvršena. U našem slučaju, ovaj princip se poštuje zbog evidentirane serije u Cassandri.
  • Dosljednost. Svaka uspješna transakcija, po definiciji, obavezuje samo valjane rezultate. Ako se nakon otvaranja transakcije i izvođenja neke od operacija utvrdi da je rezultat nevažeći, vrši se vraćanje.
  • izolacija. Kada se transakcija izvrši, paralelne transakcije ne bi trebale uticati na njen rezultat. Istovremene transakcije su izolovane pesimističkim zaključavanjem na koordinatoru. Za čitanja izvan transakcije, poštuje se princip izolacije na nivou Read Committed.
  • Održivost. Bez obzira na probleme na nižim nivoima - zamračenje sistema, kvar hardvera - promjene izvršene uspješno obavljenom transakcijom trebale bi ostati sačuvane nakon nastavka funkcionisanja.

Čitanje po indeksima

Uzmimo jednostavnu tabelu:

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

Ima ID (primarni ključ), vlasnika i datum izmjene. Potrebno je napraviti vrlo jednostavan zahtjev - odabrati podatke o vlasniku sa datumom promjene "za zadnji dan".

SELECT *
WHERE owner=?
AND modified>?

Da biste brzo obrađivali takav upit, u klasičnom SQL DBMS-u morate izgraditi indeks na kolonama (vlasnik, modificirani). To možemo učiniti vrlo jednostavno, jer sada imamo ACID garancije!

Indeksi u C*Oneu

Postoji početna tabela sa fotografijama u kojoj je ID zapisa primarni ključ.

NewSQL = NoSQL+ACID

Za indeks, C*One kreira novu tabelu koja je kopija originalne tabele. Ključ je isti kao indeksni izraz, ali uključuje i primarni ključ zapisa iz izvorne tablice:

NewSQL = NoSQL+ACID

Sada se upit za "vlasnik u posljednja XNUMX sata" može prepisati kao odabir iz druge tabele:

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

Koordinator automatski održava konzistentnost podataka između fotografija izvorne tabele i indeksa i1. Samo na osnovu šeme podataka, kada se primi promjena, koordinator generiše i pamti promjenu ne samo glavne tabele, već i promjene kopija. Ne izvode se nikakve dodatne radnje s indeksnom tablicom, zapisnici se ne čitaju, zaključavanja se ne koriste. Odnosno, dodavanje indeksa gotovo ne troši resurse i praktički ne utječe na brzinu primjene izmjena.

Uz pomoć ACID-a uspjeli smo implementirati indekse "kao u SQL-u". Oni su konzistentni, skalabilni, brzi, komponovani i ugrađeni u CQL jezik upita. Indeksna podrška ne zahtijeva nikakve promjene koda aplikacije. Sve je jednostavno, kao u SQL-u. I što je najvažnije, indeksi ne utiču na brzinu izvršenja modifikacija originalne tabele transakcija.

Šta se desilo

C*One smo razvili prije tri godine i pustili ga u komercijalnu upotrebu.

Šta smo završili? Procijenimo to na primjeru podsistema za obradu i pohranjivanje fotografija, jednog od najvažnijih tipova podataka na društvenoj mreži. Ne radi se o tijelima samih fotografija, već o svim vrstama meta-informacija. Sada u Odnoklassniki postoji oko 20 milijardi takvih zapisa, sistem obrađuje 80 hiljada zahteva za čitanje u sekundi, do 8 hiljada ACID transakcija u sekundi u vezi sa modifikacijom podataka.

Kada smo koristili SQL sa faktorom replikacije = 1 (ali u RAID-u 10), meta-informacije o fotografiji su pohranjene na visoko dostupnom klasteru od 32 Microsoft SQL Server mašine (plus 11 rezervnih). Takođe, izdvojeno je 10 servera za čuvanje rezervnih kopija. Ukupno 50 skupih automobila. Istovremeno, sistem je radio pri nazivnom opterećenju, bez margine.

Nakon migracije na novi sistem, dobili smo faktor replikacije = 3 - kopiju u svakom data centru. Sistem se sastoji od 63 Cassandra čvora za skladištenje i 6 koordinacionih mašina, za ukupno 69 servera. Ali ove mašine su mnogo jeftinije, ukupno oko 30% cene SQL sistema. U ovom slučaju, opterećenje se održava na nivou od 30%.

Sa uvođenjem C*One, latencija se također smanjila: u SQL-u je operacija pisanja trajala oko 4,5 ms. U C * One - oko 1,6 ms. Trajanje transakcije je u prosjeku manje od 40 ms, urezivanje se završava za 2 ms, trajanje čitanja i pisanja je u prosjeku 2 ms. 99. percentil je samo 3-3,1 ms, broj tajm-auta je smanjen za 100 puta - sve zbog široko rasprostranjene upotrebe spekulacija.

Do danas je većina SQL Server čvorova povučena, novi proizvodi se razvijaju samo pomoću C * One. Prilagodili smo C*One za rad u našem oblaku jedan oblak, što je omogućilo ubrzanje implementacije novih klastera, pojednostavljenje konfiguracije i automatizaciju rada. Bez izvornog koda, ovo bi bilo mnogo teže i zajebanije.

Sada radimo na prijenosu naših ostalih skladišta u oblak – ali to je sasvim druga priča.

izvor: www.habr.com

Dodajte komentar