PĂ«rshĂ«ndetje, unĂ« krijoj aplikacione pĂ«r DBMS. â Ă«shtĂ« njĂ« platformĂ« e zhvilluar nĂ« Mail.ru Group, qĂ« kombinon njĂ« DBMS me performancĂ« tĂ« lartĂ« dhe njĂ« server aplikacionesh nĂ« gjuhĂ«n Lua. ShpejtĂ«sia e zgjidhjeve tĂ« bazuara nĂ« Tarantool arrihet nĂ« veçanti pĂ«rmes mbĂ«shtetjes pĂ«r modalitetin in-memory tĂ« DBMS dhe mundĂ«sisĂ« pĂ«r tĂ« ekzekutuar logjikĂ«n e biznesit tĂ« aplikacionit nĂ« tĂ« njĂ«jtin hapĂ«sirĂ« adresash me tĂ« dhĂ«nat. NĂ« tĂ« njĂ«jtĂ«n kohĂ«, sigurohet qĂ«ndrueshmĂ«ria e tĂ« dhĂ«nave duke pĂ«rdorur transaksione ACID (me njĂ« regjistĂ«r WAL tĂ« mbajtur nĂ« disk). Tarantool ka mbĂ«shtetje tĂ« integruar pĂ«r replikimin dhe sharding. Duke filluar nga versioni 2.1, mbĂ«shteten pyetje nĂ« gjuhĂ«n SQL. Tarantool ka kod tĂ« hapur dhe shpĂ«rndahet nĂ«n licencĂ«n Simplified BSD. gjithashtu ka njĂ« version Enterprise komercial.

Ndjeni fuqinĂ«! (âŠaka shijoni performancĂ«n)
Të gjitha këto e bëjnë Tarantool një platformë tërheqëse për krijimin e aplikacioneve me ngarkesë të lartë që punojnë me DB. Në këto aplikacione shpesh lind nevoja për replikimin e të dhënave.
Siç u tha më parë, në Tarantool ka replikim të integruar të të dhënave. Principi i saj të funksionojë është ekzekutimi sekondar i të gjitha transaksioneve që gjenden në regjistrin e masterit (WAL) në replika. Zakonisht ky replikim (i cili do ta quajmë më tej replikimi në nivel të ulët) përdoret për të siguruar qëndrueshmërinë e aplikacionit dhe/ose për të shpërndarë ngarkesën e leximit midis nyjeve të klasterit.

Fig. 1. Replikimi brenda klasterit
NjĂ« shembull i njĂ« skenari alternativ mund tĂ« jetĂ« kalimi i tĂ« dhĂ«nave tĂ« krijuara nĂ« njĂ« DB nĂ« njĂ« tjetĂ«r DB pĂ«r pĂ«rpunim/monitorim. NĂ« kĂ«tĂ« rast, njĂ« zgjidhje mĂ« e pĂ«rshtatshme mund tĂ« jetĂ« pĂ«rdorimi i replikimit nĂ« nivel tĂ« lartĂ« â replikimi i tĂ« dhĂ«nave nĂ« nivelin e logjikĂ«s sĂ« biznesit tĂ« aplikacionit. Pra, ne nuk pĂ«rdorim njĂ« zgjidhje tĂ« gatshme, tĂ« integruar nĂ« DBMS, por e realizojmĂ« vetĂ« replikimin brenda aplikacionit qĂ« po zhvillojmĂ«. Ky qĂ«ndrim ka si pĂ«rparĂ«si ashtu edhe dobĂ«si. Le tĂ« pĂ«rmendim pĂ«rfitimet.
1. Kursimi i trafikut:
- mund të dërgoni jo të gjitha të dhënat, por vetëm një pjesë të tyre (p.sh., mund të dërgoni vetëm disa tabela, disa nga kolonat e tyre ose rekordet që përputhen me një kriter të caktuar);
- ndryshe nga replikimi nĂ« nivel tĂ« ulĂ«t, i cili ekzekutohet vazhdimisht nĂ« mĂ«nyrĂ« asinkrone (i realizuar nĂ« versionin aktual Tarantool â 1.10) ose sinkrone (do tĂ« realizohet nĂ« versionet e ardhshme tĂ« Tarantool), replikimi nĂ« nivel tĂ« lartĂ« mund tĂ« bĂ«het nĂ« seanca (pra, aplikacioni fillimisht bĂ«n sinkronizimin e tĂ« dhĂ«nave â njĂ« seancĂ« shkĂ«mbimi, pastaj ndodh njĂ« pushim nĂ« replikim, pas tĂ« cilit ndodh seanca tjetĂ«r e shkĂ«mbimit, etj.);
- nëse një regjistrim është ndryshuar disa herë, mund të dorëzohet vetëm versioni i tij më i fundit (ndryshe nga replikimi në nivel të ulët, ku në replika do të ndodhin radhazi të gjitha ndryshimet e bëra në master).
2. Nuk ka vështirësi në realizimin e shkëmbimit përmes HTTP, që lejon sinkronizimin e DB të largëta.

Fig. 2. Replikimi përmes HTTP
3. Strukturat e DB, midis të cilave dërgohen të dhënat, nuk është e nevojshme të jenë të njëjta (përkundrazi, në rastin e përgjithshëm është e mundur madje të përdoren DB të ndryshme, gjuhë programimi, platforma etj.).

Fig. 3. Replikimi në sisteme heterogjene
Minusi qëndron në faktin se, mesatarisht, programimi është më i ndërlikuar/kostoshëm se konfigurimi, dhe në vend të konfigurimit të funksionalitetit të integruar do të duhet të realizoni të tuajin.
Nëse në situatën tuaj përfitimet e përmendura kanë rëndësi të vendosur (ose janë një kusht i nevojshëm), atëherë ka kuptim të përdoret replikimi në nivel të lartë. Le të shqyrtojmë disa mënyra për të realizuar replikimin në nivel të lartë të të dhënave në DBMS Tarantool.
Minimizimi i trafikut
Pra, një nga përfitimet e replikimit në nivel të lartë është kursimi i trafikut. Për të shfaqur këtë përfitim në mënyrë të plotë, është e nevojshme të minimizohet numri i të dhënave të dërguara në çdo seancë shkëmbimi. Sigurisht, në këtë rast nuk duhet harruar se në fund të seancës, marrësi i të dhënave duhet të jetë i sinkronizuar me burimin (së paku për atë pjesë të të dhënave që marrë pjesë në replikim).
Si mund tĂ« minimizohet numri i tĂ« dhĂ«nave tĂ« dĂ«rguara gjatĂ« replikimit nĂ« nivel tĂ« lartĂ«? NjĂ« zgjidhje "nĂ« kokĂ«" mund tĂ« jetĂ« pĂ«rzgjedhja e tĂ« dhĂ«nave sipas datĂ«s-kohe. PĂ«r kĂ«tĂ« mund tĂ« pĂ«rdoret njĂ« fushĂ« datĂ«-kohe qĂ« tashmĂ« ekziston nĂ« tabelĂ« (nĂ«se ajo ekziston). PĂ«r shembull, dokumenti "porosi" mund tĂ« ketĂ« njĂ« fushĂ« "koha e kĂ«rkuar pĂ«r pĂ«rfundimin e porosisĂ«" â delivery_time. Problemi i kĂ«tij zgjidhjeje qĂ«ndron nĂ« faktin se vlerat nĂ« kĂ«tĂ« fushĂ« nuk janĂ« tĂ« detyruara tĂ« jenĂ« nĂ« njĂ« renditje qĂ« i pĂ«rgjigjet krijimit tĂ« porosive. Pra, ne nuk mund tĂ« mbajmĂ« mend vlerĂ«n maksimale tĂ« fushĂ«s delivery_time, e cila u kalua nĂ« seancĂ«n e mĂ«parshme tĂ« shkĂ«mbimit, dhe nĂ« seancĂ«n e ardhshme tĂ« shkĂ«mbimit, tĂ« filtrojmĂ« tĂ« gjitha regjistrimet me njĂ« vlerĂ« mĂ« tĂ« lartĂ« tĂ« fushĂ«s delivery_time. GjatĂ« shkĂ«mbimeve mund tĂ« kenĂ« shtuar regjistrime me njĂ« vlerĂ« mĂ« tĂ« vogĂ«l tĂ« fushĂ«s delivery_time. Gjithashtu, porosia mund tĂ« ketĂ« pĂ«suar ndryshime, tĂ« cilat megjithatĂ« nuk kanĂ« prekur fushĂ«n delivery_time. NĂ« tĂ« dyja rastet, ndryshimet nuk do tĂ« dĂ«rgohen nga burimi nĂ« marrĂ«s. PĂ«r tĂ« zgjidhur kĂ«to probleme, na nevojitet tĂ« dĂ«rgojmĂ« tĂ« dhĂ«na "nĂ« mbivendosje". Kjo Ă«shtĂ«, nĂ« çdo seancĂ« shkĂ«mbimi do tĂ« dĂ«rgojmĂ« tĂ« gjitha tĂ« dhĂ«nat me vlerĂ«n e fushĂ«s delivery_time, e cila tejkalon njĂ« moment tĂ« caktuar nĂ« tĂ« shkuarĂ«n (p.sh., N orĂ« nga momenti aktual). MegjithatĂ«, Ă«shtĂ« e qartĂ« se pĂ«r sistemet e mĂ«dha, ky qasje Ă«shtĂ« shumĂ« e tepruar dhe mund ta anulojĂ« kursimin e trafikut pĂ«r tĂ« cilin po synojmĂ«. PĂ«r mĂ« tepĂ«r, tabela e dĂ«rguar mund tĂ« mos ketĂ« njĂ« fushĂ« tĂ« lidhur me datĂ«n dhe orĂ«n.
Një zgjidhje tjetër, më e ndërlikuar nga pikëpamja e zbatimit, është konfirmimi i marrjes së të dhënave. Në këtë rast, në çdo seancë shkëmbimi dërgohen të gjitha të dhënat që nuk janë konfirmuar nga marrësi. Për zbatimin e saj, do të nevojitet të shtohet një kolone booleane në tabelën burimore (p.sh., is_transferred). Nëse marrësi konfirmon marrjen e regjistrimit, fusha përkatëse merr vlerën e vërtetë, pas së cilës regjistrimi më nuk merr pjesë në shkëmbimet. Ky variant zbatimi ka këto disavantazhe. Së pari, për çdo regjistrim të dërguar është e nevojshme të gjenerohet dhe dërgohet një konfirmim. Në terma të thjeshtë, kjo mund të përkojnë me dyfishimin e sasisë së të dhënave që dërgohen dhe të sjellë dyfishimin e numrit të rrethrrotullimeve. Së dyti, nuk ekziston mundësia e dërgimit të të njëjtit regjistrim në disa marrës (marrësi i parë që merr do të konfirmojë marrjen për vete dhe të gjithë të tjerët).
Një mënyrë që nuk ka disavantazhet e përmendura më sipër, konsiston në shtimin e një kolone në tabelën e dërguar për ndjekjen e ndryshimeve të rreshtave të saj. Kjo kolone mund të ketë tipin datum-orë dhe duhet të përcaktohet/updated nga aplikacioni në kohën aktuale çdo herë kur regjistrohen/ndryshohen regjistrime (atomikisht me shtimin/ndryshimin). Si një shembull, quajmë kolonen update_time. Duke ruajtur vlerën maksimale të fushës së kësaj kolone për regjistrimet e dërguara, mund të fillojmë seancën e ardhshme të shkëmbimit nga kjo vlerë (të filtrojmë regjistrimet me vlerën e fushës update_time, e cila tejkalon vlerën e ruajtur më parë). Problemi që lidhet me këtë qasje të fundit është se ndryshimet e të dhënave mund të ndodhin në mënyrë grupore. Si rezultat, vlerat e fushave në kolonen update_time mund të mos jenë unike. Pra, kjo kolone nuk mund të përdoret për dhënien e të dhënave në mënyrë të pjesshme (faqe pas faqe). Për dhënien e të dhënave në mënyrë të pjesshme do të nevojitet të shpikim mekanizma të tjerë, të cilat me shumë gjasa do të kenë efikasitet shumë të ulët (p.sh., nxjerrja nga DB e të gjitha regjistrimeve me vlerën update_time më të lartë se e caktuara dhe dhënia e një numri të caktuar të regjistrimeve, duke filluar nga një hapësirë e caktuar nga fillimi i zgjedhjes).
Mund të rritet efikasiteti i transmetimit të të dhënave, duke bërë disa përmirësime në qasjen e mëparshme. Për këtë, si vlera të fushave të kolones për ndjekjen e ndryshimeve do të përdorim një tip të numrit të plotë (numri i gjatë). Do ta quajmë kolonen row_ver. Vlera e fushës së kësaj kolone përsëri duhet të përcaktohet/updated çdo herë kur krijohet/ndryshohet një regjistrim. Por në këtë rast, fusha nuk do të marrë datën e tanishme dhe orën, por vlerën e një numri të caktuar, të rritur me një. Si rezultat, kolona row_ver do të përmbajë vlera unike dhe do të jetë në gjendje të përdoret jo vetëm për dhënien e "deltave" të të dhënave (të dhënat që janë shtuar/nndryshuar pas përfundimit të seancës së mëparshme të shkëmbimit), por edhe për ndarjen e thjeshtë dhe efikase në mënyrë të pjesëzuar.
Mënyra e fundit e propozuar për minimizimin e sasisë së të dhënave që dërgohen gjatë replikimit me nivel të lartë, më duket se është më optimalja dhe universale. Le ta shqyrtojmë atë më në detaje.
Transmetimi i të dhënave duke përdorur numrin e versioneve të rreshtave
Zbatimi i pjesës servero-master
NĂ« MS SQL Server pĂ«r zbatimin e njĂ« qasje tĂ« tillĂ« ekziston njĂ« tip i veçantĂ« kolone â rowversion. Ădo DB ka njĂ« numĂ«r qĂ« rritet me njĂ« çdo herĂ« qĂ« regjistrohet/ndryshohet njĂ« regjistrim nĂ« tabelĂ«, qĂ« ka njĂ« kolone tĂ« tipit rowversion. Vlera e kĂ«tij numĂ«ruesi i caktohet automatikisht fushĂ«s sĂ« kĂ«saj kolone nĂ« regjistrimin e shtuar/ndryshuar. DBMS Tarantool nuk ka njĂ« mekanizĂ«m tĂ« ngjashĂ«m tĂ« ndĂ«rtuar. MegjithatĂ«, nĂ« Tarantool Ă«shtĂ« e lehtĂ« ta realizosh kĂ«tĂ« manualisht. Le tĂ« shohim se si bĂ«het kjo.
Fillimisht, pak terminologji: tabelat nĂ« Tarantool quhen hapĂ«sira (space), dhe regjistrimet â turteza (tuple). NĂ« Tarantool mund tĂ« krijoni sekuenca (sequence). Sekuencat janĂ« asgjĂ« tjetĂ«r veçse gjeneratorĂ« tĂ« emĂ«rtuar tĂ« vlerave tĂ« renditura tĂ« numrave tĂ« plotĂ«. Kjo Ă«shtĂ« pikĂ«risht ajo qĂ« na nevojitet pĂ«r qĂ«llimet tona. MĂ« poshtĂ« do tĂ« krijojmĂ« njĂ« tĂ« tillĂ«.
Para se të kryeni ndonjë operacion me bazën e të dhënave në Tarantool, duhet të ekzekutoni komandën e mëposhtme:
box.cfg{}Si rezultat, Tarantool do të fillojë të regjistrojë në katalogun aktual skenarët e DB (snapshot) dhe ditarin e transaksioneve.
Krijojmë një sekuencë row_version:
box.schema.sequence.create('row_version',
{ if_not_exists = true }) Opcioni if_not_exists lejon ekzekutimin e skenarit të krijimit shumë herë: nëse objekti ekziston, Tarantool nuk do të përpiqet ta krijojë përsëri. Kjo mundësi do të përdoret në të gjitha komandat e mëvonshme DDL.
Krijojmë një hapësirë për shembull.
box.schema.space.create('goods', {
format = {
{
name = 'id',
type = 'unsigned'
},
{
name = 'name',
type = 'string'
},
{
name = 'code',
type = 'unsigned'
},
{
name = 'row_ver',
type = 'unsigned'
}
},
if_not_exists = true
}) Këtu kemi caktuar emrin e hapësirës (goods), emrat e fushave dhe tipet e tyre.
Fushat me auto-increment në Tarantool krijohen gjithashtu me ndihmën e sekuencave. Krijojmë një çelës primar auto-increment mbi fushën id:
box.schema.sequence.create('goods_id',
{ if_not_exists = true })
box.space.goods:create_index('primary', {
parts = { 'id' },
sequence = 'goods_id',
unique = true,
type = 'HASH',
if_not_exists = true
})Tarantool mbështet disa lloje indeksesh. Indekset e llojeve TREE dhe HASH janë më të përdorurat, që bazohen në strukturat përkatëse. TREE është tipi më universale i indeksit. Ai lejon nxjerrjen e të dhënave në formë të renditur. Por për zgjedhje të barazisë, HASH është më i përshtatshëm. Për pasojë, është e arsyeshme të përdorni HASH për çelësin primar (siç bëjmë ne).
Për të përdorur kolonën row_ver për të kaluar të dhënat e ndryshuara, është e nevojshme të lidhni me vlerat e sekuencës row_ver. Por ndryshe nga çelësi primar, vlera e fushës së kolonës row_ver duhet të rritet me njësi jo vetëm kur shtohen regjistrime të reja, por edhe kur ndryshohen ato ekzistuese. Për këtë mund të përdoren triggera. Në Tarantool ka dy lloje triggerash për hapësirat: before_replace dhe on_replace. Triggerat aktivizohen me çdo ndryshim të dhënash në hapësirë (për çdo turtezë të prekur nga ndryshimet, aktivizohet funksioni i trigger-it). Ndryshe nga on_replace, before_replace-triggerat lejojnë modifikimin e të dhënave të turtezës për të cilën ekzekutohet trigger-i. Për pasojë, na përshtatet tipi i fundit i trigger-ve.
box.space.goods:before_replace(function(old, new)
return box.tuple.new({new[1], new[2], new[3],
box.sequence.row_version:next()})
end) Triggeri i dhënë zëvendëson vlerën e fushës row_ver të turtezës së ruajtur me vlerën e ardhshme të sekuencës row_version.
Për të mundësuar nxjerrjen e të dhënave nga hapësira goods përmes kolonës row_ver, krijojmë një indeks:
box.space.goods:create_index('row_ver', {
parts = { 'row_ver' },
unique = true,
type = 'TREE',
if_not_exists = true
}) Tipi i indeksit â pemĂ« (TREE), pasi ne duhet tĂ« nxjerrim tĂ« dhĂ«nat nĂ« rendin nĂ« rritje tĂ« vlerave nĂ« kolonĂ«. row_ver.
Shtojmë disa të dhëna në hapësirë:
box.space.goods:insert{nil, 'pen', 123}
box.space.goods:insert{nil, 'pencil', 321}
box.space.goods:insert{nil, 'brush', 100}
box.space.goods:insert{nil, 'watercolour', 456}
box.space.goods:insert{nil, 'album', 101}
box.space.goods:insert{nil, 'notebook', 800}
box.space.goods:insert{nil, 'rubber', 531}
box.space.goods:insert{nil, 'ruler', 135} Duke qenĂ« se fusha e parĂ« Ă«shtĂ« njĂ« numĂ«rues auto-increment, ne kalojmĂ« nĂ« vend tĂ« saj nil. Tarantool e zĂ«vendĂ«son automatikisht me vlerĂ«n e ardhshme. Po ashtu, pĂ«r vlerat e fushave tĂ« kolonĂ«s row_ver mund tĂ« kalojmĂ« nil â ose tĂ« mos specifikojmĂ« fare vlerĂ«, pasi kjo kolonĂ« zĂ« pozicionin e fundit nĂ« hapĂ«sirĂ«.
Kontrollojmë rezultatin e futjes:
tarantool> box.space.goods:select()
---
- - [1, 'pen', 123, 1]
- [2, 'pencil', 321, 2]
- [3, 'brush', 100, 3]
- [4, 'watercolour', 456, 4]
- [5, 'album', 101, 5]
- [6, 'notebook', 800, 6]
- [7, 'rubber', 531, 7]
- [8, 'ruler', 135, 8]
... Siç shohim, fusha e parë dhe e fundit u plotësuan automatikisht. Tani do të jetë e lehtë të shkruhet një funksion për shkarkimin e ndryshimeve të hapësirës në faqe. goods:
local page_size = 5
local function get_goods(row_ver)
local index = box.space.goods.index.row_ver
local goods = {}
local counter = 0
for _, tuple in index:pairs(row_ver, {
iterator = 'GT' }) do
local obj = tuple:tomap({ names_only = true })
table.insert(goods, obj)
counter = counter + 1
if counter >= page_size then
break
end
end
return goods
end Funksioni merr si parametër vlerën row_ver, duke filluar nga e cila është e nevojshme për të realizuar shkarkimin e ndryshimeve dhe kthen një sasi të dhënash të ndryshuara.
Shkarkimi i të dhënave në Tarantool bëhet përmes indekseve. Funksioni get_goods përdor një iterator mbi indeksin row_ver për të marrë të dhënat e ndryshuara. Lloji i iteratorit është GT (Më i Madh Se). Kjo do të thotë se iteratori do të kryejë një kalim të renditur nëpër vlerat e indeksit duke filluar nga çelësi i dhënë (vlera e fushës row_ver).
Iteratori kthen tupla. Për të pasur mundësi për të transferuar të dhënat përmes HTTP, është e nevojshme të konvertohen tuple në një strukturë që është e përshtatshme për serializim të ardhshëm. Në shembullin e mëposhtëm përdoret funksioni standard tomap. Në vend të përdorimit tomap mund të shkruajmë një funksion të vetin. Për shembull, mund të duam të rinovojmë fushën emri, të mos e transferojmë fushën kode dhe të shtojmë fushën comment:
local function unflatten_goods(tuple)
local obj = {}
obj.id = tuple.id
obj.goods_name = tuple.name
obj.comment = 'disa komente'
obj.row_ver = tuple.row_ver
return obj
end Madhësia e faqeve të të dhënave të dhëna (numri i regjistrimeve në një porcion) përcaktohet nga variabli page_size. Në shembullin, vlera page_size është 5. Në një program real, madhësia e faqeve zakonisht ka një vlerë më të madhe. Kjo varet nga madhësia e mesme e tupleve të hapësirës. Madhësia optimale e faqeve mund të përcaktohet eksperimentalisht duke matur kohën e transferimit të të dhënave. Më shumë se kjo, më pak rrethra midis palës dërguese dhe pranuesi do të përmirësojnë kohën e përgjithshme të shkarkimit të ndryshimeve. Megjithatë, me një madhësi shumë të madhe të faqeve, do të ndalojmë shërbyesin për një kohë të gjatë duke serializuar përzgjedhjen. Si rezultat, mund të ketë vonesa në përpunimin e kërkesave të tjera që vijnë në server. Parametri page_size mund të ngarkohet nga skedari i konfigurimit. Për çdo hapësirë të transferuar mund të caktohet një vlerë e vetme. Në shumicën e rasteve, vlera e parazgjedhur (p.sh., 100) mund të jetë e përshtatshme.
Ekzekutojmë funksionin get_goods:
tarantool> get_goods(0)
---
- - row_ver: 1
code: 123
name: stilolaps
id: 1
- row_ver: 2
code: 321
name: laps
id: 2
- row_ver: 3
code: 100
name: brush
id: 3
- row_ver: 4
code: 456
name: akvarel
id: 4
- row_ver: 5
code: 101
name: album
id: 5
... Marrim vlerën e fushës row_ver nga rreshti i fundit dhe përsëri thërrasim funksionin:
tarantool> get_goods(5)
---
- - row_ver: 6
code: 800
name: notes
id: 6
- row_ver: 7
code: 531
name: goma
id: 7
- row_ver: 8
code: 135
name: metër
id: 8
...Dhe përsëri:
tarantool> get_goods(8)
---
- []
... Siç e shohim, me këtë përdorim, funksioni kthen të gjitha regjistrimet e hapësirës në mënyrë të faqeve. goodsPas faqes së fundit ndodhet një zgjedhje bosh.
Le të bëjmë disa ndryshime në hapësirë:
box.space.goods:update(4, {{'=', 6, 'libër'}})
box.space.goods:insert{nil, 'klip', 234}
box.space.goods:insert{nil, 'folder', 432} Ne kemi ndryshuar vlerën e fushës emri për një regjistrim dhe kemi shtuar dy regjistrime të reja.
Përsëritim thirrjen e fundit të funksionit:
tarantool> get_goods(8)
---
- - row_ver: 9
code: 800
name: libër
id: 6
- row_ver: 10
code: 234
name: klip
id: 9
- row_ver: 11
code: 432
name: folder
id: 10
... Funksioni ktheu regjistrimet e ndryshuara dhe të reja. Kështu, funksioni get_goods lejon që të merren të dhëna që janë ndryshuar që nga thirrja e fundit, e cila është baza e mënyrës së diskutuar të replikimit.
Do ta lëmë paraqitjen e rezultateve përmes HTTP në formë JSON jashtë këtij artikulli. Mund të lexoni për këtë këtu:
Zbatimi i klientit/anësorit
Le të shohim si duket zbatimi i anës që pranon. Do të krijojmë një hapësirë në anën që pranon për ruajtjen e të dhënave të ngarkuara:
box.schema.space.create('goods', {
format = {
{
name = 'id',
type = 'unsigned'
},
{
name = 'name',
type = 'string'
},
{
name = 'code',
type = 'unsigned'
}
},
if_not_exists = true
})
box.space.goods:create_index('primary', {
parts = { 'id' },
sequence = 'goods_id',
unique = true,
type = 'HASH',
if_not_exists = true
}) Struktura e hapësirës duket si struktura e hapësirës në burim. Por pasi ne nuk kemi ndërmend të transferojmë të dhënat e marra diku tjetër, kolona row_ver në hapësirën e pranuesit mungon. Në fushën id do të regjistrohen identifikuesit nga burimi. Prandaj, nuk ka nevojë për automatik rritës në anën e pranuesit.
Përveç kësaj, do të na nevojitet një hapësirë për ruajtjen e vlerave row_ver:
box.schema.space.create('row_ver', {
format = {
{
name = 'space_name',
type = 'string'
},
{
name = 'value',
type = 'string'
}
},
if_not_exists = true
})
box.space.row_ver:create_index('primary', {
parts = { 'space_name' },
unique = true,
type = 'HASH',
if_not_exists = true
}) Për çdo hapësirë që ngarkohet (fushën space_name) do të ruajmë këtu vlerën e fundit të ngarkuar row_ver (fushën vlera). Si çelës primar do të shërbejë kolona space_name.
Do të krijojmë një funksion për ngarkimin e të dhënave nga hapësira goods përmes HTTP. Për këtë do të na nevojitet një bibliotekë që implementon klientin HTTP. Rreshti i mëposhtëm ngarkon bibliotekën dhe krijon një instancë të klientit HTTP:
local http_client = require('http.client').new()Po ashtu, do të na nevojitet një bibliotekë për deserializimin e json:
local json = require('json')Kjo është e mjaftueshme për krijimin e një funksioni për ngarkimin e të dhënave:
funksioni lokal ngarko_të_dhenat(url, rreshti_ver)
lokal url = ('%s?rowVer=%s'):format(url,
tostring(rreshti_ver))
lokal trupi = nil
lokal të_dhenat = http_client:request('GET', url, trupi, {
mbajtja_e_gjallë_idlë = 1,
mbajtja_e_gjallë_interval = 1
})
kthe json.decode(të_dhenat.body)
end Funksioni kryen një kërkesë HTTP në adresën url, kalon aty row_ver si parametër dhe kthen rezultatin e deserializuar të kërkesës.
Funksioni për ruajtjen e të dhënave të marra duket si më poshtë:
funksioni lokal ruaj_baranet(baranet)
lokal n = #baranet
box.atomic(function()
për i = 1, n bëj
lokal obj = baranet[i]
box.space.baranet:put(
obj.id, obj.emri, obj.kodi)
end
end)
end Cikli për ruajtjen e të dhënave në hapësirë goods është vendosur në një transaksion (për këtë përdoret funksioni box.atomic) për të reduktuar numrin e operacioneve me diskun.
Në fund, funksioni i sinkronizimit të hapësirës lokale goods me burimin mund të realizohet kështu:
local function sync_goods()
local tuple = box.space.row_ver:get('goods')
local row_ver = tuple and tuple.value or 0
ââ set your url here:
local url = 'http://127.0.0.1:81/test/goods/list'
while true do
local goods = load_goods(url, row_ver)
local count = #goods
if count == 0 then
return
end
save_goods(goods)
row_ver = goods[count].rowVer
box.space.row_ver:put({'goods', row_ver})
end
end MĂ« parĂ« lexojmĂ« vlerĂ«n e ruajtur mĂ« parĂ« row_ver pĂ«r hapĂ«sirĂ«n goods. NĂ«se ajo mungon (seanca e parĂ« e shkĂ«mbimit), atĂ«herĂ« marrim si row_ver zero. MĂ« pas nĂ« cikĂ«l kryejmĂ« ngarkimin faqe pas faqeje tĂ« tĂ« dhĂ«nave tĂ« ndryshuara nga burimi pĂ«rmes adresĂ«s sĂ« dhĂ«nĂ« url. NĂ« çdo iterim ruajmĂ« tĂ« dhĂ«nat e marra nĂ« hapĂ«sirĂ«n pĂ«rkatĂ«se lokale dhe pĂ«rditĂ«sojmĂ« vlerĂ«n row_ver (nĂ« hapĂ«sirĂ« row_ver dhe nĂ« variable row_ver) â marrim vlerĂ«n row_ver nga rreshti i fundit tĂ« dhĂ«nash tĂ« ngarkuara.
Për të mbrojtur nga rastësia e përsëritjes (në rast të gabimit në program) cikli while mund të zëvendësohet me për:
për _ = 1, max_req bëj ... Si rezultat i ekzekutimit të funksionit sinkronizo_baranet hapësira goods në marrës do të përmbajë versionet e fundit të të gjitha regjistrimeve të hapësirës goods në burim.
E qartĂ«, se me kĂ«tĂ« mĂ«nyrĂ« nuk mund tĂ« transmetohet fshirja e tĂ« dhĂ«nave. NĂ«se ekziston njĂ« nevojĂ« e tillĂ«, mund tĂ« pĂ«rdoret njĂ« shĂ«nim pĂ«r fshirjen. ShtojmĂ« nĂ« hapĂ«sirĂ« goods njĂ« fushĂ« bool is_deleted dhe pĂ«rveç fshirjes fizike tĂ« regjistrimit pĂ«rdorim fshirje logjike â vendosim vlerĂ«n e fushĂ«s is_deleted nĂ« vlerĂ« e vĂ«rtetĂ«. NdonjĂ«herĂ« pĂ«rveç fushĂ«s bool is_deleted Ă«shtĂ« mĂ« e lehtĂ« tĂ« pĂ«rdoret fusha fshirĂ«, ku ruhen data-koha e fshirjes logjike tĂ« regjistrimit. Pas kryerjes sĂ« fshirjes logjike, regjistrimi i shĂ«nuar pĂ«r fshirje do tĂ« kalojĂ« nga burimi nĂ« marrĂ«s (sipas logjikĂ«s sĂ« shqyrtuar mĂ« lart).
Seanca row_ver mund të përdoret për të transmetuar të dhëna të hapësirave të tjera: nuk ka nevojë të krijohet një seancë e veçantë për çdo hapësirë të kaluar.
Ne shqyrtuam një mënyrë efektive të replikimit të të dhënave në nivel të lartë në aplikacionet që përdorin DBMS Tarantool.
Përfundimet
- DBMS Tarantool është një produkt tërheqës, me perspektivë për krijimin e aplikacioneve me ngarkesë të lartë.
- Replikimi i të dhënave në nivel të lartë ka disa përparësi krahasuar me replikimin në nivel të ulët.
- Mënyra e shqyrtuar në artikull për replikimin në nivel të lartë lejon të minimizojmë numrin e të dhënave të transmetuara duke dhënë vetëm ato regjistra që kanë ndryshuar pas seancës së fundit të shkëmbimit.
Burimi: habr.com
