Përshëndetje të gjithëve. Jam Vladislav Rodin. Aktualisht po ofroj kurse në portalin OTUS të dedikuara për arkitekturën e softuerit dhe arkitekturën e softuerit të ekspozuar ndaj ngarkesave të larta. Në prag të fillimit të një grupi të ri kursi vendosa të shkruaj një material të vogël autorësor, të cilin do të doja ta ndaj me ju.

Hyrje
Për shkak se HDD mund të kryejë vetëm rreth 400-700 operacione në sekondë (çfarë është e papara për rps tipike që bien mbi një sistem të ngarkuar), një bazë të dhënash diskore klasike është një ngushticë e arkitekturës. Prandaj, është e nevojshme të kushtoni vëmendje të veçantë modeleve të shkallëzimit të këtij depoziti.
Në momentin e tanishëm, ekzistojnë 2 modele shkallëzimi të bazës së të dhënave: replikimi dhe shardimi. Shardimi lejon shkallëzimin e operacionit të regjistrimit dhe, si pasojë, ul rps të regjistrimit që i përket një serveri në klasterin tuaj. Replikimi lejon të bëjë të njëjtën gjë, por me operacionet e leximit. Ky model i dedikohet pikërisht këtij artikulli.
Replikimi
Nëse shikoni replikimin nga një nivel shumë të lartë, është një gjë e thjeshtë: kishte një server, në të kishin të dhëna dhe pastaj ky server nuk mund të përballonte ngarkesën e leximit të këtyre të dhënave. Shtoni disa serverë të tjerë, synchronizoni të dhënat në të gjithë serverët dhe përdoruesi mund të lexojë nga çdo server i klasterit tuaj.
Pavarësisht thjeshtësisë së dukshme, ekzistojnë disa opsione për klasifikimin e realizimeve të ndryshme të këtij skenari:
- Sipas roleve në klaster (master-master ose master-slave)
- Sipas objekteve të dërguara (row-based, statement-based ose mixed)
- Sipas mekanizmit të sinkronizimit të nodave
Sot do të shqyrtojmë pikën e tretë.
Si ndodh komitimi i transaksionit
Kjo temë nuk ka lidhje të drejtpërdrejtë me replikimin, për të mund të shkruhet një artikull të veçantë, megjithatë, pasi pa kuptimin e mekanizmit të komitimit të transaksionit leximet e mëtejshme janë të padobishme, lejojeni vetes të kujtoni gjërat më themelore. Komitimi i transaksionit ndodh në 3 faza:
- Shënimi i transaksionit në regjistrin e bazës së të dhënave.
- Zbatimi i transaksionit në motorin e bazës së të dhënave.
- Kthimi i konfirmimit te klienti për zbatimin e suksesshëm të transaksionit.
Në bazat e ndryshme, në këtë algoritëm mund të lindin nuanca: për shembull, në motorin InnoDB të bazës MySQL, ka 2 regjistra: një për replikimin (binary log) dhe tjetri për mbajtjen e ACID (undo/redolog), ndërsa në PostgreSQL ka një regjistër që kryen të dy funksionet (write ahead log = WAL). Por ajo që është paraqitur është pikërisht koncepti i përgjithshëm që lejon të injorohen këto nuanca.
Replikimi sinkron (sync)
Le të shtojmë në algoritmin e komitimit të transaksioneve logjikën për replikimin e ndryshimeve të marra:
- Shënimi i transaksionit në regjistrin e bazës së të dhënave.
- Zbatimi i transaksionit në motorin e bazës së të dhënave.
- Dërgimi i të dhënave në të gjitha replikat.
- Marrja e konfirmimit nga të gjitha replikat për ekzekutimin e transaksionit mbi to.
- Kthimi i konfirmimit te klienti për zbatimin e suksesshëm të transaksionit.
Me këtë qasje, ne kemi disa disavantazhe:
- klienti pret që ndryshimet të aplikohen në të gjitha replikat.
- me rritjen e numrit të nyjeve në klaster, ne zvogëlojmë mundësinë që operacioni i shkruar të përfundojë me sukses.
Nëse me pikën 1 është më shumë ose më pak e qartë, arsyet e pikës 2 meritojnë shpjegim. Nëse me replikimin sinkron ne nuk marrim një përgjigje nga ndonjë nyje, ne përgjysmojmë transaksionin. Kështu, duke rritur numrin e nyjeve në klaster, rritni mundësinë që operacioni i shkruar të dështojë.
A mund të presim konfirmimin vetëm nga një pjesë e nyjeve, për shembull, nga 51% (kuorum)? Po, mundemi, megjithatë në versionin klasik kërkohet konfirmimi nga të gjitha nyjet, pasi vetëm kështu mund të sigurojmë konsistencën e plotë të të dhënave në klaster, që është padyshim një avantazh i këtij lloji replikimi.
Replikimi asinkron (async)
Le të modifikojmë algoritmin e mëparshëm. Të dhënat në replikat do t'i dërgojmë "ndonjëherë më pas", dhe "ndonjëherë më pas" ndryshimet do të aplikohen në replikat:
- Shënimi i transaksionit në regjistrin e bazës së të dhënave.
- Zbatimi i transaksionit në motorin e bazës së të dhënave.
- Kthimi i konfirmimit te klienti për zbatimin e suksesshëm të transaksionit.
- Dërgimi i të dhënave në replikat dhe aplikimi i ndryshimeve nga ato.
Kjo qasje çon në atë që klasteri punon shpejt, sepse ne nuk e mbajmë klientin në pritje për sa kohë që të dhënat të arrijnë te replikat dhe të bëhen commit.
Por kushti i dërgimit të të dhënave në replikat "ndonjëherë më pas" mund të çojë në humbjen e transaksionit, madje të humbjes së një transaksioni të konfirmuar nga përdoruesi, sepse nëse të dhënat nuk arrijnë të replikohen në kohë, konfirmimi i dërguar klientit për suksesin e operacionit është bërë, dhe në nyjën që mori ndryshimet, disku HDD dështoi, ne humbasim transaksionin, që mund të shkaktojë pasoja shumë të pakëndshme.
Replikimi polusinkron (semisync)
Më në fund kemi arritur tek replikimi semi-sinkron. Ky tip replikimi nuk është shumë i njohur dhe nuk është shumë i përhapur, megjithatë përbën një interes të madh, pasi mund të kombinojë përfitimet e replikimit si sinkron ashtu edhe asinkron.
Të përpiqemi të bashkojmë dy qasje të mëparshme. Nuk do ta mbajmë klientin për një kohë të gjatë, por do të kërkojmë që të dhënat të replikohen:
- Shënimi i transaksionit në regjistrin e bazës së të dhënave.
- Zbatimi i transaksionit në motorin e bazës së të dhënave.
- Dërgimi i të dhënave në replikat.
- Marrja e konfirmimit nga replika për pranimin e ndryshimeve (ato do të aplikohen "ndonjëherë më vonë").
- Kthimi i konfirmimit te klienti për zbatimin e suksesshëm të transaksionit.
Vini re që me këtë algoritëm, humbja e transaksionit ndodh vetëm në rastin e rënies së të dyja nodave, asaj që pranon ndryshimet dhe asaj replikë. Probabiliteti i një të tillë dështimi konsiderohet i vogël dhe këto rreziqe pranohen.
Por me këtë qasje, ekziston rreziku i leximeve fantazmë. Imagjinoni skenarin e mëposhtëm: në hapin 4 nuk morëm konfirmim nga asnjë replikë. Duhet ta anulojmë këtë transaksion, por nuk i kthejmë konfirmimin klientit. Ndërsa të dhënat u aplikuan në hapin 2, midis përfundimit të hapit 2 dhe anulimit të transaksionit krijohet një diferencë kohore, në të cilën transaksionet paralele mund të shohin ato ndryshime që nuk duhet të ishin në bazë.
Replikimi Lose-less semi-sinkron
Nëse mendoni pak, thjesht duke ndërruar hapat e algoritmit, mund të zgjidhni problemin e leximeve fantazmë në këtë skenar:
- Shënimi i transaksionit në regjistrin e bazës së të dhënave.
- Dërgimi i të dhënave të replikës.
- Marrja e konfirmimit nga replika për pranimin e ndryshimeve (ato do të aplikohen "ndonjëherë më vonë").
- Zbatimi i transaksionit në motorin e bazës së të dhënave.
- Kthimi i konfirmimit te klienti për zbatimin e suksesshëm të transaksionit.
Tani ne i konfirmojmë ndryshimet vetëm nëse ato janë replikuar.
Përfundimi
Ashtu siç ndodh gjithmonë, nuk ka zgjidhje ideale, por ka një grup zgjidhjesh, secila prej të cilave ka përfitimet dhe disavantazhet e saj dhe përshtatet për të zgjidhur klasa të ndryshme problemesh. Kjo është gjithashtu e vërtetë për përzgjedhjen e mekanizmit të sinkronizimit të të dhënave të bazës së dhënash të replikuar. Grupi i përfitimeve që ka replikimi semi-sinkron është mjaft i rëndësishëm dhe interesant për tu njohur si i merituar për vëmendje, pavarësisht nga përhapja e tij e vogël.
Po, kjo është gjithçka. Të shihemi në !
Burimi: habr.com
