KÔrgetasemeline replikatsioon andmebaasis Tarantool

Tere, ma loon rakendusi andmebaaside jaoks. Tarantool — see on Mail.ru Groupi loodud platvorm, mis ĂŒhendab endas kĂ”rgotstarbelise andmebaasi ja rakenduste serveri Lua keeles. Tarantooli pĂ”hinevate lahenduste kĂ”rge töökiirus saavutatakse osaliselt tĂ€nu in-memory andmebaasi reĆŸiimi toele ja rakenduse Ă€riloogika tĂ€itmise vĂ”imalusele andmete ĂŒhes aadressiruumis. Samuti tagatakse andmete pĂŒsimĂ€lu ACID-tehingute abil (ketta peal hoitakse WAL-ĆŸurnaal). Tarantoo sisaldab sisseehitatud replikatsiooni ja ĆĄardimist. Alates versioonist 2.1 toetatakse SQL-keeles pĂ€ringute tĂ€itmist. Tarantool on avatud lĂ€htekoodiga ja levitatakse Simplified BSD litsentsi alusel. Samuti on olemas kaubanduslik Enterprise-variand.

KÔrgetasemeline replikatsioon andmebaasis Tarantool
Kogege jÔudu! (
ehk nautige jÔudlust)

KÔik eelnevalt mainitu muudab Tarantooli atraktiivseks platvormiks kÔrge koormusega rakenduste loomiseks, mis töötavad andmebaasidega. Sellistes rakendustes tekib sageli vajadus andmete replikatsiooni jÀrele.

Nagu eespool mainitud, sisaldab Tarantool sisseehitatud andmete replikatsiooni. Selle toimimise pĂ”himĂ”te seisneb kĂ”igi meisterdokumentide (WAL) sisaldavate tehingute jĂ€rjestikuses tĂ€itmises replikaates. TĂŒĂŒpiliselt kasutatakse seda replikatsiooni (edaspidi nimetame seda madalaks tasemeks) rakenduse tĂ”rkeasetuse tagamiseks ja/vĂ”i koormuse jaotamiseks klastrinode vahel.

KÔrgetasemeline replikatsioon andmebaasis Tarantool
Joon. 1. Replikatsioon klastris

Alternatiivse stsenaariumi nĂ€iteks vĂ”ib tuua andmete edastamise, mis on loodud ĂŒhes andmebaasis, teise andmebaasi töötlemiseks/monitoorimiseks. Viimases juhul vĂ”ib mugav lahendus olla kĂ”rge taseme replikatsioon — andmete replikatsioon rakenduse Ă€ri- ja loogika tasemel. See tĂ€hendab, et me ei kasuta valmislahendust, mis on sisse ehitatud andmebaasi, vaid teeme oma jĂ”ududega replikatsiooni oma rakenduses. Sellel lĂ€henemisel on nii eeliseid kui ka puudusi. Tuletame meelde plusse.

1. Andmete edastamise vÀhendamine:

  • vĂ”imalik on edastada mitte kĂ”iki andmeid, vaid ainult osa neist (nĂ€iteks vĂ”ib edastada ainult teatud tabeleid, mĂ”ned nende veerud vĂ”i kirjed, mis vastavad teatud kriteeriumitele);
  • erinevalt madala taseme replikatsioonist, mis toimub pidevalt asĂŒnkroonses (rakendatud praeguses versioonis Tarantool — 1.10) vĂ”i sĂŒnkroonses (realiseeritakse Tarantooli jĂ€rgnevatel versioonidel) reĆŸiimis, kĂ”rgetasemelist replikatsiooni saab teha sessioonidena (st rakendus tĂ€idab esmalt andmete sĂŒnkroniseerimist — andmeedastus sessioon, seejĂ€rel jĂ€rgneb replikatsioonist paus, pĂ€rast mida toimub jĂ€rgmine andmeedastus sessioon jne.);
  • kui kirje on mitu korda muutunud, saab edastada ainult viimase versiooni (erinevalt madalast tasemest replikatsioonist, mille kĂ€igus replikatsioonides jĂ€rgis jĂ€rk-jĂ€rgult kĂ”iki muudatusi, mis tehti meistris).

2. HTTP-poolsete vahetuste realiseerimise keerukuse puudumine, mis vĂ”imaldab sĂŒnkroniseerida kaugandmebaase.

KÔrgetasemeline replikatsioon andmebaasis Tarantool
Joon. 2. Replikatsioon HTTP kaudu

3. Andmebaasi struktuurid, mille vahel andmeid edastatakse, ei pea olema identsed (ĂŒldiselt on isegi vĂ”imalik kasutada erinevaid andmebaase, programmeerimiskeeli, platvorme jne.).

KÔrgetasemeline replikatsioon andmebaasis Tarantool
Joon. 3. Replikatsioon heterogeensetes sĂŒsteemides

Puuduseks on see, et keskmiselt on programmeerimine keerulisem/kallim kui konfigureerimine, ja sisseehitatud funktsionaalsuse seadistamise asemel tuleb realiseerida enda lahendused.

Kui teie olukorras on toodud eelised kriitilise tÀhtsusega (vÔi vajalikud tingimused), siis on mÔttekas kasutada kÔrgetasemelist replikatsiooni. Vaatame mÔningaid viise andmete kÔrgetasemelise replikatsiooni elluviimiseks andmebaasis Tarantool.

Trahvi vÀhendamine

Nii et ĂŒheks kĂ”rgetasemelise replikatsiooni eelisteks on andmeedastuse vĂ€hendamine. Selle eelise tĂ€ielikuks vĂ€ljendamiseks on oluline minimeerida andmete hulka, mis edastatakse iga andmeedastus sessiooni puhul. Loomulikult ei tohi unustada, et andmeedastus sessiooni lĂ”pus peab saaja olema sĂŒnkroonitud allika andmetega (kui mitte vĂ€hem neid andmeid, mis osalevad replikatsioonis).

Kuidas siis minimeerida andmete hulka, mis edastatakse kĂ”rgetasemelise replikatsiooni kĂ€igus? Üheks viisiks vĂ”ib olla andmete valimine kuupĂ€eva ja kellaaja jĂ€rgi. Selleks vĂ”ib kasutada tabelis juba olemasolevat kuupĂ€eva ja kellaaja vĂ€ljakut (kui see on olemas). NĂ€iteks vĂ”ib dokumendil 'tellimus' olla vĂ€li 'tellimuse tĂ€itmise nĂ”utav aeg' — delivery_time. Selle lahenduse probleem seisneb selles, et sellel vĂ€ljal ei pea vÀÀrtused olema tellimuste loomise jĂ€rjekorras. SeetĂ”ttu ei saa me selle vĂ€li maksimaalset vÀÀrtust meeles pidada. delivery_time, mis edastati eelneva vahetusseansi ajal, ja jĂ€rgmise vahetuse jooksul koguda kĂ”ik kirjed, millel on vĂ€ljavÀÀrtus suurem. delivery_time. Vahetuste vahel vĂ”isid lisanduda kirjed, mille vĂ€li on vĂ€iksem. delivery_time. Samuti vĂ”is tellimus muutuda, kuid see ei mĂ”jutanud vĂ€lja. delivery_time. MĂ”lemal juhul ei edastata muudatused allikast vastuvĂ”tjale. Nende probleemide lahendamiseks peame andmeid edastama 'kattega'. See tĂ€hendab, et igas vahetuses edastame kĂ”ik andmed, mille vĂ€ljavÀÀrtus delivery_time, ĂŒletab mingi ajakohase hetke (nĂ€iteks N tundi alates praegusest hetkest). Selgelt on aga, et suurte sĂŒsteemide puhul on selline lĂ€henemine liiga ĂŒlemÀÀrane ja vĂ”ib tĂŒhistada meie liikluse kokkuhoiu, mille poole me pĂŒĂŒame. Lisaks vĂ”ib edastatavas tabelis puududa kuupĂ€eva-aega seonduv vĂ€li.

Teine variant, mis on keerukam teostusena, seisneb andmete vastuvĂ”tmise kinnitamises. Sellisel juhul edastatakse igas vahetuses kĂ”ik andmed, mille vastuvĂ”tmist vastuvĂ”tja ei ole kinnitanud. Selle teostamiseks tuleb allika tabelisse lisada boolse veeru (nt is_transferred). Kui vastuvĂ”tja kinnitab kirje vastuvĂ”tu, vĂ”tab vastav vĂ€li vÀÀrtuse true, pĂ€rast mida see kirje enam vahetustes ei osale. Selle teostusvariandi miinused on jĂ€rgmised. Esiteks peab iga edastatud kirje jaoks genereerima ja saatma kinnituse. Üldiselt vĂ”ib see sarnaneda edastatavate andmete kahekordistumisega ja toob kaasa kahekordse ringreiside arvu. Teiseks puudub vĂ”imalus saata sama kirje mitmele vastuvĂ”tjale (esimene saanud vastuvĂ”tja kinnitab vastuvĂ”ttu enda ja kĂ”ikide teiste nimel).

Puuduvate miinuste kĂ”rvaldamine seisneb selles, et edastatavas tabelis lisatakse veerg, et jĂ€lgida oma ridade muutumise. Sellel veerul vĂ”ib olla kuupĂ€eva-aeg ja see peab mÀÀrama / uuendama rakenduse poolt praegusele ajale iga kord, kui kirjeid lisatakse / muudetakse (aatomaarne koos lisamise / muutmisega). NĂ€iteks nimetame veeru update_time. SĂ€ilitades selle veeru maksimaalse vÀÀrtuse edastatud kirgede korral, saame jĂ€rgmist vahetust alustada sellest vÀÀrtusest (koguda kirjed, millevĂ€ljavÀÀrtus update_time, ĂŒletab eelnevalt salvestatud vÀÀrtuse). Probleem, mis on seotud viimase lĂ€henemisega, seisneb selles, et andmete muutused vĂ”ivad toimuda partiide vahel. SeetĂ”ttu vĂ”ivad selle veeru vÀÀrtused update_time olla mitteunikaalsed. Seega ei saa seda veergu kasutada andmete osalise (lehe kaupa) vĂ€ljundi jaoks. Lehe kaupa andmete edastamiseks tuleb leiutada tĂ€iendavad mehhanismid, mis tĂ”enĂ€oliselt on vĂ€ga madala efektiivsusega (nĂ€iteks andmebaasist kĂ”ikide kirgede saamine, millevÀÀrtus on update_time ĂŒlalteatud ning valehinnanguga andmete edastamine, alustades teatud nihkest valiku algusest).

Saame andmete edastamise efektiivsust parandada, tĂ€iendades eelmist lĂ€henemist. Selleks kasutame muutuste jĂ€lgimise veeru vÀÀrtustena tĂ€isarvulist tĂŒĂŒpi (pikad tĂ€isarvud). Nimetame veeru row_ver. Selle veeru vÀÀrtus peab endiselt olema mÀÀratud / uuendatud iga kord, kui kirjet luuakse / muudetakse. Kuid antud juhul mÀÀratakse vĂ€ljal mitte praegune kuupĂ€ev-aeg, vaid mingi loenduri vÀÀrtus, suurendatud ĂŒhe vĂ”rra. Selle tulemusena sisaldab veerg row_ver unikaalseid vÀÀrtusi ja seda vĂ”ib kasutada mitte ainult 'delta' andmete edastamiseks (andmete, mis on lisatud / muutunud pĂ€rast eelmise vahetusseansi lĂ”ppemist), vaid ka lihtsaks ja efektiivseks lehtede kaupa jagamiseks.

Viimane ettepanek andmeedastamise vÀhendamiseks kÔrgetasemelise replikatsiooni raames tundub olevat kÔige optimaalsem ja universaalsem. Vaatame seda lÀhemalt.

Andmete edastamine ridade versioonide loenduri kaudu.

Serveri / peamise osa teostamine.

MS SQL Serveris on sellise lĂ€henemise rakendamiseks eriline veeru tĂŒĂŒp - rowversion. Iga andmebaas omab loenduri, mis suureneb ĂŒhe vĂ”rra iga kord, kui kirjed lisatakse / muudetakse tabelis, millel on sellise veeru tĂŒĂŒp. rowversion. Selle arvesti vÀÀrtus mÀÀratakse automaatselt selle veeru vĂ€lja, mis on lisatud/muudetud kirjesse. Tarantooli andmebaas ei oma sarnast sisseehitatud mehhanismi. Siiski on seda Tarantoolis lihtne kĂ€sitsi rakendada. Vaatame, kuidas seda teha.

Alustame veidi terminoloogiaga: tabelid Tarantoolis nimetatakse spaasideks (space) ja kirjed korduudeks (tuple). Tarantoolis on vÔimalik luua jÀrjestusi (sequence). JÀrjestused on nimelised generaatorid jÀrjestatud tÀisarvude vÀÀrtustega. See on just see, mida meie eesmÀrkide jaoks vajame. Allpool loome sellise jÀrjestuse.

Enne kui tÀidate mÔnda tegevust Tarantooli andmebaasis, peate tÀitma jÀrgmise kÀsu:

box.cfg{}

Tulemuseks hakkab Tarantool salvestama praegusesse katalooge andmebaasi (snapshot) ja tehingute pÀeviku.

Loome jÀrjestuse row_version:

box.schema.sequence.create('row_version',
    { if_not_exists = true })

Valik if_not_exists lubab skripti loomist mitmeid kordi: kui objekt juba eksisteerib, ei ĂŒrita Tarantool seda uuesti luua. Seda vĂ”imalust kasutatakse kĂ”igis jĂ€rgnevatel DDL-kĂ€skudel.

Loome nÀidiseks spaa.

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
})

Siin mÀÀrasime spaa nime (goods), vĂ€ljade nimed ja nende tĂŒĂŒbid.

Tarantoolis luuakse automaatselt suurenevaid vÀlju ka jÀrjestuste abil. Loome automaatselt suureneva pÔhivÔtme vÀlja 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 toetab mitmeid indeksitĂŒĂŒpide. KĂ”ige sagedamini kasutatakse TREE- ja HASH-tĂŒĂŒpi indekseid, mis pĂ”hinevad vastavatel struktuuridel. TREE on kĂ”ige universaalsem indeksitĂŒĂŒp. See vĂ”imaldab andmeid hankida jĂ€rjestatud viisil. Kuid vĂ”rdsuse valimiseks sobib rohkem HASH. Seega on pĂ”hivĂ”tme jaoks mĂ”istlik kasutada HASH (nagu me ka tegin).

Kasutamaks veergu row_ver muudetud andmete edastamiseks, tuleb sellele veerule siduda jĂ€rjestuse vÀÀrtused row_ver. Kuid erinevalt pĂ”hivĂ”tme vÀÀrtusest, peab veeru vÀÀrtus row_ver kasvama ĂŒksuse vĂ”rra mitte ainult uute rekordite lisamisel, vaid ka olemasolevate muutmisel. Selleks saab kasutada triggereid. Tarantoolis on kaks tĂŒĂŒpi triggereid spaa jaoks: before_replace ja on_replace. Triggereid aktiveeritakse iga andme muutuse korral spaas (iga korduute puhul, mis tulevad muutustest, kĂ€ivitatakse triggeri funktsioon). Erinevalt on_replace, before_replace-triggereid vĂ”imaldavad muutuda korduute andmeid, mille jaoks triggeri tĂ€itmine toimub. Seega sobib meile viimane triggereid tĂŒĂŒp.

box.space.goods:before_replace(function(old, new)
    return box.tuple.new({new[1], new[2], new[3],
        box.sequence.row_version:next()})
end)

Antud trigger asendab vÀlja vÀÀrtuse row_ver salvestatud korduudest jÀrgmise jÀrjestuse vÀÀrtusega. row_version.

Et andmeid saaks spaas vÀlja vÔtta goods veeru row_ver, loome indeksi:

box.space.goods:create_index('row_ver', {
    parts = { 'row_ver' },
    unique = true,
    type = 'TREE',
    if_not_exists = true
})

Indeksi tĂŒĂŒp on puu (TREE), kuna andmed tuleks hankida vÀÀrtuste kasvava jĂ€rjestuse jĂ€rgi veerus. row_ver.

Lisame spaa mÔned andmed:

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}

Kuna esimene vĂ€li on automaatselt suurenev arvesti, edastame selle asemel nil. Tarantool asendab automaatselt jĂ€rgmise vÀÀrtusega. Samuti saab vĂ€lja vÀÀrtustena veeru row_ver edastada nil — vĂ”i mitte mÀÀrata vÀÀrtust ĂŒldse, kuna see veerg on spaas viimasel positsioonil.

Kontrollime lisamise tulemust:

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]
...

Nagu nĂ€eme, tĂ€itusid esimene ja viimane vĂ€li automaatselt. NĂŒĂŒd on lihtne kirjutada funktsioon lehe kaupa muudatuste vĂ€lja tĂ”mbamiseks spaas 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

Funktsioon vÔtab parameetrina vÀÀrtuse row_ver, alates millest tuleb muudatuste vÀlja tÔmbamine teha, ja tagastab osa muudetud andmetest.

Tarantoolis andmete valimine toimub indeksite kaudu. Funktsioon get_goods kasutab indeksil pĂ”hinevat iteratorkĂ€iku row_ver muudetud andmete saamiseks. Iteraatori tĂŒĂŒp on GT (Greater Than). See tĂ€hendab, et iteraator lĂ€bib jĂ€rjestikku indeksi vÀÀrtuseid alates antud vĂ”tmest (vĂ€lja vÀÀrtusest row_ver).

Iteraator tagastab tupse. Et andmeid hiljem HTTP kaudu edastada, tuleb tupse konverteerida struktuuri, mis sobib jÀrgnevale serialiseerimisele. NÀites kasutatakse selleks standardset funktsiooni tomap. Selle asemel, et kasutada tomap vÔib kirjutada oma funktsiooni. NÀiteks vÔime soovida vÀlja nimetada name, mitte edastada vÀlja code ja lisada vÀlja 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

Lehe suurus, milles andmeid vĂ”etakse (kirjete arv ĂŒhes osas), mÀÀratakse muutuja kaudu page_size. NĂ€ites on vÀÀrtus page_size 5. Reaalses programmis on lehe suurus tavaliselt suurem. See sĂ”ltub tupse keskmisest suurusest. Optimaalset lehe suurust saab katsetades leida, mÔÔtes andmete edastamise aega. Mida suurem on lehe suurus, seda vĂ€hem on kĂ€e tagasi mĂ”ne eelneva ja jĂ€rgnevate poolte vahel. See vĂ”ib vĂ€hendada ĂŒldist muutuste edastamise aega. Kuid liiga suure lehe suurusega saame liiga kaua serverit hĂ”ivata tagastatava andmete serialiseerimisega. Selle tulemusena vĂ”ivad tekkida viivitused teistele serverisse saabuvate pĂ€ringute töötlemisel. Parameetrit page_size vĂ”ib laadida konfiguratsioonifailist. Iga edastatava spetsiifika jaoks vĂ”ib mÀÀrata oma vÀÀrtuse. Enamikule spetsiifikale sobib siiski vaikimisi vÀÀrtus (nĂ€iteks 100).

KĂ€ivitame funktsiooni 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
...

VÔtame vÀlja vÀÀrtuse row_ver viimases reaas ja kutsub uuesti funktsiooni:

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
...

Ja veel kord:

tarantool> get_goods(8)
---
- []
...

Nagu nĂ€eme, tagastab funktsioon sellisel viisil lehtede kaupa kĂ”ik spetsiifika kirjed. goodsViimase lehe jĂ€rel on tĂŒhine valik.

Teeme spetsiifikasse muudatusi:

box.space.goods:update(4, {{'=', 6, 'copybook'}})
box.space.goods:insert{nil, 'clip', 234}
box.space.goods:insert{nil, 'folder', 432}

Olemegi muutnud vĂ€lja vÀÀrtust name ĂŒhe kirje jaoks ning lisanud kaks uut kirjet.

Kutsume uuesti viimast funktsiooni:

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
...

Funktsioon tagastas muudetud ja lisatud kirjed. Seega vÔimaldab funktsioon get_goods saada andmeid, mis on muutunud alates selle viimase kÔne tegemisest, mis ongi kÀsitletava replikatsiooni meetodi aluseks.

JĂ€tame tulemuste edastamise HTTP kaudu JSONina selle artikli raames arutlemata. Selle kohta saate lugeda siit: https://habr.com/ru/company/mailru/blog/272141/

Kliendi/slave osa rakendamine

Vaatleme, milline nÀeb vÀlja vastuvÔtva poole rakendamine. Loome vastuvÔtval poolel spetsiifika, kuhu salvestada laaditud andmed:

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
})

Spetsiifika struktuur sarnaneb allika spetsiifika struktuuriga. Kuid kuna me ei kavatse saadud andmeid kuhugi mujale edastada, puudub vastuvÔtva spetsiifika veerg. row_ver VÀlja id salvestatakse allika identifikaatorid. Seega pole vastuvÔtva poole jaoks vajalik, et see oleks automaatselt suurenev.

Lisaks on vajalik spetsiifika, et salvestada vÀÀrtused 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
})

Iga edastatava spetsiifika kohta (vÀlja space_name) salvestame siia viimase laadimise vÀÀrtuse row_ver (vÀlja value). Peamise vÔtmena kasutatakse veergu space_name.

Loome funktsiooni, et laadida andmeid spetsiifikalt goods HTTP kaudu. Selleks vajame HTTP-kliendi rakendavalt teeki. JĂ€rgmine rida laadib teegi ja loob HTTP-kliendi eksemplari:

local http_client = require('http.client').new()

Samuti vajame teeki JSONi deserialiseerimiseks:

local json = require('json')

Sellega piisab funktsiooni loomiseks, et laadida andmeid:

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

Funktsioon tÀidab HTTP-pÀringu aadressile url, edastab sellele row_ver parameetrina ja tagastab deserialiseeritud pÀringu tulemuse.

Saadavate andmete salvestamise funktsioon nÀeb vÀlja jÀrgmine:

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

Andmete salvestamise tsĂŒkkel on paigutatud tehingusse (selleks kasutatakse funktsiooni goods box.atomic ) operatsioonide arvu vĂ€hendamiseks kettal.LĂ”puks on kohaliku ruumi sĂŒnkroniseerimise funktsioon vĂ”imalik ellu viia jĂ€rgmiselt:

Alustame varem salvestatud vÀÀrtuse lugemisest goods ruumile

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

. Kui seda pole (vahetussessioon on esimene), siis vĂ”tame vÀÀrtuseks row_ver null. JĂ€rgmistel kordadel teeme lehe kaupa muudatustega andmete laadimise allikast antud url-i kaudu. Igal iteratsioonil salvestame saadud andmed vastavasse kohalikku ruumi ja uuendame vÀÀrtust goods(ruumis row_ver ja muutuja row_ver ) — vĂ”tame vÀÀrtuse row_ver viimases laaditud andmete real. row_verJuhuks, et vĂ€ltida juhuslikku lĂ”ksu sattumist (programmi vea korral), vĂ”ib tsĂŒkli row_ver viimastest laaditud andmetest.

asendada samal ajal Funktsiooni tÀitmise tulemuseks on for:

sync_goods

ruum vastuvÔtjas sisaldama kÔiki salvestatud ruumi viimaseid versioone allikas. goods On ilmne, et sel viisil ei saa edastada andmete kustutamist. Kui selline vajadus on, saab kasutada kustutamise mÀrgistamise meetodit. Lisame ruumi goods boolenvÀli

Ilmselgelt ei saa sellist meetodit kasutada andmete kustutamise edasiviimiseks. Kui selline vajadus tekib, saab kasutada kustutamismĂ€rgistust. Lisame ruumi. goods ja fĂŒĂŒsilise kustutamise asemel kasutame loogilist kustutamist — seadistame vĂ€lja selle vĂ€ljade vÀÀrtuse. is_deleted , kuhu salvestatakse loogilise kustutamise kuupĂ€eva ja aega. PĂ€rast loogilise kustutamise teostamist saadetakse kustutamiseks mĂ€rgitud rida allikast vastuvĂ”tjasse (vastavalt varem kĂ€sitletud loogikale). is_deleted vÀÀrtuse trueJada is_deleted saab kasutada teiste ruumide andmete edastamiseks: ei ole vaja luua eraldi jĂ€rjestust iga edastatava ruumi jaoks. kustutatudOleme kĂ€sitlenud tĂ”husat viisi kĂ”rgetasemeliseks andmete replikeerimiseks rakendustes, mis kasutavad andmebaasi Tarantool.

Andmebaas Tarantool on atraktiivne, perspektiivikas toode, mis sobib kÔrge koormusega rakenduste loomiseks. row_ver KÔrgetasemeline andmete replikeerimine on mitmeid eeliseid madalama taseme replikeerimise ees.

Artiklis kÀsitletud kÔrgetasemeline replikeerimise meetod vÔimaldab minimeerida edastatavate andmete arvu, edastades ainult need kirjed, mis on muutunud alates viimati toimunud vahetusest.

JĂ€reldused

  1. Tere, ma arendan rakendusi andmebaasi Tarantool jaoks — see on Mail.ru Groupis vĂ€lja töötatud platvorm, mis ĂŒhendab endas kĂ”rge jĂ”udlusega andmebaasi ja rakenduse serveri keeles Lua. Tarantooli pĂ”hjal loodud lahenduste kĂ”rge töökiirus saavutatakse osaliselt andmebaasi mĂ€lu reĆŸiimi toetamise ja rakenduse Ă€riloogika tĂ€itmise vĂ”imaluse tĂ”ttu ĂŒhes ja samas aadressiruumis andmetega. Samuti
  2. đŸ„‡KĂ”rgetasemeline replikeerimine andmebaasis Tarantool | ProHoster
  3. Artiklis kÀsitletud meetod kÔrgetasemeliseks replikatsiooniks vÔimaldab minimeerida edastatud andmete hulka, edastades ainult need kirjed, mis on muutunud pÀrast viimast vahetusseanssi.

Allikas: habr.com

Osta usaldusvÀÀrne veebihosting DDoS kaitsega, VPS VDS serverid đŸ”„ Osta usaldusvÀÀrne veebihosting DDoS kaitsega, VPS VDS serverid | ProHoster