Përshëndetje, unë jam duke krijuar aplikacione për DBMS
Ndjeni fuqinë! (…aka shijoni performancën)
Të gjitha sa më sipër e bëjnë Tarantool një platformë tërheqëse për krijimin e aplikacioneve me ngarkesë të lartë që punojnë me bazat e të dhënave. Në aplikacione të tilla, shpesh ka nevojë për riprodhimin e të dhënave.
Siç u përmend më lart, Tarantool ka riprodhimin e integruar të të dhënave. Parimi i funksionimit të tij është të ekzekutojë në mënyrë sekuenciale në kopje të gjitha transaksionet e përfshira në regjistrin kryesor (WAL). Zakonisht një përsëritje e tillë (ne do ta quajmë më tej nivel i ulët) përdoret për të siguruar tolerancën e gabimeve të aplikacionit dhe/ose për të shpërndarë ngarkesën e leximit midis nyjeve të grupimit.
Oriz. 1. Përsëritja brenda një grupi
Një shembull i një skenari alternativ do të ishte transferimi i të dhënave të krijuara në një bazë të dhënash në një bazë të dhënash tjetër për përpunim/monitorim. Në rastin e fundit, një zgjidhje më e përshtatshme mund të jetë përdorimi nivel të lartë replikimi - përsëritja e të dhënave në nivelin e logjikës së biznesit të aplikacionit. ato. Ne nuk përdorim një zgjidhje të gatshme të integruar në DBMS, por zbatojmë replikimin vetë brenda aplikacionit që po zhvillojmë. Kjo qasje ka si avantazhe ashtu edhe disavantazhe. Le të rendisim avantazhet.
1. Kursimi i trafikut:
- Ju nuk mund të transferoni të gjitha të dhënat, por vetëm një pjesë të tyre (për shembull, mund të transferoni vetëm disa tabela, disa nga kolonat ose regjistrimet e tyre që plotësojnë një kriter të caktuar);
- Ndryshe nga replikimi i nivelit të ulët, i cili kryhet vazhdimisht në mënyrë asinkrone (i zbatuar në versionin aktual të Tarantool - 1.10) ose sinkron (që do të zbatohet në versionet e mëvonshme të Tarantool), përsëritja e nivelit të lartë mund të kryhet në sesione (d.m.th. aplikacioni fillimisht sinkronizon të dhënat - të dhënat e sesionit të shkëmbimit, pastaj ka një pauzë në replikim, pas së cilës ndodh sesioni tjetër i shkëmbimit, etj.);
- nëse një rekord ka ndryshuar disa herë, mund të transferoni vetëm versionin e tij të fundit (ndryshe nga replikimi i nivelit të ulët, në të cilin të gjitha ndryshimet e bëra në master do të luhen në mënyrë sekuenciale në kopje).
2. Nuk ka vështirësi me zbatimin e shkëmbimit HTTP, i cili ju lejon të sinkronizoni bazat e të dhënave në distancë.
Oriz. 2. Replikimi mbi HTTP
3. Strukturat e bazës së të dhënave ndërmjet të cilave transferohen të dhënat nuk duhet të jenë të njëjta (për më tepër, në rastin e përgjithshëm, madje është e mundur të përdoren DBMS të ndryshme, gjuhë programimi, platforma etj.).
Oriz. 3. Replikimi në sisteme heterogjene
E keqja është se, mesatarisht, programimi është më i vështirë/i kushtueshëm sesa konfigurimi, dhe në vend që të personalizoni funksionalitetin e integruar, do t'ju duhet të zbatoni tuajin.
Nëse në situatën tuaj avantazhet e mësipërme janë vendimtare (ose janë një kusht i domosdoshëm), atëherë ka kuptim të përdorni përsëritje të nivelit të lartë. Le të shohim disa mënyra për të zbatuar përsëritjen e të dhënave të nivelit të lartë në Tarantool DBMS.
Minimizimi i trafikut
Pra, një nga avantazhet e përsëritjes së nivelit të lartë është kursimi i trafikut. Në mënyrë që ky avantazh të realizohet plotësisht, është e nevojshme të minimizohet sasia e të dhënave të transferuara gjatë çdo seance shkëmbimi. Natyrisht, nuk duhet të harrojmë se në fund të seancës, marrësi i të dhënave duhet të sinkronizohet me burimin (të paktën për atë pjesë të të dhënave që përfshihet në replikim).
Si të minimizoni sasinë e të dhënave të transferuara gjatë riprodhimit të nivelit të lartë? Një zgjidhje e drejtpërdrejtë mund të jetë zgjedhja e të dhënave sipas datës dhe orës. Për ta bërë këtë, mund të përdorni fushën datë-kohë që ekziston tashmë në tabelë (nëse ekziston). Për shembull, një dokument "urdhri" mund të ketë një fushë "koha e kërkuar e ekzekutimit të porosisë" - delivery_time
. Problemi me këtë zgjidhje është se vlerat në këtë fushë nuk duhet të jenë në sekuencën që korrespondon me krijimin e porosive. Pra, ne nuk mund të kujtojmë vlerën maksimale të fushës delivery_time
, të transmetuara gjatë seancës së mëparshme të shkëmbimit dhe gjatë seancës së ardhshme të shkëmbimit zgjidhni të gjitha regjistrimet me një vlerë më të lartë të fushës delivery_time
. Regjistrimet me një vlerë më të ulët të fushës mund të jenë shtuar ndërmjet sesioneve të shkëmbimit delivery_time
. Gjithashtu, rendi mund të kishte pësuar ndryshime, të cilat megjithatë nuk ndikuan në fushë delivery_time
. Në të dyja rastet, ndryshimet nuk do të transferohen nga burimi në destinacion. Për të zgjidhur këto probleme, do të na duhet të transferojmë të dhëna "të mbivendosura". ato. në çdo seancë shkëmbimi do të transferojmë të gjitha të dhënat me vlerën e fushës delivery_time
, duke tejkaluar një pikë në të kaluarën (për shembull, N orë nga momenti aktual). Megjithatë, është e qartë se për sistemet e mëdha kjo qasje është shumë e tepërt dhe mund të zvogëlojë në asgjë kursimet e trafikut që ne po përpiqemi. Përveç kësaj, tabela që transferohet mund të mos ketë një fushë të lidhur me një datë-orë.
Një zgjidhje tjetër, më komplekse për sa i përket zbatimit, është të pranoni marrjen e të dhënave. Në këtë rast, gjatë çdo seance shkëmbimi, transmetohen të gjitha të dhënat, marrja e të cilave nuk është konfirmuar nga marrësi. Për ta zbatuar këtë, do t'ju duhet të shtoni një kolonë Boolean në tabelën burimore (për shembull, is_transferred
). Nëse marrësi pranon marrjen e regjistrimit, fusha përkatëse merr vlerën true
, pas së cilës hyrja nuk përfshihet më në shkëmbime. Ky opsion i zbatimit ka disavantazhet e mëposhtme. Së pari, për çdo rekord të transferuar, duhet të krijohet dhe dërgohet një konfirmim. Përafërsisht, kjo mund të krahasohet me dyfishimin e sasisë së të dhënave të transferuara dhe duke çuar në dyfishimin e numrit të udhëtimeve vajtje-ardhje. Së dyti, nuk ekziston mundësia e dërgimit të të njëjtit rekord te disa marrës (marrësi i parë që merr do të konfirmojë marrjen për vete dhe për të gjithë të tjerët).
Një metodë që nuk ka disavantazhet e dhëna më sipër është shtimi i një kolone në tabelën e transferuar për të gjurmuar ndryshimet në rreshtat e saj. Një kolonë e tillë mund të jetë e llojit datë-kohë dhe duhet të vendoset/përditësohet nga aplikacioni në orën aktuale sa herë që shtohen/ndryshohen të dhënat (atomikisht me shtimin/ndryshimin). Si shembull, le ta quajmë kolonën update_time
. Duke ruajtur vlerën maksimale të fushës së kësaj kolone për të dhënat e transferuara, ne mund të fillojmë seancën e ardhshme të shkëmbimit me këtë vlerë (zgjidhni regjistrimet me vlerën e fushës update_time
, duke tejkaluar vlerën e ruajtur më parë). Problemi me qasjen e fundit është se ndryshimet e të dhënave mund të ndodhin në grupe. Si rezultat i vlerave të fushës në kolonë update_time
mund të mos jetë unike. Kështu, kjo kolonë nuk mund të përdoret për daljen e të dhënave të pjesëzuara (faqe pas faqe). Për të shfaqur të dhënat faqe pas faqeje, do t'ju duhet të shpikni mekanizma shtesë që me shumë mundësi do të kenë efikasitet shumë të ulët (për shembull, duke marrë nga baza e të dhënave të gjitha regjistrimet me vlerën update_time
më e lartë se një e dhënë dhe duke prodhuar një numër të caktuar regjistrimesh, duke filluar nga një kompensim i caktuar nga fillimi i mostrës).
Ju mund të përmirësoni efikasitetin e transferimit të të dhënave duke përmirësuar paksa qasjen e mëparshme. Për ta bërë këtë, ne do të përdorim llojin e numrit të plotë (integer long) si vlerat e fushës së kolonës për gjurmimin e ndryshimeve. Le të emërtojmë kolonën row_ver
. Vlera e fushës së kësaj kolone duhet ende të vendoset/përditësohet sa herë që krijohet/ndryshohet një rekord. Por në këtë rast, fushës nuk do t'i caktohet data-ora aktuale, por vlera e disa numëruesit, e rritur me një. Si rezultat, kolona row_ver
do të përmbajë vlera unike dhe mund të përdoret jo vetëm për të shfaqur të dhënat "delta" (të dhënat e shtuara/ndryshuara që nga fundi i sesionit të mëparshëm të shkëmbimit), por edhe për t'i zbërthyer thjesht dhe në mënyrë efektive në faqe.
Metoda e fundit e propozuar për të minimizuar sasinë e të dhënave të transferuara brenda kornizës së përsëritjes së nivelit të lartë më duket më optimale dhe universale. Le ta shohim më në detaje.
Kalimi i të dhënave duke përdorur një numërues të versioneve të rreshtave
Implementimi i pjesës server/master
Në MS SQL Server, ekziston një lloj kolone e veçantë për të zbatuar këtë qasje - rowversion
. Çdo bazë të dhënash ka një numërues që rritet me një sa herë që një rekord shtohet/ndryshohet në një tabelë që ka një kolonë si rowversion
. Vlera e këtij numëruesi caktohet automatikisht në fushën e kësaj kolone në rekordin e shtuar/ndryshuar. Tarantool DBMS nuk ka një mekanizëm të ngjashëm të integruar. Sidoqoftë, në Tarantool nuk është e vështirë ta zbatosh atë me dorë. Le të shohim se si bëhet kjo.
Së pari, pak terminologji: tabelat në Tarantool quhen hapësira, dhe regjistrimet quhen tuple. Në Tarantool mund të krijoni sekuenca. Sekuencat nuk janë gjë tjetër veçse gjeneratorë të emërtuar të vlerave të numrave të plotë të renditur. ato. kjo është pikërisht ajo që na nevojitet për qëllimet tona. Më poshtë do të krijojmë një sekuencë të tillë.
Para se të kryeni ndonjë operacion të bazës së të dhënave në Tarantool, duhet të ekzekutoni komandën e mëposhtme:
box.cfg{}
Si rezultat, Tarantool do të fillojë të shkruajë fotografitë e bazës së të dhënave dhe regjistrat e transaksioneve në drejtorinë aktuale.
Le të krijojmë një sekuencë row_version
:
box.schema.sequence.create('row_version',
{ if_not_exists = true })
Opsioni if_not_exists
lejon që skripti i krijimit të ekzekutohet disa herë: nëse objekti ekziston, Tarantool nuk do të përpiqet ta krijojë atë përsëri. Ky opsion do të përdoret në të gjitha komandat e mëvonshme DDL.
Le të krijojmë një hapësirë si 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 vendosim emrin e hapësirës (goods
), emrat e fushave dhe llojet e tyre.
Fushat me rritje automatike në Tarantool krijohen gjithashtu duke përdorur sekuenca. Le të krijojmë një çelës primar me rritje automatike sipas fushës 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 më të përdorura janë llojet TREE dhe HASH, të cilat bazohen në strukturat që korrespondojnë me emrin. TREE është lloji më i gjithanshëm i indeksit. Kjo ju lejon të rikuperoni të dhënat në një mënyrë të organizuar. Por për zgjedhjen e barazisë, HASH është më i përshtatshëm. Prandaj, është e këshillueshme që të përdoret HASH për çelësin primar (që është ajo që bëmë).
Për të përdorur kolonën row_ver
për të transferuar të dhëna të ndryshuara, duhet të lidhni vlerat e sekuencës në fushat e kësaj kolone row_ver
. Por ndryshe nga çelësi primar, vlera e fushës së kolonës row_ver
duhet të rritet me një jo vetëm kur shtoni regjistrime të reja, por edhe kur ndryshoni ato ekzistuese. Ju mund të përdorni shkas për këtë. Tarantool ka dy lloje shkasash hapësinorë: before_replace
и on_replace
. Aktivizuesit aktivizohen sa herë që të dhënat në hapësirë ndryshojnë (për çdo tufë të prekur nga ndryshimet, hapet një funksion aktivizues). Ndryshe nga on_replace
, before_replace
-triggers ju lejojnë të modifikoni të dhënat e tuples për të cilën ekzekutohet trigger. Prandaj, lloji i fundit i nxitësve na përshtatet.
box.space.goods:before_replace(function(old, new)
return box.tuple.new({new[1], new[2], new[3],
box.sequence.row_version:next()})
end)
Shkaku i mëposhtëm zëvendëson vlerën e fushës row_ver
tuple ruhet në vlerën tjetër të sekuencës row_version
.
Në mënyrë që të mund të nxjerrim të dhëna nga hapësira goods
sipas kolonës row_ver
, le të krijojmë një indeks:
box.space.goods:create_index('row_ver', {
parts = { 'row_ver' },
unique = true,
type = 'TREE',
if_not_exists = true
})
Lloji i indeksit - pema (TREE
), sepse do të na duhet të nxjerrim të dhënat në rend rritës të vlerave në kolonë row_ver
.
Le të 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}
Sepse Fusha e parë është një numërues automatik në rritje; në vend të kësaj kalojmë zero. Tarantool do të zëvendësojë automatikisht vlerën tjetër. Në mënyrë të ngjashme, si vlera e fushave të kolonës row_ver
ju mund të kaloni zero - ose të mos specifikoni fare vlerën, sepse kjo kolonë zë pozicionin e fundit në hapësirë.
Le të 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ç mund ta shihni, fushat e para dhe të fundit plotësohen automatikisht. Tani do të jetë e lehtë të shkruhet një funksion për ngarkimin faqe pas faqe të ndryshimeve të hapësirës 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 të shkarkohen ndryshimet, dhe kthen një pjesë të të dhënave të ndryshuara.
Mostrimi i të dhënave në Tarantool bëhet përmes indekseve. Funksioni get_goods
përdor një përsëritës sipas indeksit row_ver
për të marrë të dhëna të ndryshuara. Lloji i iteratorit është GT (Më i madh se, më i madh se). Kjo do të thotë që përsëritësi do të përshkojë në mënyrë sekuenciale vlerat e indeksit duke filluar nga çelësi i kaluar (vlera e fushës row_ver
).
Iteratori kthen tuples. Për të qenë në gjendje të transferoni të dhëna më pas përmes HTTP, është e nevojshme të konvertohen tuplet në një strukturë të përshtatshme për serializimin e mëvonshëm. Shembulli përdor funksionin standard për këtë tomap
. Në vend të përdorimit tomap
ju mund të shkruani funksionin tuaj. Për shembull, ne mund të dëshirojmë të riemërtojmë një fushë name
, mos e kaloni fushën code
dhe shtoni një fushë comment
:
local function unflatten_goods(tuple)
local obj = {}
obj.id = tuple.id
obj.goods_name = tuple.name
obj.comment = 'some comment'
obj.row_ver = tuple.row_ver
return obj
end
Madhësia e faqes së të dhënave dalëse (numri i regjistrimeve në një pjesë) përcaktohet nga ndryshorja page_size
. Në shembull vlera page_size
është 5. Në një program real, madhësia e faqes zakonisht ka më shumë rëndësi. Varet nga madhësia mesatare e tuples hapësinore. Madhësia optimale e faqes mund të përcaktohet në mënyrë empirike duke matur kohën e transferimit të të dhënave. Sa më e madhe të jetë madhësia e faqes, aq më i vogël është numri i udhëtimeve vajtje-ardhje midis anëve dërguese dhe pritëse. Në këtë mënyrë ju mund të zvogëloni kohën e përgjithshme për shkarkimin e ndryshimeve. Megjithatë, nëse madhësia e faqes është shumë e madhe, ne do të shpenzojmë shumë gjatë në server duke serializuar mostrën. 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 secilën hapësirë të transmetuar, mund të vendosni vlerën e vet. Megjithatë, për shumicën e hapësirave vlera e paracaktuar (për shembull, 100) mund të jetë e përshtatshme.
Le të ekzekutojmë funksionin get_goods
:
tarantool> get_goods(0)
---
- - row_ver: 1
code: 123
name: pen
id: 1
- row_ver: 2
code: 321
name: pencil
id: 2
- row_ver: 3
code: 100
name: brush
id: 3
- row_ver: 4
code: 456
name: watercolour
id: 4
- row_ver: 5
code: 101
name: album
id: 5
...
Le të marrim vlerën e fushës row_ver
nga rreshti i fundit dhe thirrni përsëri funksionin:
tarantool> get_goods(5)
---
- - row_ver: 6
code: 800
name: notebook
id: 6
- row_ver: 7
code: 531
name: rubber
id: 7
- row_ver: 8
code: 135
name: ruler
id: 8
...
Edhe njehere:
tarantool> get_goods(8)
---
- []
...
Siç mund ta shihni, kur përdoret në këtë mënyrë, funksioni kthen të gjitha regjistrimet e hapësirës faqe për faqe goods
. Faqja e fundit pasohet nga një përzgjedhje boshe.
Le të bëjmë ndryshime në hapësirë:
box.space.goods:update(4, {{'=', 6, 'copybook'}})
box.space.goods:insert{nil, 'clip', 234}
box.space.goods:insert{nil, 'folder', 432}
Ne kemi ndryshuar vlerën e fushës name
për një hyrje dhe shtoi dy hyrje të reja.
Le të përsërisim thirrjen e fundit të funksionit:
tarantool> get_goods(8)
---
- - row_ver: 9
code: 800
name: copybook
id: 6
- row_ver: 10
code: 234
name: clip
id: 9
- row_ver: 11
code: 432
name: folder
id: 10
...
Funksioni ktheu të dhënat e ndryshuara dhe të shtuara. Pra funksioni get_goods
ju lejon të merrni të dhëna që kanë ndryshuar që nga thirrja e fundit, e cila është baza e metodës së riprodhimit në shqyrtim.
Ne do ta lëmë nxjerrjen e rezultateve nëpërmjet HTTP në formën e JSON jashtë objektit të këtij neni. Ju mund të lexoni për këtë këtu:
Zbatimi i pjesës klient/skllav
Le të shohim se si duket zbatimi i palës marrëse. Le të krijojmë një hapësirë në anën marrëse për të ruajtur të dhënat e shkarkuara:
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 i ngjan strukturës së hapësirës në burim. Por meqenëse të dhënat e marra nuk do t'i kalojmë askund tjetër, kolona row_ver
nuk është në hapësirën e marrësit. Në fushë id
identifikuesit e burimit do të regjistrohen. Prandaj, nga ana e marrësit nuk ka nevojë ta bëni atë në rritje automatike.
Përveç kësaj, ne kemi nevojë për një hapësirë për të ruajtur vlerat 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ë të ngarkuar (fushë space_name
) do të ruajmë vlerën e ngarkuar të fundit këtu row_ver
(fushë value
). Kolona vepron si çelësi kryesor space_name
.
Le të krijojmë një funksion për të ngarkuar të dhënat e hapësirës goods
nëpërmjet HTTP. Për ta bërë këtë, na duhet një bibliotekë që implementon një klient HTTP. Linja e mëposhtme ngarkon bibliotekën dhe instancon klientin HTTP:
local http_client = require('http.client').new()
Ne gjithashtu kemi nevojë për një bibliotekë për deserializimin e json:
local json = require('json')
Kjo është e mjaftueshme për të krijuar një funksion ngarkimi të të dhënave:
local function load_data(url, row_ver)
local url = ('%s?rowVer=%s'):format(url,
tostring(row_ver))
local body = nil
local data = http_client:request('GET', url, body, {
keepalive_idle = 1,
keepalive_interval = 1
})
return json.decode(data.body)
end
Funksioni ekzekuton një kërkesë HTTP në adresën url dhe e dërgon atë 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 ky:
local function save_goods(goods)
local n = #goods
box.atomic(function()
for i = 1, n do
local obj = goods[i]
box.space.goods:put(
obj.id, obj.name, obj.code)
end
end)
end
Cikli i ruajtjes së të dhënave në hapësirë goods
vendosur në një transaksion (funksioni përdoret për këtë box.atomic
) për të reduktuar numrin e operacioneve të diskut.
Së fundi, funksioni i sinkronizimit të hapësirës lokale goods
me një burim mund ta zbatoni si kjo:
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
Së pari lexojmë vlerën e ruajtur më parë row_ver
për hapësirë goods
. Nëse mungon (sesioni i parë i shkëmbimit), atëherë e marrim si row_ver
zero. Më pas në ciklin ne kryejmë një shkarkim faqe për faqe të të dhënave të ndryshuara nga burimi në url-në e specifikuar. Në çdo përsëritje, ne i ruajmë të dhënat e marra në hapësirën e duhur lokale dhe përditësojmë vlerën row_ver
(në hapësirë row_ver
dhe në ndryshoren row_ver
) - merr vlerën row_ver
nga rreshti i fundit i të dhënave të ngarkuara.
Për të mbrojtur kundër lidhjes aksidentale (në rast të një gabimi në program), laku while
mund të zëvendësohet nga for
:
for _ = 1, max_req do ...
Si rezultat i ekzekutimit të funksionit sync_goods
hapësirë goods
marrësi do të përmbajë versionet më të fundit të të gjitha regjistrimeve hapësinore goods
në burim.
Natyrisht, fshirja e të dhënave nuk mund të transmetohet në këtë mënyrë. Nëse ekziston një nevojë e tillë, mund të përdorni një shenjë fshirjeje. Shto në hapësirë goods
fushë boolean is_deleted
dhe në vend që të fshijmë fizikisht një rekord, ne përdorim fshirjen logjike - vendosim vlerën e fushës is_deleted
në kuptim true
. Ndonjëherë në vend të një fushe boolean is_deleted
është më i përshtatshëm për të përdorur fushën deleted
, e cila ruan datën-kohën e fshirjes logjike të regjistrimit. Pas kryerjes së një fshirjeje logjike, rekordi i shënuar për fshirje do të transferohet nga burimi në destinacion (sipas logjikës së diskutuar më sipër).
Sekuenca row_ver
mund të përdoret për të transmetuar të dhëna nga hapësira të tjera: nuk ka nevojë të krijohet një sekuencë e veçantë për secilën hapësirë të transmetuar.
Ne shikuam një mënyrë efektive të riprodhimit të të dhënave të nivelit të lartë në aplikacione duke përdorur Tarantool DBMS.
Gjetjet
- Tarantool DBMS është një produkt tërheqës, premtues për krijimin e aplikacioneve me ngarkesë të lartë.
- Replikimi i të dhënave të nivelit të lartë ka një sërë përparësish mbi përsëritjen e nivelit të ulët.
- Metoda e përsëritjes së nivelit të lartë e diskutuar në artikull ju lejon të minimizoni sasinë e të dhënave të transferuara duke transferuar vetëm ato regjistrime që kanë ndryshuar që nga sesioni i fundit i shkëmbimit.
Burimi: www.habr.com