NewSQL = NoSQL+ACID

NewSQL = NoSQL+ACID
Derisa, në Odnoklassniki, më parë kishte rreth 50 TB të dhënash që procesoheshin në kohë reale, të ruajtura në SQL Server. Për një vëllim të tillë, të sigurosh një qendër të dhënash të shpejtë, të besueshme dhe gjithashtu të qëndrueshme ndaj defekteve, duke përdorur SQL DBMS, është praktikisht e pamundur. Në raste të tilla, zakonisht përdoren një nga depot NoSQL, por jo gjithçka mund të transferohet në NoSQL: disa entitete kërkojnë garanci të transaksioneve ACID.

Kjo na çoi në përdorimin e një depoje NewSQL, që do të thotë një DBMS që ofron qëndrueshmëri, shkallëzim dhe performancë të sistemeve NoSQL, por që ruan garancitë ACID të sistemeve klasike. Ka pak sisteme industriale të këtij klasi të ri që funksionojnë, prandaj ne e realizuam një sistem të tillë vetë dhe e lançuam në operacionin industrial.

Si funksionon dhe çfarë arritëm - lexoni më poshtë.

Sot, audienca mujore e "Odnoklassniki" është mbi 70 milion vizitorë unikë. Ne jemi në mesin e pesë rrjeteve më të mëdha sociale në botë, dhe në listën e dyzetë të faqeve ku përdoruesit kalojnë më shumë kohë. Infrastrukturat e "OK" përpunojnë ngarkesa shumë të larta: më shumë se një milion kërkesa HTTP/s në frontet. Një pjesë e parkut të serverëve me më shumë se 8000 njësitë janë të vendosura afër njëri-tjetrit - në katër qendrat e të dhënave në Moskë, gjë që lejon sigurimin e një vonese rrjeti më pak se 1 ms midis tyre.

Ne kemi përdorur Cassandra që nga viti 2010, duke filluar nga versioni 0.6. Sot, në funksion janë disa dhjetëra klastera. Klasteri më i shpejtë përpunon më shumë se 4 milion operacione në sekondë, ndërsa më i madhi ruan 260 TB.

Megjithatë, të gjitha këto janë klastere të zakonshme NoSQL, të cilat përdoren për ruajtjen e të dhënave me konsistencë të ulët. Na duhej të zëvendësonim depozitat kryesore të konsistencës, Microsoft SQL Server, të cilat ishin përdorur që nga fillimi i "Odnoklassniki". Deponi përbëhej nga më shumë se 300 makina SQL Server Standard Edition, të cilat mbanin 50 TB të dhënash - entitete biznesi. Këto të dhëna modifikohen në kuadër të transaksioneve ACID dhe kërkojnë konsistencë të lartë..

Për shpërndarjen e të dhënave midis node-ve SQL Server përdorëm si particionim vertikal (shardim). Historikisht, ne kemi përdorur një skemë të thjeshtë të shardimit të të dhënave: çdo entitet i ishte caktuar një token - një funksion nga ID e entitetit. Entitetet me të njëjtin token ishin vendosur në një SQL server. Marrëdhënia e tipit master-detail realizohej në mënyrë që tokenet e regjistrimit kryesor dhe atij të krijuar të ishin gjithmonë identike dhe të ishin në një server. Në rrjetin social, pothuajse të gjitha të dhënat janë krijuar në emër të përdoruesit - domethënë, të gjitha të dhënat e përdoruesit brenda një nënproçesi funksional ruhet në një server. Pra, pothuajse gjithmonë në transaksionet biznesi merrnin pjesë tabela të një SQL serveri, që mundësoi ruajtjen e konsistencës së të dhënave përmes transaksioneve lokale ACID, pa nevojën e përdorimit të transaksioneve ACID të ngadalta dhe të pasigurt. Falë shardimit dhe për të përshpejtuar punën e SQL:

Nuk përdorim kufizime të çelësit të huaj, pasi me shardimin ID e entitetit mund të ndodhet në një server tjetër.

  • Nuk pĂ«rdorim procedurat e ruajtura dhe trigger-at pĂ«r shkak tĂ« ngarkesĂ«s sĂ« shtuar nĂ« CPU tĂ« DBMS.
  • Nuk pĂ«rdorim JOINs pĂ«r shkak tĂ« tĂ« gjitha gjĂ«rave tĂ« mĂ«sipĂ«rme dhe numrit tĂ« madh tĂ« leximeve tĂ« rastĂ«sishme nga disku.
  • JashtĂ« transaksionit, pĂ«r tĂ« reduktuar bllokimet, pĂ«rdorim nivelin e izolimit tĂ« Leximit tĂ« Paangazhuar.
  • KryejmĂ« vetĂ«m transaksione tĂ« shkurtra (nĂ« mesatare mĂ« tĂ« shkurtra se 100 ms).
  • Nuk pĂ«rdorim UPDATE dhe DELETE tĂ« shumĂ« regjistrave pĂ«r shkak tĂ« numrit tĂ« madh tĂ« bllokimeve - pĂ«rditĂ«sojmĂ« vetĂ«m njĂ« regjistĂ«r nĂ« njĂ« kohĂ«.
  • KĂ«rkesat gjithmonĂ« i kryejmĂ« vetĂ«m pĂ«rmes indekseve - njĂ« kĂ«rkesĂ« me planin e shikimit tĂ« plotĂ« tĂ« tabelĂ«s pĂ«r ne do tĂ« thotĂ« mbingarkesĂ« tĂ« DB dhe refuzimin e saj.
  • KĂ«to hapa na lejuan tĂ« nxjerrim nga SQL-serverat thuajse maksimumin e performancĂ«s. MegjithatĂ«, problemet ishin gjithnjĂ« e mĂ« tĂ« shumta. Le tĂ« shikojmĂ« ato.

Problemet me SQL

Duke qenë se ne përdorim shardimin e shkruar vetë, shtimi i shardeve të rinj bëhej manualisht nga administratorët. Gjatë gjithë kësaj kohe, replikat e dhënave të shkallëzuara nuk shërbenin për kërkesat.

  • Me rritjen e numrit tĂ« regjistrave nĂ« tabelĂ«, shpejtĂ«sia e futjes dhe modifikimit u ul, kur u shtuan indekse nĂ« tabelĂ«n ekzistuese, shpejtĂ«sia ra shumĂ« herĂ«, krijimi dhe rikrijimi i indekseve ndodhte me downtime.
  • Prania e njĂ« sasi tĂ« vogĂ«l Windows pĂ«r SQL Server nĂ« production vĂ«shtirĂ«son menaxhimin e infrastrukturĂ«s.
  • Por problemi kryesor -

ĐĐŸ глаĐČĐœĐ°Ń ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒĐ° —

Qëndrueshmëria

Serverat klasik SQL kanë qëndrushmëri të dobët. Supozojmë se keni vetëm një server baze të dhënash, dhe ai dështon çdo tri vjet. Në këtë kohë, faqja e internetit nuk funksionon për 20 minuta, që është e pranueshme. Po sikur të keni 64 servera, faqja nuk funksionon çdo tri javë. Ndërsa nëse keni 200 servera, faqja nuk funksionon çdo javë. Kjo është një problem.

ÇfarĂ« mund tĂ« bĂ«het pĂ«r tĂ« rritur qĂ«ndrushmĂ«rinĂ« e serverit SQL? Wikipedia na propozon tĂ« ndĂ«rtojmĂ« klastra me disponueshmĂ«ri tĂ« lartĂ«: ku nĂ« rastin e dĂ«shtimit tĂ« ndonjĂ« komponenti ka njĂ« kopje rezerva.

Kjo kërkon një park të shtrenjtë pajisjesh: shumë kopje, fibra optike, ruajtje të përbashkët, dhe madje aktivizimi i rezervës nuk funksionon me besueshmëri: rreth 10% e aktivizimeve përfundojnë me dështimin e njësisë rezervë pas njësisë kryesore.

Por e meta kryesore e një klasteri me disponueshmëri të lartë është se nuk ka disponueshmëri në rast dështimi të qendrës së të dhënave ku ndodhet. 'Odnoklassniki' ka katër qendra të të dhënave, dhe na nevojitet të sigurojmë funksionimin gjatë një katastrofe të plotë në njërën prej tyre.

PĂ«r kĂ«tĂ« ne do tĂ« mund tĂ« aplikonim replikimin Multi-Master tĂ« integruar nĂ« SQL Server. Ky zgjidhje Ă«shtĂ« shumĂ« mĂ« e shtrenjtĂ« pĂ«r shkak tĂ« kostos sĂ« softuerit dhe vuajti nga problemet e njohura me replikimin — vonesa tĂ« paparashikueshme tĂ« transaksioneve gjatĂ« replikimit tĂ« sinkronizuar dhe vonesa nĂ« aplikimin e replikimeve (dhe, si pasojĂ«, modifikime tĂ« humbura) gjatĂ« replikimit asinkron. I implicit zgjidhjen manuale tĂ« konflikteve e bĂ«n kĂ«tĂ« variant plotĂ«sisht tĂ« paaplikueshĂ«m pĂ«r ne.

TĂ« gjitha kĂ«to probleme kĂ«rkonin njĂ« zgjidhje radikale dhe ne filluam analizĂ«n e tyre tĂ« detajuar. KĂ«tu na nevojitet tĂ« njohim se çfarĂ« bĂ«n kryesisht SQL Server — transaksionet.

Transaksioni i thjeshtë

Le të shqyrtojmë një transaksion shumë të thjeshtë, nga këndvështrimi i zhvilluesit të SQL: shtimi i një fotografie në album. Albumet dhe fotografitë ruhen në tavolina të ndryshme. Albumi ka një numërues të fotografive publik. Atëherë ky transaksion ndahet në hapat e mëposhtëm:

  1. Bllokojmë albumin me çelës.
  2. Krijojmë një regjistrim në tabelën e fotografive.
  3. Nëse fotografia ka status publik, përmirësojmë numëruesin e fotografive publike në album, përditësojmë regjistrimin dhe komitojmë transaksionin.

Ose në formën e pseudokodit:

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

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

TX.commit();

Shohim se skenari më i zakonshëm i transaksionit të biznesit është të lexojmë të dhënat nga DB në memorien e serverit të aplikacionit, të ndryshojmë diçka dhe të ruajmë vlerat e reja përsëri në DB. Zakonisht në një transaksion të tillë ne përditësojmë disa entitete, disa tabela.

GjatĂ« kryerjes sĂ« transaksionit, mund tĂ« ndodhin modifikime konkuruese tĂ« tĂ« njĂ«jtave tĂ« dhĂ«na nga njĂ« sistem tjetĂ«r. PĂ«r shembull, Antispami mund tĂ« vendosĂ« se njĂ« pĂ«rdorues Ă«shtĂ« i dyshimtĂ« dhe prandaj tĂ« gjitha fotografitĂ« e pĂ«rdoruesit nuk duhet tĂ« jenĂ« mĂ« publike, ato duhet tĂ« dĂ«rgohen pĂ«r moderim, qĂ« do tĂ« thotĂ« se duhet tĂ« ndryshohet photo.status nĂ« njĂ« vlerĂ« tjetĂ«r dhe tĂ« rregullohen numĂ«ruesit pĂ«rkatĂ«s. ËshtĂ« e qartĂ« se nĂ«se kjo operacion do tĂ« ndodhte pa garancitĂ« e atomaritetit tĂ« zbatimit dhe izolimit tĂ« modifikimeve konkurente, si nĂ« ACID, rezultati do tĂ« jetĂ« jo ai qĂ« duhet — ose numĂ«ruesi i fotografive do tĂ« tregojĂ« njĂ« vlerĂ« tĂ« pasaktĂ«, ose jo tĂ« gjitha fotografitĂ« do tĂ« dĂ«rgohen pĂ«r moderim.

Një kod i tillë, që manipullon me entitete të ndryshme biznesi brenda një transaksioni, është shkruar shumë gjatë gjithë ekzistencës së Odnoklassniki. Nga përvoja e migrimeve në NoSQL me Konsistencën e vonuar ne dimë se vështirësitë më të mëdha (dhe shpenzimet kohore) shkaktohen nga nevoja për të zhvilluar kod që siguron konsistencën e të dhënave. Prandaj kërkesa kryesore për ruajtjen e re ishte sigurimi për logjikën aplikative të transaksioneve të vërteta ACID.

Një tjetër, po aq e rëndësishme, kërkesë ishte:

  • NĂ« rast dĂ«shtimi tĂ« qendrĂ«s sĂ« tĂ« dhĂ«nave duhet tĂ« jenĂ« tĂ« disponueshme si leximi, ashtu edhe shkruarja nĂ« ruajtjen e re.
  • Ruajtja e shpejtĂ«sisĂ« aktuale tĂ« zhvillimit. Kjo do tĂ« thotĂ« se gjatĂ« punĂ«s me ruajtjen e re, sasia e kodit duhet tĂ« jetĂ« afĂ«rsisht e njĂ«jtĂ«, nuk duhet tĂ« ketĂ« nevojĂ« pĂ«r tĂ« shtuar ndonjĂ« gjĂ« nĂ« ruajtje, pĂ«r tĂ« zhvilluar algorithma pĂ«r zgjidhjen e konflikteve, pĂ«r mbĂ«shtetje tĂ« indekseve tĂ« dyta etj.
  • ShpejtĂ«sia e punĂ«s sĂ« ruajtjes sĂ« re duhet tĂ« jetĂ« mjaft e lartĂ«, si gjatĂ« leximit tĂ« tĂ« dhĂ«nave, ashtu edhe gjatĂ« trajtimit tĂ« transaksioneve, qĂ« do tĂ« thotĂ« qĂ« zgjidhjet akademikisht tĂ« sakta, universale, por tĂ« ngadalta, si pĂ«r shembull, komitetet me dy faza.
  • Automatizimi i shkallĂ«zimit nĂ« fluks.
  • PĂ«rdorimi i serverĂ«ve tĂ« zakonshĂ«m tĂ« lirĂ«, pa nevojĂ«n pĂ«r tĂ« blerĂ« pajisje ekzotike.
  • MundĂ«sia pĂ«r tĂ« zhvilluar depozitimin me forcat e zhvilluesve tĂ« kompanisĂ«. Me fjalĂ« tĂ« tjera, prioriteti i ishte dhĂ«nĂ« zgjidhjeve tĂ« brendshme ose atyre me kod tĂ« hapur, pĂ«r mĂ« tepĂ«r nĂ« Java.

Zgjidhjet, zgjidhjet

Duke analizuar zgjidhjet e mundshme, arritëm në dy mundësi arkitekture:

E para — tĂ« marrim ndonjĂ« SQL-server dhe tĂ« implementojmĂ« qĂ«ndrueshmĂ«rinĂ« e nevojshme, mekanizmin e shkallĂ«zimit, klasterin e qĂ«ndrueshĂ«m, zgjidhjen e konflikteve dhe transaksionet ACID tĂ« shpĂ«rndara, tĂ« besueshme dhe tĂ« shpejta. Ne e vlerĂ«suam kĂ«tĂ« opsion si mjaft tĂ« ndĂ«rlikuar dhe punĂ« tĂ« madhe.

Opsioni i dytĂ« — tĂ« marrim njĂ« depo NoSQL tĂ« gatshme me shkallĂ«zim tĂ« implementuar, klaster tĂ« qĂ«ndrueshĂ«m, zgjidhje konflikti dhe tĂ« realizojmĂ« transaksionet dhe SQL vetĂ«. NĂ« pamje tĂ« parĂ«, madje edhe detyra e realizimit tĂ« SQL, pa pĂ«rmendur transaksionet ACID, duket si njĂ« detyrĂ« pĂ«r vite. Por mĂ« pas kuptuam se grupi i mundĂ«sive SQL qĂ« ne pĂ«rdorim nĂ« praktikĂ« Ă«shtĂ« shumĂ« larg ANSI SQL ashtu si Cassandra CQL Ă«shtĂ« e largĂ«t nga ANSI SQL. Duke u shqyrtuar mĂ« me vĂ«mendje CQL, kuptuam se Ă«shtĂ« mjaft afĂ«r asaj qĂ« na nevojitet.

Cassandra dhe CQL

Pra, çfarë është interesante për Cassandra, çfarë mundësish ka?

Së pari, mund të krijosh tabela me mbështetje për lloje të ndryshme të të dhënave, mund të bësh SELECT ose UPDATE mbi çelësin primar.

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

PĂ«r tĂ« siguruar koherencĂ«n e tĂ« dhĂ«nave tĂ« replikave, Cassandra pĂ«rdor qĂ«ndrimin me shumicĂ«. NĂ« rastin mĂ« tĂ« thjeshtĂ«, kjo do tĂ« thotĂ« se kur vendosen tre replika tĂ« tĂ« njĂ«jtit rresht nĂ« node tĂ« ndryshme tĂ« klasterit, shkrimi konsiderohet i suksesshĂ«m, nĂ«se shumica e nodeve (dmth dy nga tre) kanĂ« konfirmuar suksesin e kĂ«tij operacioni tĂ« shkrimit. TĂ« dhĂ«nat e rreshtit konsiderohen tĂ« koherentĂ«, nĂ«se gjatĂ« leximit shumica e nodeve janĂ« pyetur dhe kanĂ« konfirmuar ato. NĂ« kĂ«tĂ« mĂ«nyrĂ«, me tre replika garantohet koherenca e plotĂ« dhe e menjĂ«hershme e tĂ« dhĂ«nave nĂ« rastin e dĂ«shtimit tĂ« njĂ« node. Ky qasje na lejoi tĂ« realizojmĂ« njĂ« skemĂ« edhe mĂ« tĂ« besueshme: gjithmonĂ« dĂ«rgojmĂ« kĂ«rkesa nĂ« tĂ« tri replikat, duke pritur pĂ«rgjigjen nga dy mĂ« tĂ« shpejtĂ«. PĂ«rgjigjja e vonuar e tretĂ« nĂ« kĂ«tĂ« rast injorohet. NĂ« kĂ«tĂ« rast, node qĂ« Ă«shtĂ« vonuar me pĂ«rgjigjen mund tĂ« ketĂ« probleme serioze — bllokime, mbledhje plehrash nĂ« JVM, rimarrje e memories direkte nĂ« kernelin linux, dĂ«shtim tĂ« pajisjes, shkĂ«putje nga rrjeti. MegjithatĂ«, kjo nuk ndikon aspak nĂ« operacionin e klientit dhe nĂ« tĂ« dhĂ«na.

Qasja, kur ne i drejtohemi tre nodeve, ndërsa marrim përgjigje nga dy, quhet speculacion: kërkesa për replika të tepërta dërgohet para se të 'bjerë'.

NjĂ« tjetĂ«r pĂ«rfitim nga Cassandra Ă«shtĂ« Batchlog — njĂ« mekanizĂ«m qĂ« garanton ose aplikimin e plotĂ«, ose mosaplikimin e plotĂ« tĂ« paketĂ«s sĂ« ndryshimeve qĂ« bĂ«ni. Kjo na lejon tĂ« zgjidhim A nĂ« ACID — atomizimi nga kutia.

Më e afërta me transaksionet në Cassandra është ajo që quhet 'transaksionet e lehta'. Por nga transaksionet 'e vërteta' ACID ato janë shumë të largëta: në të vërtetë, është mundësia për të bërë CAS në të dhënat e vetëm një regjistrimi, duke përdorur konsensusin përmes protokollit të rëndë Paxos. Prandaj, shpejtësia e këtyre transaksioneve nuk është shumë e lartë.

ÇfarĂ« na mungoi nĂ« Cassandra

Pra, na duhej të realizonim transaksione të vërteta ACID në Cassandra. Duke përdorur ato, mund të realizonim lehtësisht dy mundësi të tjera të dobishme të DBMS klasike: indekset e shpejta konsistente, që do të na lejonin të bënim kërkesa për të dhëna jo vetëm mbi çelësin primar dhe një gjenerator të zakonshëm të ID auto-incrementale monotone.

C*One

Kështu lindi sistemi i ri i menaxhimit të të dhënave C*One, i përbërë nga tri lloje nodash serverësh:

  • Depozitat — serverĂ« (gjashtĂ«) standarde Cassandra, pĂ«rgjegjĂ«s pĂ«r ruajtjen e tĂ« dhĂ«nave nĂ« diskĂ«t lokalĂ«. NdĂ«rsa rritet ngarkesa dhe volumi i tĂ« dhĂ«nave, numri i tyre mund tĂ« shkallĂ«zohet lehtĂ«sisht nĂ« dhjetĂ«ra dhe qindra.
  • KoordinatorĂ«t e transaksioneve — sigurojnĂ« zbatimin e transaksioneve.
  • KlientĂ«t — serverĂ« aplikacionesh, qĂ« realizojnĂ« operacionet e biznesit dhe nisin transaksionet. Ka mundĂ«si tĂ« jenĂ« mijĂ«ra klientĂ«.

NewSQL = NoSQL+ACID

TĂ« gjithĂ« serverĂ«t e llojeve tĂ« ndryshme janĂ« pjesĂ« e njĂ« klasteri tĂ« pĂ«rbashkĂ«t, pĂ«rdorin protokollin e brendshĂ«m tĂ« mesazheve Cassandra pĂ«r tĂ« komunikuar me njĂ«ri-tjetrin dhe gossip pĂ«r shkĂ«mbimin e informacionit tĂ« klasterit. Me ndihmĂ«n e Heartbeat, serverĂ«t mĂ«sojnĂ« pĂ«r dĂ«shtime tĂ« ndĂ«rsjella, mbajnĂ« njĂ« skemĂ« tĂ« pĂ«rbashkĂ«t tĂ« tĂ« dhĂ«nave — tabelat, strukturĂ«n dhe replikimin e tyre; skemĂ«n e ndarjes, topologjinĂ« e klasterit, etj.

Klientët

NewSQL = NoSQL+ACID

Në vend të drejtorëve standardë, përdoret modaliteti Fat Client. Kjo nyjë nuk ruan të dhëna, por mund të veprojë si koordinatori për ekzekutimin e kërkesave, dmth. Klienti vetë kryen funksionin e koordinimit të kërkesave të tij: interrogon replikat e magazinës dhe zgjidh konfliktet. Kjo është jo vetëm më e besueshme dhe më e shpejtë se drejtori standard, i cili kërkon komunikim me një koordinator të largët, por gjithashtu lejon menaxhimin e transmetimit të kërkesave. Jashtë një transaksioni të hapur në klient, kërkesat dërgohen në magazina. Në rast se klienti hap një transaksion, të gjitha kërkesat brenda transaksionit dërgohen në koordinatorin e transaksioneve.
NewSQL = NoSQL+ACID

Koordinatori i transaksioneve C*One

Koordinatori është ajo që ne e kemi realizuar për C*One nga zero. Ai është përgjegjës për menaxhimin e transaksioneve, bllokimeve dhe rendit të zbatimit të transaksioneve.

PĂ«r çdo transaksion tĂ« shĂ«rbyer, koordinatori gjeneron njĂ« afat tĂ« pĂ«rkohshĂ«m: çdo i ardhshĂ«m Ă«shtĂ« mĂ« i madh se ai i transaksionit tĂ« mĂ«parshĂ«m. Duke qenĂ« se nĂ« Cassandra sistemi i zgjidhjes sĂ« konflikteve bazohet nĂ« afate tĂ« pĂ«rkohshme (nga dy regjistrime konfliktuese, regjistrimi aktual Ă«shtĂ« ai me afatin mĂ« tĂ« vonshĂ«m), konflikti gjithmonĂ« do tĂ« zgjidhet nĂ« favor tĂ« transaksionit tĂ« mĂ«vonshĂ«m. KĂ«shtu e kemi realizuar orĂ«t e Lamportit — njĂ« mĂ«nyrĂ« e lirĂ« pĂ«r tĂ« zgjidhur konfliktet nĂ« njĂ« sistem tĂ« shpĂ«rndarĂ«.

Bllokimet

PĂ«r tĂ« siguruar izolimin, ne vendosĂ«m tĂ« pĂ«rdorim mĂ«nyrĂ«n mĂ« tĂ« thjeshtĂ« — bllokimet pesimiste sipas çelĂ«sit primar tĂ« regjistrimit. NĂ« fjalĂ« tĂ« tjera, nĂ« njĂ« transaksion regjistrimi duhet sĂ« pari tĂ« bllokohet, e mĂ« pas tĂ« lexohen, modifikohen dhe ruhen. VetĂ«m pas njĂ« komitimi tĂ« suksesshĂ«m regjistrimi mund tĂ« ç'bllokohet qĂ« transaksionet konkurruese ta pĂ«rdorin atĂ«.

Realizimi i tillë i bllokimit është i thjeshtë në një mjedis të pa shpërndarë. Në një sistem të shpërndarë ka dy mënyra kryesore: ose të realizosh bllokim të shpërndarë në klaster, ose të shpërndash transaksionet në mënyrë që transaksionet që përfshijnë një regjistrim të shërbehen gjithmonë nga i njëjti koordinator.

Duke qenĂ« se nĂ« rastin tonĂ« tĂ« dhĂ«nat tashmĂ« janĂ« shpĂ«rndarĂ« nĂ« grupe tĂ« transaksioneve lokale nĂ« SQL, vendosĂ«m tĂ« ngelim koordinatoret pĂ«r grupet e transaksioneve lokale: njĂ« koordinator kryen tĂ« gjitha transaksionet me token nga 0 deri nĂ« 9, tjetri — me token nga 10 deri nĂ« 19, dhe kĂ«shtu me radhĂ«. Si rezultat, çdo nga instancat e koordinatoreve bĂ«het master i grupit tĂ« transaksioneve.

Atëherë bllokimet mund të realizohen si një HashMap të zakonshme në memorje të koordinatoreve.

Dështimet e koordinatoreve

Duke qenë se një koordinator shërben vetëm një grup transaksionesh, është shumë e rëndësishme të përcaktohet shpejt fakti i dështimit të tij, në mënyrë që përpjekja për të ekzekutuar transaksionin të qëndrojë brenda afatit. Për ta bërë këtë shpejt dhe besueshëm, ne përdorëm një protokoll të lidhur të kvorumit të boy-kontaktit:

Në çdo qendër të të dhënave vendosen të paktën dy nova koordinatori. Periudhshëm, çdo koordinator dërgon një mesazh të boy-kontaktit tek koordinatoret e tjerë dhe i informon ata për funksionimin e tij, si dhe për mesazhet e boy-kontaktit nga cilët koordinatoret në klaster ai mori më së fundi.

NewSQL = NoSQL+ACID

Duke marrë informacion të ngjashëm nga të tjerët në mesazhet e tyre të boy-kontaktit, çdo koordinator vendos për veten se cilat nova të klasterit po funksionojnë dhe cilat jo, duke ndjekur parimin e kvorumit: nëse nova X ka marrë nga shumica e nova në klaster informacion për marrjen normale të mesazheve nga nova Y, atëherë Y funksionon. Dhe anasjelltas, sapo shumica të raportojnë për humbjen e mesazheve nga nova Y, atëherë Y ka dështuar. Curiositet, nëse kvorumi i raporton nova X se nuk merr më mesazhe nga ajo, atëherë edhe nova X do ta konsiderojë veten të dështuar.

Mesazhet e boy-kontaktit dërgohen me një frekuencë të lartë, rreth 20 herë në sekondë, me një periudhë prej 50 ms. Në Java është e vështirë të garantosh reagimin e aplikacionit brenda 50 ms për shkak të ngecjeve të krahasueshme që shkakton mbledhësi i plehrave. Na arriti të arrijmë këtë kohë reagimi duke përdorur mbledhësin e plehrave G1, i cili lejon caktimin e një qëllimi për kohën e ngecjeve të GC. Megjithatë, nganjëherë, mjaft rrallë, ngecjet e mbledhësit kalojnë përtej 50 ms, që mund të çojë në zbulesë të rreme të dështimit. Që të mos ndodhte një e tillë, koordinatori nuk e raporton dështimin e një nova të largët pas humbjes së mesazhit të parë të boy-kontaktit nga ajo, vetëm nëse humbet disa radhazi. Kështu arritëm të arrijmë zbulesën e dështimit të nova të koordinatoreve brenda 200 ms.

Por nuk është vetëm e rëndësishme të kuptohet, cila nova ka ndaluar së funksionuari. Duhet të bëhet diçka me këtë.

Kopjim rezervë

Schema klasike parashikon se në rast dështimi të masterit, të nisë zgjedhjen e një të ri me ndihmën e një nga modave të njohura universale . Megjithatë, këto algoritme kanë probleme të njohura në lidhje me konvergjencën në kohë dhe gjatësi të procesit të vetëzgjedhjes. Këto vonesa shtesë arritëm t'i shmangim duke përdorur një skemë zëvendësimi për koordinatoret në një rrjet të lidhur plotësisht:

NewSQL = NoSQL+ACID

Supozoni se duam tĂ« realizojmĂ« njĂ« transaksion nĂ« grupin 50. Fillimisht do tĂ« pĂ«rcaktojmĂ« skemĂ«n e zĂ«vendĂ«simit, domethĂ«nĂ« cilat node do tĂ« kryejnĂ« transaksionet e grupit 50 nĂ« rastin e dĂ«shtimit tĂ« koordinatores kryesore. QĂ«llimi ynĂ« Ă«shtĂ« tĂ« ruajmĂ« funksionalitetin e sistemit nĂ« rast dĂ«shtimi tĂ« qendrĂ«s sĂ« tĂ« dhĂ«nave. Do tĂ« pĂ«rcaktojmĂ« se rezervi i parĂ« do tĂ« jetĂ« njĂ« node nga njĂ« qendĂ«r tjera e tĂ« dhĂ«nave, ndĂ«rsa rezervi i dytĂ« njĂ« node nga njĂ« qendĂ«r e tretĂ«. Kjo skemĂ« zgjidhet njĂ«herĂ« dhe nuk ndryshon derisa tĂ« ndryshojĂ« topologjia e klasterit, domethĂ«nĂ« derisa tĂ« hyjnĂ« node tĂ« reja (diçka qĂ« ndodh shumĂ« rrallĂ«). Rregulli pĂ«r zgjedhjen e njĂ« masteri tĂ« ri aktiv nĂ« rastin e dĂ«shtimit tĂ« njĂ« tĂ« vjetri do tĂ« jetĂ« gjithmonĂ« ky: masteri aktiv do tĂ« jetĂ« rezervi i parĂ«, dhe nĂ«se edhe ai dĂ«shton — rezervi i dytĂ«.

Kjo skemë është më e besueshme se algoritmi universal, sepse për aktivizimin e një masteri të ri mjafton të përcaktohet fakti i dështimit të atij të vjetër.

Por si do ta dinë klientët se cili nga masterat është aktualisht aktiv? Në 50 ms është e pamundur të shpërndahen informatat për mijëra klientë. Mund të ndodhë që një klient të dërgojë një kërkesë për hapjen e një transaksioni, pa e ditur se ky master tashmë nuk po funksionon, duke e bërë që kërkesa të ngecë në një skadim kohor. Për të parandaluar këtë, klientët dërgojnë spekulativisht një kërkesë për hapjen e transaksionit tek masteri i grupit dhe të dy rezervat e tij, por do të përgjigjet vetëm ai që është master aktiv në atë moment. Të gjitha komunikimet e mëtejshme brenda transaksionit klienti do t'i bëjë vetëm me masterin aktiv.

Masterat rezervë vendosin kërkesat e marra për transaksionet që nuk janë për to në radhë për transaksionet e parakohshme, ku ato ruhen për një periudhë të caktuar. Nëse masteri aktiv vdes, masteri i ri merr në shqyrtim kërkesat për hapjen e transaksioneve nga radhët e tij dhe i përgjigjet klientit. Nëse klienti tashmë ka hapur një transaksion me masterin e vjetër, atëherë përgjigjia e dytë injorohet (dhe, natyrisht, një transaksion i tillë nuk do të përfundojë dhe do të përsëritet nga klienti).

Si funksionon një transaksion

Supozoni se klienti i ka dërguar koordinatores një kërkesë për hapjen e një transaksioni për një aset të tillë me një çelës të tillë. Koordinatori e bllokon këtë aset dhe e vendos në tabelën e bllokimeve në memorie. Nëse është e nevojshme, koordinatori lexon këtë aset nga depoja dhe ruan të dhënat e marra në gjendjen e transaksionit në memorien e koordinatores.

NewSQL = NoSQL+ACID

Kur klienti dĂ«shiron tĂ« ndryshojĂ« tĂ« dhĂ«nat nĂ« transaksion, ai e dĂ«rgon njĂ« kĂ«rkesĂ« pĂ«r modifikimin e asetet tek koordinatori, dhe ai vendos tĂ« dhĂ«nat e reja nĂ« tabelĂ«n e gjendjes sĂ« transaksioneve nĂ« memorie. KĂ«shtu, regjistrimi pĂ«rfundon — regjistrimi nĂ« depo nuk bĂ«het.

NewSQL = NoSQL+ACID

Kur klienti kërkon të dhënat e veta të ndryshuara brenda një transaksioni aktiv, koordinatori vepron kështu:

  • nĂ«se ID Ă«shtĂ« tashmĂ« nĂ« transaksion, tĂ« dhĂ«nat merren nga memoria;
  • nĂ«se ID nuk Ă«shtĂ« nĂ« memorie, tĂ« dhĂ«nat e munguar lexohen nga nodet e depove, kombinohen me ato qĂ« tashmĂ« janĂ« nĂ« memorie, dhe rezultati i dorĂ«zohet klientit.

Kështu, klienti mund të lexojë ndryshimet e veta, ndërsa klientët e tjerë nuk i shohin këto ndryshime, sepse ato ruhen vetëm në memorien e koordinatores, në nodet e Cassandra ato ende nuk ekzistojnë.

NewSQL = NoSQL+ACID

Kur klienti dërgon një commit, gjendja, e cila kishte qenë në memorie te shërbimi, ruhet nga koordinatori në logged batch, dhe tashmë në formën e logged batch dërgohet në depot e Cassandra. Depot bëjnë të gjitha nevojat që ky paketë të aplikohet atomikisht (plotësisht) dhe i kthejnë përgjigje koordinatores, e cila pastaj çliron bllokimet dhe konfirmon suksesin e transaksionit tek klienti.

NewSQL = NoSQL+ACID

Dhe për të anuluar, koordinatori ka nevojë vetëm të çlirojë memorjen e zënë nga gjendja e transaksionit.

Si rezultat i përmirësimeve të përshkruara më sipër, ne realizuam parimet ACID:

  • AtomikĂ«sia. Kjo Ă«shtĂ« garancia se asnjĂ« transaksion nuk do tĂ« regjistrohet nĂ« sistem pjesĂ«risht, do tĂ« zbatohet ose tĂ« gjitha nĂ«noperacionet e tij, ose asnjĂ«ra. Ky princip respektohet nga logged batch nĂ« Cassandra.
  • Konsistenca. Çdo transaksion i suksesshĂ«m, nga definicioni, regjistron vetĂ«m rezultate tĂ« lejueshme. NĂ«se pas hapjes sĂ« transaksionit dhe kryerjes sĂ« disa operacioneve zbulohet se rezultati nuk Ă«shtĂ« i lejueshĂ«m, herĂ« pas here bĂ«het njĂ« anulim.
  • Izolimi. Kur gjatĂ« kryerjes sĂ« transaksionit, transaksionet paralele nuk duhet tĂ« ndikojnĂ« nĂ« rezultatin e tij. Transaksionet konkurruese janĂ« izoluar me anĂ« tĂ« bllokimeve pesimiste nĂ« koordinatori. PĂ«r leximet jashtĂ« transaksionit, respektohet principi i izolimit nĂ« nivelin Read Committed.
  • QĂ«ndrueshmĂ«ria. PavarĂ«sisht problemeve nĂ« nivelet e poshtme — ndalimi i sistemit, dĂ«shtimi i pajisjeve — ndryshimet e bĂ«ra nga njĂ« transaksion i pĂ«rfunduar me sukses duhet tĂ« mbeten tĂ« ruajtura pas rinisjes sĂ« funksionimit.

Leximi përmes indekseve

Le të marrim një tabelë të thjeshtë:

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

)

Ajo ka njĂ« ID (çelĂ«si primar), pronar dhe datĂ«n e modificimit. Ne duhet tĂ« bĂ«jmĂ« njĂ« kĂ«rkesĂ« shumĂ« tĂ« thjeshtĂ« — tĂ« zgjedhim tĂ« dhĂ«nat sipas pronarit me datĂ«n e modifikimit "nĂ« 24 orĂ«t e fundit".

SELECT *
WHERE owner=?
AND modified>?

Për të bërë një kërkesë të tillë të punojë shpejt, në një DBMS klasik SQL, duhet të ndërtojmë një indeks sipas kolonave (owner, modified). Kështu mund ta bëjmë mjaft lehtë, pasi tani kemi garanci ACID!

Indekset në C*One

Ka një tabelë burimore me fotografi, ku ID e regjistrimit është çelësi primar.

NewSQL = NoSQL+ACID

PĂ«r indeksin, C*One krijon njĂ« tabelĂ« tĂ« re, e cila Ă«shtĂ« njĂ« kopje e tĂ« dhĂ«nave burimore. ÇelĂ«si pĂ«rputhet me shprehjen e indeksit, duke pĂ«rfshirĂ« gjithashtu çelĂ«sin primar tĂ« regjistrit nga tabela burimore:

NewSQL = NoSQL+ACID

Tani, kërkesa për "pronarin në 24 orët e fundit" mund të riformulohet si një kërkesë nga tabela tjetër:

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

Konsistenca e të dhënave në tabelën burimore photos dhe indeksi i1 mbahen automatikisht nga koordinatori. Bazuar vetëm në skemën e të dhënave, kur merr një ndryshim, koordinatori gjeneron dhe ruan jo vetëm ndryshimin e tabelës kryesore, por edhe ndryshimet e kopjave. Nuk kërkohen veprime të tjera me tabelën e indeksit, logjet nuk lexohen, bllokimet nuk përdoren. Kështu që shtimi i indekseve nuk konsumon pothuajse burime dhe nuk ndikon në shpejtësinë e aplikimit të modifikimeve.

Me ndihmën e ACID, arritëm të realizojmë indekse "si në SQL". Ato kanë konsistencë, mund të shkallëzohen, punojnë shpejt, mund të jenë të përbërë dhe të integruar në gjuhën e kërkesave CQL. Për mbështetjen e indekseve nuk është e nevojshme të bëhen ndryshime në kodin aplikativ. E gjithë kjo është e thjeshtë, si në SQL. Dhe ajo që është më e rëndësishmja, indekset nuk ndikojnë në shpejtësinë e ekzekutimit të modifikimeve në tabelën burimore të transaksioneve.

ÇfarĂ« rezultati u arrit

Ne zhvilluam C*One tre vjet më parë dhe e vendosëm në prodhim.

ÇfarĂ« arritĂ«m nĂ« fund? Le tĂ« e vlerĂ«sojmĂ« kĂ«tĂ« nĂ« shembullin e nĂ«nsistemit tĂ« pĂ«rpunimit dhe ruajtjes sĂ« fotografive, njĂ« nga llojet mĂ« tĂ« rĂ«ndĂ«sishme tĂ« tĂ« dhĂ«nave nĂ« rrjetet sociale. Nuk bĂ«het fjalĂ« pĂ«r vetĂ« trupat e fotografive, por pĂ«r tĂ« gjithĂ« metainformacionin. Aktualisht, nĂ« "Odnoklassniki" ka rreth 20 miliardĂ« tĂ« tilla regjistrime, sistemi pĂ«rpunon 80 mijĂ« kĂ«rkesa leximi nĂ« sekondĂ«, deri nĂ« 8 mijĂ« ACID-transaksione nĂ« sekondĂ«, tĂ« lidhura me modifikimin e tĂ« dhĂ«nave.

Kur ne përdorëm SQL me faktor replikimi = 1 (por në RAID 10), metainformacioni i fotografive ruhej në një grup me akses të lartë prej 32 makinash me Microsoft SQL Server (plus 11 të rezervuar). Gjithashtu, u rezervuan 10 servera për ruajtjen e backup-eve. Në total, 50 makina të shtrenjta. Në të njëjtën kohë, sistemi punonte me ngarkesë nominale, pa rezervë.

Pas migrimit nĂ« sistemin e ri, ne morĂ«m faktor replikimi = 3 — njĂ« kopje nĂ« çdo qendĂ«r tĂ« tĂ« dhĂ«nave. Sistemi pĂ«rbĂ«het nga 63 nodet e ruajtjes Cassandra dhe 6 makina koordinatori, nĂ« total 69 servera. Por kĂ«to makina janĂ« shumĂ« mĂ« tĂ« lira, kostoja e tyre e pĂ«rgjithshme pĂ«rbĂ«n rreth 30 % tĂ« kostos sĂ« sistemit nĂ« SQL. NĂ« tĂ« njĂ«jtĂ«n kohĂ«, ngarkesa qĂ«ndron nĂ« nivelin 30 %.

Me implementimin e C*One vonesat gjithashtu u ulĂ«n: nĂ« SQL, operacioni i shkrimit zgjaste rreth 4.5 ms. NĂ« C*One — rreth 1.6 ms. KohĂ«zgjatja e njĂ« transaksioni, nĂ« mesatare, Ă«shtĂ« mĂ« pak se 40 ms, komiti kryhet brenda 2 ms, kohĂ«zgjatja e leximit dhe shkrimit — nĂ« mesatare 2 ms. 99-i percentile Ă«shtĂ« vetĂ«m 3-3.1 ms, numri i kohĂ«ve tĂ« kaluar Ă«shtĂ« ulur 100 herĂ« — gjithçka pĂ«r shkak tĂ« pĂ«rdorimit tĂ« gjerĂ« tĂ« spekulacionit.

Derisa në këtë moment, pjesa e madhe e nodëve SQL Server është hequr nga funksionimi, produktet e reja zhvillohen vetëm me përdorimin e C*One. Ne e kemi adaptuar C*One për të punuar në re one-cloud, e cila ka lejuar përshpejtimin e krijimit të grupeve të reja, thjeshtimin e konfigurimit dhe automatizimin e operimit. Pa kodin burimor, do të ishte shumë më e vështirë dhe më e komplikuar.

Aktualisht, ne po punojmĂ« pĂ«r tĂ« transferuar ruajtjet tona tĂ« tjera nĂ« re — por kjo Ă«shtĂ« njĂ« histori krejtĂ«sisht tjetĂ«r.

Burimi: habr.com

Bleni hostim tĂ« besueshĂ«m pĂ«r faqe me mbrojtje nga DDoS, serverĂ« VPS VDS đŸ”„ Bleni hostim tĂ« besueshĂ«m pĂ«r faqe me mbrojtje nga DDoS, serverĂ« VPS VDS | ProHoster