Replikimi i nivelit të lartë në Tarantool DBMS

Përshëndetje, unë jam duke krijuar aplikacione për DBMS Tarantool është një platformë e zhvilluar nga Mail.ru Group që kombinon një DBMS me performancë të lartë dhe një server aplikacioni në gjuhën Lua. Shpejtësia e lartë e zgjidhjeve të bazuara në Tarantool arrihet, veçanërisht, për shkak të mbështetjes për modalitetin në memorie të DBMS dhe aftësisë për të ekzekutuar logjikën e biznesit të aplikacionit në një hapësirë ​​​​të vetme adresash me të dhëna. Në të njëjtën kohë, qëndrueshmëria e të dhënave sigurohet duke përdorur transaksionet ACID (një regjistër WAL mbahet në disk). Tarantool ka mbështetje të integruar për replikimin dhe ndarjen. Duke filluar nga versioni 2.1, pyetjet në gjuhën SQL mbështeten. Tarantool është me burim të hapur dhe i licencuar nën licencën e thjeshtuar BSD. Ekziston edhe një version komercial i Ndërmarrjes.

Replikimi i nivelit të lartë në Tarantool 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.

Replikimi i nivelit të lartë në Tarantool DBMS
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ë.

Replikimi i nivelit të lartë në Tarantool DBMS
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.).

Replikimi i nivelit të lartë në Tarantool DBMS
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: https://habr.com/ru/company/mailru/blog/272141/

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

  1. Tarantool DBMS është një produkt tërheqës, premtues për krijimin e aplikacioneve me ngarkesë të lartë.
  2. 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.
  3. 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

Shto një koment