NewSQL = NoSQL+ACID

NewSQL = NoSQL+ACID
Donedavno je Odnoklassniki pohranjivao oko 50 TB podataka obrađenih u stvarnom vremenu u SQL Serveru. Za takav volumen gotovo je nemoguće osigurati brz i pouzdan, pa čak i tolerantan pristup podatkovnom centru korištenjem SQL DBMS-a. Obično se u takvim slučajevima koristi jedna od NoSQL pohrana, ali ne može se sve prenijeti na NoSQL: neki entiteti zahtijevaju garancije ACID transakcije.

To nas je dovelo do korištenja NewSQL pohrane, odnosno DBMS-a koji pruža toleranciju na greške, skalabilnost i performanse NoSQL sustava, ali istovremeno zadržavajući ACID garancije poznate klasičnim sustavima. Malo je radnih industrijskih sustava te nove klase, pa smo takav sustav sami implementirali i pustili u komercijalni rad.

Kako to funkcionira i što se dogodilo - pročitajte ispod rubrike.

Danas je mjesečna publika Odnoklassniki više od 70 milijuna jedinstvenih posjetitelja. Mi Mi smo u prvih pet najveća društvena mreža na svijetu, te među dvadeset stranica na kojima korisnici provode najviše vremena. OK infrastruktura podnosi vrlo velika opterećenja: više od milijun HTTP zahtjeva/s po frontu. Dijelovi poslužiteljske flote od više od 8000 komada smješteni su blizu jedan drugoga – u četiri moskovska podatkovna centra, što omogućuje mrežnu latenciju manju od 1 ms između njih.

Cassandru koristimo od 2010. godine, počevši od verzije 0.6. Danas je u funkciji nekoliko desetaka klastera. Najbrži klaster obrađuje više od 4 milijuna operacija u sekundi, a najveći pohranjuje 260 TB.

Međutim, sve su to obični NoSQL klasteri koji se koriste za pohranu slabo koordiniran podaci. Htjeli smo zamijeniti glavnu konzistentnu pohranu, Microsoft SQL Server, koji se koristio od osnutka Odnoklassniki. Skladište se sastojalo od više od 300 SQL Server Standard Edition strojeva koji su sadržavali 50 TB podataka – poslovnih subjekata. Ovi se podaci mijenjaju kao dio ACID transakcija i zahtijevaju visoka konzistencija.

Za distribuciju podataka po čvorovima SQL Servera koristili smo i okomite i vodoravne particioniranje (sharding). Povijesno gledano, koristili smo jednostavnu shemu dijeljenja podataka: svaki entitet bio je povezan s tokenom - funkcijom ID-a entiteta. Entiteti s istim tokenom postavljeni su na isti SQL poslužitelj. Odnos master-detail implementiran je tako da se tokeni glavnog i podređenog zapisa uvijek podudaraju i nalaze na istom poslužitelju. U društvenoj mreži gotovo svi zapisi generiraju se u ime korisnika – što znači da su svi korisnički podaci unutar jednog funkcionalnog podsustava pohranjeni na jednom poslužitelju. Odnosno, poslovna transakcija je gotovo uvijek uključivala tablice s jednog SQL poslužitelja, što je omogućilo osiguranje konzistentnosti podataka korištenjem lokalnih ACID transakcija, bez potrebe za korištenjem sporo i nepouzdano distribuirane ACID transakcije.

Zahvaljujući dijeljenju i ubrzanju SQL-a:

  • Ne koristimo ograničenja stranog ključa jer se prilikom dijeljenja ID entiteta može nalaziti na drugom poslužitelju.
  • Ne koristimo pohranjene procedure i okidače zbog dodatnog opterećenja CPU-a DBMS-a.
  • Ne koristimo JOIN-ove zbog svega navedenog i puno nasumičnog čitanja s diska.
  • Izvan transakcije koristimo razinu izolacije Read Uncommitted za smanjenje zastoja.
  • Obavljamo samo kratke transakcije (u prosjeku kraće od 100 ms).
  • Ne koristimo višeredni UPDATE i DELETE zbog velikog broja zastoja - ažuriramo samo jedan zapis odjednom.
  • Uvijek izvršavamo upite samo na indeksima - upit s punim planom skeniranja tablice za nas znači preopterećenje baze podataka i uzrok njezinog kvara.

Ovi su nam koraci omogućili da iz SQL poslužitelja izvučemo gotovo maksimalnu izvedbu. Međutim, problemi su postajali sve brojniji. Pogledajmo ih.

Problemi sa SQL-om

  • Budući da smo koristili samonapisano dijeljenje, administratori su ručno dodavali nove dijelove. Cijelo to vrijeme, skalabilne replike podataka nisu servisirale zahtjeve.
  • Kako broj zapisa u tablici raste, brzina umetanja i modificiranja se smanjuje; kada se dodaju indeksi u postojeću tablicu, brzina opada za faktor; stvaranje i ponovno stvaranje indeksa odvija se uz zastoj.
  • Imati malu količinu Windowsa za SQL Server u proizvodnji otežava upravljanje infrastrukturom

Ali glavni problem je

tolerancija kvarova

Klasični SQL poslužitelj ima lošu toleranciju na pogreške. Recimo da imate samo jedan poslužitelj baze podataka i on se kvari jednom svake tri godine. Za to vrijeme stranica ne radi 20 minuta, što je prihvatljivo. Ako imate 64 poslužitelja, stranica ne radi jednom svaka tri tjedna. A ako imate 200 servera, onda stranica ne radi svaki tjedan. Ovo je problem.

Što se može učiniti da se poboljša tolerancija grešaka SQL poslužitelja? Wikipedia nas poziva da gradimo visoko dostupan klaster: gdje u slučaju kvara bilo koje od komponenti postoji rezervna.

Za to je potrebna flota skupe opreme: brojna dupliciranja, optička vlakna, dijeljena pohrana, a uključivanje rezerve ne funkcionira pouzdano: oko 10% komutacija završi kvarom rezervnog čvora poput vlaka iza glavnog čvora.

Ali glavni nedostatak tako visoko dostupnog klastera je nulta dostupnost ako podatkovni centar u kojem se nalazi zakaže. Odnoklassniki ima četiri podatkovna centra i moramo osigurati rad u slučaju potpunog kvara na jednom od njih.

Za ovo bismo mogli koristiti Multi-Master replikacija ugrađena u SQL Server. Ovo je rješenje puno skuplje zbog cijene softvera i ima dobro poznate probleme s replikacijom - nepredvidiva kašnjenja transakcija kod sinkrone replikacije i kašnjenja u primjeni replikacija (i, kao rezultat, izgubljene izmjene) kod asinkrone replikacije. Ono što se podrazumijeva ručno rješavanje sukoba čini ovu opciju za nas potpuno neprimjenjivom.

Svi ovi problemi zahtijevali su radikalno rješenje te smo ih počeli detaljno analizirati. Ovdje se trebamo upoznati s onim što SQL Server uglavnom radi – transakcije.

Jednostavna transakcija

Razmotrimo najjednostavniju transakciju, sa stajališta primijenjenog SQL programera: dodavanje fotografije u album. Albumi i fotografije pohranjeni su u različite ploče. Album ima javni brojač fotografija. Tada se takva transakcija dijeli na sljedeće korake:

  1. Blokiramo album ključem.
  2. Napravite unos u tablici fotografija.
  3. Ako fotografija ima javni status, dodajte brojač javnih fotografija u album, ažurirajte zapis i izvršite 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 scenarij za poslovnu transakciju čitanje podataka iz baze podataka u memoriju aplikacijskog poslužitelja, promjena nečega i spremanje novih vrijednosti natrag u bazu podataka. Obično u takvoj transakciji ažuriramo nekoliko entiteta, nekoliko tablica.

Prilikom izvršavanja transakcije može doći do istodobne izmjene istih podataka iz drugog sustava. Na primjer, Antispam može odlučiti da je korisnik na neki način sumnjiv i stoga sve njegove fotografije više ne bi trebale biti javne, potrebno ih je poslati na moderiranje, što znači promijeniti photo.status na neku drugu vrijednost i isključiti odgovarajuće brojače. Očito, ako se ova operacija dogodi bez jamstva atomičnosti primjene i izolacije konkurentskih modifikacija, kao u ACID, tada rezultat neće biti ono što je potrebno - ili će brojač fotografija pokazati pogrešnu vrijednost ili neće sve fotografije biti poslane na moderiranje.

Puno sličnog koda, koji manipulira različitim poslovnim subjektima unutar jedne transakcije, napisano je tijekom cijelog postojanja Odnoklassniki. Na temelju iskustva migracija na NoSQL iz Eventualna dosljednost Znamo da najveći izazov (i ulaganje vremena) dolazi od razvoja koda za održavanje dosljednosti podataka. Stoga smo smatrali da je glavni zahtjev za novu pohranu osiguranje stvarnih ACID transakcija za logiku aplikacije.

Drugi, ne manje važni zahtjevi bili su:

  • Ako podatkovni centar zakaže, mora biti dostupno i čitanje i pisanje u novu pohranu.
  • Održavanje trenutne brzine razvoja. To jest, kada radite s novim repozitorijem, količina koda bi trebala biti približno ista; ne bi trebalo biti potrebno ništa dodavati u repozitorij, razvijati algoritme za rješavanje sukoba, održavanje sekundarnih indeksa itd.
  • Brzina nove pohrane morala je biti prilično velika, kako kod čitanja podataka tako i kod obrade transakcija, što je zapravo značilo da akademski rigorozna, univerzalna, ali spora rješenja, kao što je npr. dvofazni commit.
  • Automatsko skaliranje u hodu.
  • Korištenje običnih jeftinih poslužitelja, bez potrebe za kupnjom egzotičnog hardvera.
  • Mogućnost razvoja pohrane od strane programera tvrtke. Drugim riječima, prioritet je dan 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 uzeti bilo koji SQL poslužitelj i implementirati potrebnu toleranciju grešaka, mehanizam skaliranja, failover klaster, rješavanje sukoba i distribuirane, pouzdane i brze ACID transakcije. Ovu smo opciju ocijenili kao vrlo netrivijalnu i radno intenzivnu.

Druga opcija je uzeti gotovu NoSQL pohranu s implementiranim skaliranjem, failover klasterom, rješavanjem sukoba i sami implementirati transakcije i SQL. Na prvi pogled čak i zadatak implementacije SQL-a, da ne spominjemo ACID transakcije, izgleda kao zadatak koji će trajati godinama. Ali onda smo shvatili da je skup značajki SQL-a koji koristimo u praksi toliko daleko od ANSI SQL-a Cassandra CQL daleko od ANSI SQL-a. Pogledavši još bolje CQL, shvatili smo da je prilično blizu onoga što nam je potrebno.

Cassandra i CQL

Dakle, što je zanimljivo o Cassandri, koje mogućnosti ima?

Prvo, ovdje možete kreirati tablice koje podržavaju različite vrste podataka; možete učiniti SELECT ili UPDATE na primarnom ključu.

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

Kako bi osigurala dosljednost podataka replike, Cassandra koristi pristup kvoruma. U najjednostavnijem slučaju, to znači da kada su tri replike istog reda postavljene na različite čvorove klastera, pisanje se smatra uspješnim ako je većina čvorova (to jest, dva od tri) potvrdila uspjeh ove operacije pisanja . Podaci retka smatraju se dosljednima ako je, prilikom čitanja, većina čvorova ispitana i potvrdila ih. Stoga je s tri replike zajamčena potpuna i trenutna konzistentnost podataka ako jedan čvor zakaže. Ovaj nam je pristup omogućio implementaciju još pouzdanije sheme: uvijek slati zahtjeve sve tri replike, čekajući odgovor od dvije najbrže. Kasni odgovor treće replike se u ovom slučaju odbacuje. Čvor koji kasni s odgovorom može imati ozbiljne probleme - kočnice, sakupljanje smeća u JVM-u, izravno vraćanje memorije u Linux kernelu, kvar hardvera, prekid veze s mrežom. Međutim, to ni na koji način ne utječe na operacije ili podatke klijenta.

Pristup kada kontaktiramo tri čvora i dobijemo odgovor od dva se zove špekulacije: zahtjev za dodatnim replikama šalje se i prije nego “otpadne”.

Još jedna prednost Cassandre je Batchlog, mehanizam koji osigurava da se niz promjena koje napravite u potpunosti primijeni ili da se uopće ne primijeni. To nam omogućuje da odmah riješimo A u ACID - atomičnost.

Najbliže transakcijama u Cassandri su tzv.lagane transakcije". Ali oni su daleko od "pravih" ACID transakcija: zapravo, ovo je prilika za napraviti CAS na podacima iz samo jednog zapisa, koristeći konsenzus koristeći teški Paxos protokol. Stoga je brzina takvih transakcija mala.

Ono što nam je nedostajalo u Cassandri

Dakle, morali smo implementirati prave ACID transakcije u Cassandri. Pomoću kojih bismo mogli lako implementirati dvije druge prikladne značajke klasičnog DBMS-a: konzistentne brze indekse, koji bi nam omogućili obavljanje odabira podataka ne samo primarnim ključem, i obični generator monotonih auto-inkrementirajućih ID-ova.

Konus

Tako je rođen novi DBMS Konus, koji se sastoji od tri vrste poslužiteljskih čvorova:

  • Storage – (gotovo) standardni Cassandra poslužitelji zaduženi za pohranu podataka na lokalne diskove. Kako opterećenje i obujam podataka raste, njihova se količina lako može skalirati na desetke i stotine.
  • Koordinatori transakcija - osiguravaju izvršenje transakcija.
  • Klijenti su aplikacijski poslužitelji koji provode poslovne operacije i pokreću transakcije. Takvih klijenata može biti na tisuće.

NewSQL = NoSQL+ACID

Poslužitelji svih vrsta dio su zajedničkog klastera, koriste interni Cassandra protokol poruka za međusobnu komunikaciju i trač za razmjenu informacija o klasteru. Uz Heartbeat, poslužitelji uče o međusobnim kvarovima, održavaju jedinstvenu podatkovnu shemu - tablice, njihovu strukturu i replikaciju; shema particioniranja, topologija klastera itd.

Klijenti

NewSQL = NoSQL+ACID

Umjesto standardnih upravljačkih programa, koristi se način Fat Client. Takav čvor ne pohranjuje podatke, ali može djelovati kao koordinator za izvršenje zahtjeva, odnosno sam Klijent djeluje kao koordinator svojih zahtjeva: postavlja upite replikama za pohranu i rješava konflikte. Ovo nije samo pouzdanije i brže od standardnog upravljačkog programa, koji zahtijeva komunikaciju s udaljenim koordinatorom, već vam također omogućuje kontrolu prijenosa zahtjeva. Izvan transakcije otvorene na klijentu, zahtjevi se šalju u repozitorije. Ako je klijent otvorio transakciju, tada se svi zahtjevi unutar transakcije šalju koordinatoru transakcije.
NewSQL = NoSQL+ACID

C*One Transaction Coordinator

Koordinator je nešto što smo implementirali za C*One od nule. Odgovoran je za upravljanje transakcijama, zaključavanjima i redoslijedom kojim se transakcije primjenjuju.

Za svaku servisiranu transakciju, koordinator generira vremensku oznaku: svaka sljedeća transakcija veća je od prethodne transakcije. Budući da se Cassandrin sustav rješavanja sukoba temelji na vremenskim oznakama (od dva sukobljena zapisa, onaj s posljednjom vremenskom oznakom smatra se trenutnim), sukob će uvijek biti riješen u korist sljedeće transakcije. Tako smo implementirali Lamport sat - jeftin način rješavanja sukoba u distribuiranom sustavu.

Brave

Kako bismo osigurali izolaciju, odlučili smo koristiti najjednostavniju metodu - pesimistične brave temeljene na primarnom ključu zapisa. Drugim riječima, u transakciji se zapis prvo mora zaključati, tek onda pročitati, modificirati i spremiti. Tek nakon uspješnog izvršenja zapis se može otključati kako bi ga konkurentske transakcije mogle koristiti.

Implementacija takvog zaključavanja je jednostavna u nedistribuiranom okruženju. U distribuiranom sustavu postoje dvije glavne opcije: ili implementirati distribuirano zaključavanje na klasteru 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 među skupinama lokalnih transakcija u SQL-u, odlučeno je dodijeliti lokalne skupine transakcija koordinatorima: jedan koordinator obavlja sve transakcije s tokenima od 0 do 9, drugi - s tokenima od 10 do 19, i tako dalje. Kao rezultat toga, svaka od instanci koordinatora postaje glavni transakcijske grupe.

Tada se zaključavanja mogu implementirati u obliku banalnog HashMapa u memoriju koordinatora.

Kvarovi koordinatora

Budući da jedan koordinator isključivo opslužuje grupu transakcija, vrlo je važno brzo utvrditi činjenicu njegovog neuspjeha kako bi drugi pokušaj izvršenja transakcije istekao. Kako bismo ovo učinili brzim i pouzdanim, upotrijebili smo potpuno povezani protokol otkucaja kvoruma:

Svaki podatkovni centar ima najmanje dva koordinatorska čvora. Povremeno svaki koordinator šalje poruku otkucaja srca ostalim koordinatorima i obavještava ih o svom funkcioniranju, kao i koje je poruke otkucaja srca posljednji put primio od kojeg koordinatora u klasteru.

NewSQL = NoSQL+ACID

Primajući slične informacije od drugih kao dio svojih otkucajnih poruka, svaki koordinator za sebe odlučuje koji čvorovi klastera funkcioniraju, a koji ne, vodeći se načelom kvoruma: ako je čvor X primio informaciju od većine čvorova u klasteru o normalnom primanje poruka od čvora Y, tada , Y radi. I obrnuto, čim većina prijavi nedostajuće poruke iz čvora Y, Y je odbio. Zanimljivo je da ako kvorum obavijesti čvor X da više ne prima poruke od njega, tada će se sam čvor X smatrati neispravnim.

Poruke otkucaja srca šalju se velikom frekvencijom, oko 20 puta u sekundi, s periodom od 50 ms. U Javi je teško jamčiti odgovor aplikacije unutar 50 ms zbog usporedive duljine pauza koje uzrokuje sakupljač smeća. Uspjeli smo postići ovo vrijeme odziva koristeći G1 skupljač smeća, koji nam omogućuje da odredimo cilj za trajanje GC pauza. Međutim, ponekad, vrlo rijetko, pauze kolektora prelaze 50 ms, što može dovesti do lažne detekcije kvara. Da se to ne bi dogodilo, koordinator ne prijavljuje kvar udaljenog čvora kada nestane prva poruka otkucaja srca s njega, samo ako ih je nestalo nekoliko zaredom. Tako smo uspjeli detektirati kvar koordinatorskog čvora u 200 ms.

Ali nije dovoljno brzo shvatiti koji je čvor prestao funkcionirati. Moramo učiniti nešto u vezi ovoga.

Rezervacija

Klasična shema uključuje, u slučaju glavnog neuspjeha, pokretanje novih izbora pomoću jednog od moderan univerzalna algoritmi. Međutim, takvi algoritmi imaju dobro poznate probleme s vremenskom konvergencijom i duljinom samog izbornog procesa. Uspjeli smo izbjeći takva dodatna kašnjenja pomoću sheme zamjene koordinatora u potpuno povezanoj mreži:

NewSQL = NoSQL+ACID

Recimo da želimo izvršiti transakciju u grupi 50. Odredimo unaprijed shemu zamjene, odnosno koji čvorovi će izvršavati transakcije u grupi 50 u slučaju kvara glavnog koordinatora. Naš cilj je održati funkcionalnost sustava u slučaju kvara podatkovnog centra. Odredimo da će prva rezerva biti čvor iz drugog podatkovnog centra, a druga rezerva će biti čvor iz trećeg. Ova shema se odabire jednom i ne mijenja se sve dok se ne promijeni topologija klastera, odnosno dok u njega ne uđu novi čvorovi (što se događa vrlo rijetko). Procedura odabira novog aktivnog mastera u slučaju kvara starog uvijek će biti sljedeća: prva rezerva postaje aktivni master, a ako je prestala raditi, druga rezerva postaje aktivni master.

Ova shema je pouzdanija od univerzalnog algoritma, budući da je za aktiviranje novog mastera dovoljno utvrditi neuspjeh starog.

Ali kako će klijenti razumjeti koji majstor sada radi? Nemoguće je poslati informaciju tisućama klijenata u 50 ms. Moguća je situacija kada klijent pošalje zahtjev za otvaranje transakcije, a da još ne zna da ovaj master više ne radi, a zahtjev će isteći. Kako se to ne bi dogodilo, klijenti spekulativno šalju zahtjev za otvaranje transakcije masteru grupe i objema njegovim rezervama odjednom, ali na taj zahtjev će odgovoriti samo onaj koji je trenutno aktivni master. Svu daljnju komunikaciju unutar transakcije klijent će obavljati samo s aktivnim masterom.

Backup masteri primljene zahtjeve za transakcije koje nisu njihove 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ć otvorio transakciju sa starim majstorom, tada se drugi odgovor ignorira (i, očito, takva transakcija neće biti dovršena i klijent će je ponoviti).

Kako transakcija funkcionira

Recimo da je klijent poslao zahtjev koordinatoru za otvaranje transakcije za taj i taj entitet s tim i takvim primarnim ključem. Koordinator zaključava ovaj entitet i postavlja ga u tablicu zaključavanja u memoriji. Ako je potrebno, koordinator čita ovaj entitet iz pohrane i pohranjuje rezultirajuće podatke u stanju transakcije u memoriju koordinatora.

NewSQL = NoSQL+ACID

Kada klijent želi promijeniti podatke u transakciji, šalje zahtjev koordinatoru za izmjenu entiteta, a koordinator postavlja nove podatke u tablicu statusa transakcije u memoriju. Ovime je snimanje završeno - nema snimanja u memoriju.

NewSQL = NoSQL+ACID

Kada klijent zatraži vlastite izmijenjene podatke kao dio aktivne transakcije, koordinator postupa na sljedeći način:

  • ako je ID već u transakciji, tada se podaci uzimaju iz memorije;
  • ako u memoriji nema ID-a, podaci koji nedostaju čitaju se iz čvorova za pohranu, kombiniraju se s onima koji su već u memoriji, a rezultat se daje klijentu.

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

NewSQL = NoSQL+ACID

Kada klijent pošalje commit, stanje koje je bilo u memoriji usluge koordinator sprema u zabilježenu seriju i šalje se kao zabilježena serija u pohranu Cassandre. Trgovine poduzimaju sve što je potrebno kako bi ovaj paket bio atomski (potpuno) primijenjen te vraćaju odgovor koordinatoru koji otključava blokadu i klijentu potvrđuje uspješnost transakcije.

NewSQL = NoSQL+ACID

A za povratak, koordinator treba samo osloboditi memoriju koju zauzima stanje transakcije.

Kao rezultat gore navedenih poboljšanja, implementirali smo ACID principe:

  • Valentnost. Ovo je jamstvo da nijedna transakcija neće biti djelomično zabilježena u sustavu; ili će sve njegove podoperacije biti dovršene ili nijedna neće biti dovršena. Pridržavamo se ovog načela putem zabilježene serije u Cassandri.
  • Dosljednost. Svaka uspješna transakcija, po definiciji, bilježi samo valjane rezultate. Ako se nakon otvaranja transakcije i izvođenja dijela operacija otkrije da rezultat nije valjan, izvodi se vraćanje.
  • Izolacija. Kada se transakcija izvrši, istodobne transakcije ne bi trebale utjecati na njezin ishod. Konkurentske transakcije su izolirane pomoću pesimističkih zaključavanja na koordinatoru. Za čitanja izvan transakcije, načelo izolacije se poštuje na razini Read Committed.
  • Postojanost. Bez obzira na probleme na nižim razinama - zamračenje sustava, kvar hardvera - promjene napravljene uspješno dovršenom transakcijom trebale bi ostati sačuvane kada se operacije nastave.

Čitanje po indeksima

Uzmimo jednostavnu tablicu:

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

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

SELECT *
WHERE owner=?
AND modified>?

Da bi se takav upit brzo obradio, u klasičnom SQL DBMS-u potrebno je izgraditi indeks po stupcima (vlasnički, modificirani). To možemo učiniti prilično jednostavno, budući da sada imamo ACID jamstva!

Indeksi u C*One

Postoji izvorna tablica s fotografijama u kojoj je ID zapisa primarni ključ.

NewSQL = NoSQL+ACID

Za indeks, C*One stvara novu tablicu koja je kopija originala. Ključ je isti kao izraz indeksa, a uključuje i primarni ključ zapisa iz izvorne tablice:

NewSQL = NoSQL+ACID

Sada se upit za "vlasnika za prošli dan" može prepisati kao odabir iz druge tablice:

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

Konzistentnost podataka u fotografijama izvorne tablice i tablici indeksa i1 automatski održava koordinator. Samo na temelju podatkovne sheme, kada se primi promjena, koordinator generira i pohranjuje promjenu ne samo u glavnoj tablici, već iu kopijama. Nikakve dodatne akcije se ne izvode na tablici indeksa, dnevnici se ne čitaju i ne koriste se zaključavanja. Odnosno, dodavanje indeksa ne troši gotovo nikakve resurse i nema praktički nikakvog utjecaja na brzinu primjene izmjena.

Pomoću ACID-a uspjeli smo implementirati indekse slične SQL-u. Oni su dosljedni, skalabilni, brzi, sastavljavi i ugrađeni u CQL jezik upita. Za podršku indeksima nisu potrebne promjene koda aplikacije. Sve je jednostavno kao u SQL-u. I što je najvažnije, indeksi ne utječu na brzinu izvršenja izmjena izvorne transakcijske tablice.

Što se dogodilo

Prije tri godine razvili smo C*One i pustili ga u komercijalni rad.

Što smo na kraju dobili? Procijenimo to na primjeru podsustava za obradu i pohranu fotografija, jedne od najvažnijih vrsta podataka u društvenoj mreži. Ne govorimo o samim tijelima fotografija, već o svim vrstama metainformacija. Sada Odnoklassniki ima oko 20 milijardi takvih zapisa, sustav obrađuje 80 tisuća zahtjeva za čitanje u sekundi, do 8 tisuća ACID transakcija u sekundi povezanih s modificiranjem podataka.

Kada smo koristili SQL s faktorom replikacije = 1 (ali u RAID-u 10), metainformacije o fotografijama bile su pohranjene na vrlo dostupnom klasteru od 32 računala s Microsoft SQL Serverom (plus 11 sigurnosnih kopija). Također je dodijeljeno 10 poslužitelja za pohranu sigurnosnih kopija. Ukupno 50 skupih automobila. Pritom je sustav radio pod nazivnim opterećenjem, bez rezerve.

Nakon prelaska na novi sustav dobili smo faktor replikacije = 3 - kopiju u svakom podatkovnom centru. Sustav se sastoji od 63 čvora za pohranu Cassandra i 6 strojeva za koordinaciju, za ukupno 69 poslužitelja. Ali ti su strojevi puno jeftiniji, njihov ukupni trošak je oko 30% cijene SQL sustava. U isto vrijeme, opterećenje se održava na 30%.

S 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, commit je dovršen za 2 ms, trajanje čitanja i pisanja je u prosjeku 2 ms. 99. percentil - samo 3-3,1 ms, broj timeouta se smanjio za 100 puta - sve zbog raširene upotrebe špekulacija.

Do sada je većina čvorova SQL Servera povučena iz upotrebe; novi proizvodi se razvijaju samo koristeći C*One. Prilagodili smo C*One za rad u našem oblaku jednooblak, što je omogućilo ubrzanje implementacije novih klastera, pojednostavljenje konfiguracije i automatizaciju rada. Bez izvornog koda ovo bi bilo mnogo teže i glomaznije.

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

Izvor: www.habr.com

Dodajte komentar