Augsta līmeņa replikācija Tarantool DBVS

Sveiki, veidoju lietojumprogrammas DBVS Tarantool ir Mail.ru Group izstrādāta platforma, kas apvieno augstas veiktspējas DBVS un lietojumprogrammu serveri Lua valodā. Uz Tarantool balstÄ«to risinājumu lielais ātrums tiek sasniegts, jo Ä«paÅ”i pateicoties atbalstam DBVS atmiņas režīmam un iespējai izpildÄ«t lietojumprogrammu biznesa loÄ£iku vienā adreÅ”u telpā ar datiem. Tajā paŔā laikā datu noturÄ«ba tiek nodroÅ”ināta, izmantojot ACID transakcijas (diskā tiek uzturēts WAL žurnāls). Tarantool ir iebÅ«vēts atbalsts replikācijai un sadalÄ«Å”anai. Sākot ar versiju 2.1, tiek atbalstÄ«ti vaicājumi SQL valodā. Tarantool ir atvērtā koda un licencēta saskaņā ar vienkārÅ”oto BSD licenci. Ir arÄ« komerciāla Enterprise versija.

Augsta līmeņa replikācija Tarantool DBVS
SajÅ«ti spēku! (...aka izbaudiet priekÅ”nesumu)

Viss iepriekÅ” minētais padara Tarantool par pievilcÄ«gu platformu lielas slodzes lietojumprogrammu izveidei, kas darbojas ar datu bāzēm. Šādās lietojumprogrammās bieži vien ir nepiecieÅ”ama datu replikācija.

Kā minēts iepriekÅ”, Tarantool ir iebÅ«vēta datu replikācija. Tās darbÄ«bas princips ir secÄ«gi izpildÄ«t visas galvenajā žurnālā (WAL) ietvertās transakcijas replikās. Parasti Ŕāda replikācija (mēs to turpmāk sauksim zems lÄ«menis) tiek izmantots, lai nodroÅ”inātu lietojumprogrammas kļūdu toleranci un/vai lai sadalÄ«tu lasÄ«Å”anas slodzi starp klastera mezgliem.

Augsta līmeņa replikācija Tarantool DBVS
Rīsi. 1. Replikācija klasterī

AlternatÄ«va scenārija piemērs varētu bÅ«t vienā datubāzē izveidoto datu pārsÅ«tÄ«Å”ana uz citu datu bāzi apstrādei/uzraudzÄ«bai. Pēdējā gadÄ«jumā ērtāks risinājums var bÅ«t izmantoÅ”ana augsts lÄ«menis replikācija ā€” datu replikācija lietojumprogrammas biznesa loÄ£ikas lÄ«menÄ«. Tie. Mēs neizmantojam gatavu risinājumu, kas iebÅ«vēts DBVS, bet paÅ”i Ä«stenojam replikāciju lietojumprogrammā, kuru izstrādājam. Å ai pieejai ir gan priekÅ”rocÄ«bas, gan trÅ«kumi. UzskaitÄ«sim priekÅ”rocÄ«bas.

1. Satiksmes ietaupījumi:

  • JÅ«s nevarat pārsÅ«tÄ«t visus datus, bet tikai daļu no tiem (piemēram, varat pārsÅ«tÄ«t tikai dažas tabulas, dažas to kolonnas vai ierakstus, kas atbilst noteiktam kritērijam);
  • AtŔķirÄ«bā no zema lÄ«meņa replikācijas, kas tiek veikta nepārtraukti asinhronā (ieviesta paÅ”reizējā Tarantool versijā - 1.10) vai sinhronā (jāievieÅ” nākamajās Tarantool versijās) režīmā, augsta lÄ«meņa replikāciju var veikt sesijās (t.i., lietojumprogramma vispirms sinhronizē datus - apmaiņas sesijas datus, pēc tam notiek replikācijas pauze, pēc kuras notiek nākamā apmaiņas sesija utt.);
  • ja ieraksts ir mainÄ«ts vairākas reizes, varat pārsÅ«tÄ«t tikai tā jaunāko versiju (atŔķirÄ«bā no zema lÄ«meņa replikācijas, kurā visas izmaiņas, kas veiktas galvenajā versijā, tiks atskaņotas secÄ«gi replikās).

2. Nav nekādu grÅ«tÄ«bu ar HTTP apmaiņas ievieÅ”anu, kas ļauj sinhronizēt attālās datu bāzes.

Augsta līmeņa replikācija Tarantool DBVS
Rīsi. 2. Replikācija, izmantojot HTTP

3. Datu bāzes struktÅ«rām, starp kurām tiek pārsÅ«tÄ«ti dati, nav jābÅ«t vienādām (turklāt vispārÄ«gā gadÄ«jumā pat iespējams izmantot dažādas DBVS, programmÄ“Å”anas valodas, platformas utt.).

Augsta līmeņa replikācija Tarantool DBVS
Rīsi. 3. Replikācija neviendabīgās sistēmās

MÄ«nuss ir tāds, ka vidēji programmÄ“Å”ana ir grÅ«tāka/dārgāka nekā konfigurÄ“Å”ana, un tā vietā, lai pielāgotu iebÅ«vēto funkcionalitāti, nāksies ieviest savu.

Ja jÅ«su situācijā iepriekÅ” minētās priekÅ”rocÄ«bas ir izŔķiroÅ”as (vai ir nepiecieÅ”ams nosacÄ«jums), tad ir lietderÄ«gi izmantot augsta lÄ«meņa replikāciju. ApskatÄ«sim vairākus veidus, kā ieviest augsta lÄ«meņa datu replikāciju Tarantool DBVS.

Satiksmes samazināŔana

Tātad viena no augsta lÄ«meņa replikācijas priekÅ”rocÄ«bām ir satiksmes ietaupÄ«jumi. Lai Ŕī priekÅ”rocÄ«ba tiktu pilnÄ«bā realizēta, katras apmaiņas sesijas laikā ir jāsamazina pārsÅ«tÄ«to datu apjoms. Protams, nevajadzētu aizmirst, ka sesijas beigās datu uztvērējam ir jābÅ«t sinhronizētam ar avotu (vismaz tai datu daļai, kas ir iesaistÄ«ta replikācijā).

Kā samazināt pārsÅ«tÄ«to datu apjomu augsta lÄ«meņa replikācijas laikā? VienkārÅ”s risinājums varētu bÅ«t datu atlase pēc datuma un laika. Lai to izdarÄ«tu, varat izmantot tabulā jau esoÅ”o datuma un laika lauku (ja tāds ir). Piemēram, ā€œpasÅ«tÄ«jumaā€ dokumentā var bÅ«t lauks ā€œnepiecieÅ”amais pasÅ«tÄ«juma izpildes laiksā€ - delivery_time. Problēma ar Å”o risinājumu ir tāda, ka vērtÄ«bām Å”ajā laukā nav jābÅ«t tādā secÄ«bā, kas atbilst pasÅ«tÄ«jumu izveidei. Tāpēc mēs nevaram atcerēties maksimālo lauka vērtÄ«bu delivery_time, kas pārsÅ«tÄ«ts iepriekŔējās apmaiņas sesijas laikā, un nākamās apmaiņas sesijas laikā atlasiet visus ierakstus ar lielāku lauka vērtÄ«bu delivery_time. Ieraksti ar zemāku lauka vērtÄ«bu, iespējams, ir pievienoti starp apmaiņas sesijām delivery_time. Tāpat pasÅ«tÄ«jumā varēja bÅ«t izmaiņas, kas tomēr laukumu neietekmēja delivery_time. Abos gadÄ«jumos izmaiņas netiks pārsÅ«tÄ«tas no avota uz galamērÄ·i. Lai atrisinātu Ŕīs problēmas, mums bÅ«s jāpārsÅ«ta dati "pārklājoties". Tie. katrā apmaiņas sesijā mēs pārsÅ«tÄ«sim visus datus ar lauka vērtÄ«bu delivery_time, pārsniedzot kādu punktu pagātnē (piemēram, N stundas no paÅ”reizējā brīža). Tomēr ir acÄ«mredzams, ka lielām sistēmām Ŕī pieeja ir ļoti lieka un var samazināt trafika ietaupÄ«jumus, uz kuriem mēs tiecamies. Turklāt pārsÅ«tāmajā tabulā var nebÅ«t lauka, kas saistÄ«ts ar datumu un laiku.

Vēl viens risinājums, kas ir sarežģītāks ievieÅ”anas ziņā, ir apstiprināt datu saņemÅ”anu. Å ajā gadÄ«jumā katras apmaiņas sesijas laikā tiek pārsÅ«tÄ«ti visi dati, kuru saņemÅ”anu saņēmējs nav apstiprinājis. Lai to Ä«stenotu, avota tabulai bÅ«s jāpievieno BÅ«la kolonna (piemēram, is_transferred). Ja saņēmējs apstiprina ieraksta saņemÅ”anu, attiecÄ«gajā laukā tiek iegÅ«ta vērtÄ«ba true, pēc kura ieraksts vairs nav iesaistÄ«ts apmaiņā. Å ai ievieÅ”anas iespējai ir Ŕādi trÅ«kumi. Pirmkārt, katram pārsÅ«tÄ«tajam ierakstam ir jāģenerē un jānosÅ«ta apstiprinājums. Aptuveni runājot, tas varētu bÅ«t salÄ«dzināms ar pārsÅ«tÄ«to datu apjoma dubultoÅ”anu un divkārÅ”ot reisu skaitu turp un atpakaļ. Otrkārt, nav iespējas nosÅ«tÄ«t vienu un to paÅ”u ierakstu vairākiem saņēmējiem (pirmais saņēmējs apstiprinās saņemÅ”anu sev un visiem pārējiem).

Metode, kurai nav iepriekÅ” minēto trÅ«kumu, ir pievienot kolonnu nosÅ«tÄ«tajai tabulai, lai izsekotu izmaiņām tās rindās. Šāda kolonna var bÅ«t datuma-laika tipa, un lietojumprogrammai tā ir jāiestata/jāatjaunina lÄ«dz paÅ”reizējam laikam ikreiz, kad ieraksti tiek pievienoti/mainÄ«ti (ar pievienoÅ”anu/maiņu atomiski). Kā piemēru nosauksim kolonnu update_time. Saglabājot Ŕīs kolonnas maksimālo lauka vērtÄ«bu pārsÅ«tÄ«tajiem ierakstiem, mēs varam sākt nākamo apmaiņas sesiju ar Å”o vērtÄ«bu (atlasiet ierakstus ar lauka vērtÄ«bu update_time, pārsniedzot iepriekÅ” saglabāto vērtÄ«bu). Problēma ar pēdējo pieeju ir tāda, ka datu izmaiņas var notikt partijās. Kolonnas lauka vērtÄ«bu rezultātā update_time var nebÅ«t unikāla. Tādējādi Å”o kolonnu nevar izmantot dalÄ«tu (lapu pa lappusei) datu izvadei. Lai parādÄ«tu datus pa lappusei, jums bÅ«s jāizgudro papildu mehānismi, kuriem, visticamāk, bÅ«s ļoti zema efektivitāte (piemēram, izgÅ«t no datu bāzes visus ierakstus ar vērtÄ«bu update_time augstāks par doto un rada noteiktu ierakstu skaitu, sākot ar noteiktu nobÄ«di no izlases sākuma).

JÅ«s varat uzlabot datu pārsÅ«tÄ«Å”anas efektivitāti, nedaudz uzlabojot iepriekŔējo pieeju. Lai to izdarÄ«tu, mēs izmantosim vesela skaitļa tipu (garu veselu skaitli) kā kolonnas lauka vērtÄ«bas izmaiņu izsekoÅ”anai. Nosauksim kolonnu row_ver. Å Ä«s kolonnas lauka vērtÄ«ba joprojām ir jāiestata/atjaunina katru reizi, kad tiek izveidots/mainÄ«ts ieraksts. Bet Å”ajā gadÄ«jumā laukam tiks pieŔķirts nevis paÅ”reizējais datums-laiks, bet gan kāda skaitÄ«tāja vērtÄ«ba, kas palielināta par vienu. Rezultātā kolonna row_ver satur unikālas vērtÄ«bas, un tos var izmantot ne tikai, lai parādÄ«tu ā€œdeltaā€ datus (dati, kas pievienoti/mainÄ«ti kopÅ” iepriekŔējās apmaiņas sesijas beigām), bet arÄ« vienkārÅ”i un efektÄ«vi sadalÄ«t tos lapās.

Pēdējā piedāvātā metode augsta lÄ«meņa replikācijas ietvaros pārsÅ«tÄ«to datu apjoma samazināŔanai man Ŕķiet visoptimālākā un universālākā. ApskatÄ«sim to sÄ«kāk.

Datu nodoŔana, izmantojot rindu versiju skaitītāju

Servera/master daļas ievieŔana

MS SQL Server Ŕīs pieejas ievieÅ”anai ir Ä«paÅ”s kolonnu veids - rowversion. Katrai datu bāzei ir skaitÄ«tājs, kas palielinās par vienu katru reizi, kad ieraksts tiek pievienots/mainÄ«ts tabulā, kurā ir lÄ«dzÄ«ga kolonna rowversion. Å Ä« skaitÄ«tāja vērtÄ«ba tiek automātiski pieŔķirta Ŕīs kolonnas laukam pievienotajā/mainÄ«tajā ierakstā. Tarantool DBVS nav lÄ«dzÄ«ga iebÅ«vēta mehānisma. Tomēr Tarantool to nav grÅ«ti ieviest manuāli. ApskatÄ«sim, kā tas tiek darÄ«ts.

Pirmkārt, nedaudz terminoloÄ£ijas: Tarantool tabulas sauc par atstarpēm, bet ierakstus sauc par korežiem. Tarantool varat izveidot secÄ«bas. SecÄ«bas ir nekas vairāk kā nosaukti sakārtotu veselu skaitļu vērtÄ«bu Ä£eneratori. Tie. tas ir tieÅ”i tas, kas mums vajadzÄ«gs mÅ«su mērÄ·iem. Zemāk mēs izveidosim Ŕādu secÄ«bu.

Pirms jebkādas datu bāzes darbības veikŔanas Tarantool, jums ir jāpalaiž Ŕāda komanda:

box.cfg{}

Tā rezultātā Tarantool sāks rakstÄ«t datu bāzes momentuzņēmumus un darÄ«jumu žurnālus paÅ”reizējā direktorijā.

Izveidosim secību row_version:

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

Opcija if_not_exists ļauj vairākas reizes izpildīt izveides skriptu: ja objekts pastāv, Tarantool nemēģinās to izveidot vēlreiz. Šī opcija tiks izmantota visās turpmākajās DDL komandās.

Izveidosim telpu kā piemēru.

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

Šeit mēs iestatām telpas nosaukumu (goods), lauku nosaukumi un to veidi.

Automātiski pieaugoÅ”ie lauki programmā Tarantool tiek izveidoti arÄ«, izmantojot secÄ«bas. Izveidosim automātiski pieaugoÅ”u primāro atslēgu pa laukiem 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 atbalsta vairāku veidu indeksus. Visbiežāk izmantotie indeksi ir TREE un HASH tipi, kuru pamatā ir nosaukumam atbilstoÅ”as ā€‹ā€‹struktÅ«ras. TREE ir daudzpusÄ«gākais indeksa veids. Tas ļauj izgÅ«t datus organizētā veidā. Bet vienlÄ«dzÄ«gai atlasei HASH ir piemērotāks. AttiecÄ«gi primārajai atslēgai ir ieteicams izmantot HASH (to mēs arÄ« darÄ«jām).

Lai izmantotu kolonnu row_ver lai pārsÅ«tÄ«tu mainÄ«tos datus, Ŕīs kolonnas laukiem ir jāsaista secÄ«bas vērtÄ«bas row_ver. Bet atŔķirÄ«bā no primārās atslēgas, kolonnas lauka vērtÄ«ba row_ver jāpalielina par vienu ne tikai pievienojot jaunus ierakstus, bet arÄ« mainot esoÅ”os. Å im nolÅ«kam varat izmantot trigerus. Tarantool ir divu veidu kosmosa izraisÄ«tāji: before_replace Šø on_replace. Trigeri tiek aktivizēti ikreiz, kad mainās dati telpā (katrai izmaiņu ietekmētajai kortei tiek palaista trigera funkcija). AtŔķirÄ«bā no on_replace, before_replace-trigeri ļauj modificēt kortedža, kurai tiek izpildÄ«ts trigeris, datus. AttiecÄ«gi mums der pēdējais trigeru veids.

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

Šis trigeris aizstāj lauka vērtību row_ver saglabāta virkne līdz nākamajai secības vērtībai row_version.

Lai varētu iegūt datus no kosmosa goods pēc kolonnas row_ver, izveidosim indeksu:

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

Indeksa veids - koks (TREE), jo mums bÅ«s jāizņem dati kolonnas vērtÄ«bu augoŔā secÄ«bā row_ver.

Pievienosim telpai dažus datus:

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}

Jo Pirmais lauks ir automātiski augoÅ”s skaitÄ«tājs; tā vietā mēs atstājam nulli. Tarantool automātiski aizstās nākamo vērtÄ«bu. LÄ«dzÄ«gi kā kolonnas lauku vērtÄ«ba row_ver jÅ«s varat atstāt nulli - vai nenorādÄ«t vērtÄ«bu vispār, jo Ŕī kolonna aizņem pēdējo vietu telpā.

Pārbaudīsim ievietoŔanas rezultātu:

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

Kā redzat, pirmais un pēdējais lauks tiek aizpildÄ«ts automātiski. Tagad bÅ«s viegli uzrakstÄ«t funkciju vietas izmaiņu augÅ”upielādei pa lappusei 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

Funkcija kā parametru izmanto vērtÄ«bu row_ver, no kura ir nepiecieÅ”ams izlādēt izmaiņas, un atgriež daļu no mainÄ«tajiem datiem.

Datu paraugu ņemÅ”ana programmā Tarantool tiek veikta, izmantojot indeksus. Funkcija get_goods izmanto iteratoru pēc indeksa row_ver lai saņemtu mainÄ«tos datus. Iteratora veids ir GT (lielāks par, lielāks par). Tas nozÄ«mē, ka iterators secÄ«gi Ŕķērsos indeksa vērtÄ«bas, sākot no nodotās atslēgas (lauka vērtÄ«ba row_ver).

Iterators atgriež korteņus. Lai pēc tam varētu pārsÅ«tÄ«t datus, izmantojot HTTP, korteži ir jāpārvērÅ” struktÅ«rā, kas ir ērta turpmākai serializācijai. Piemērā Å”im nolÅ«kam tiek izmantota standarta funkcija tomap. Tā vietā, lai izmantotu tomap jÅ«s varat uzrakstÄ«t savu funkciju. Piemēram, mēs varētu vēlēties pārdēvēt lauku name, neiet garām laukam code un pievienojiet lauku 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

Izvaddatu lapas izmēru (ierakstu skaitu vienā daļā) nosaka mainÄ«gais page_size. Piemērā vērtÄ«ba page_size ir 5. Reālā programmā lapas izmēram parasti ir lielāka nozÄ«me. Tas ir atkarÄ«gs no atstarpes kortedža vidējā lieluma. Optimālo lapas izmēru var noteikt empÄ«riski, mērot datu pārraides laiku. Jo lielāks ir lapas izmērs, jo mazāks ir turp un atpakaļ braucienu skaits starp sÅ«tÄ«tāju un saņēmēju. Tādā veidā jÅ«s varat samazināt kopējo izmaiņu lejupielādes laiku. Tomēr, ja lapas izmērs ir pārāk liels, mēs pārāk ilgu laiku pavadÄ«sim serverÄ«, lai serializētu paraugu. Tā rezultātā var rasties kavÄ“Å”anās, apstrādājot citus serverÄ« saņemtos pieprasÄ«jumus. Parametrs page_size var ielādēt no konfigurācijas faila. Katrai pārraidÄ«tajai telpai varat iestatÄ«t savu vērtÄ«bu. Tomēr lielākajai daļai vietu noklusējuma vērtÄ«ba (piemēram, 100) var bÅ«t piemērota.

Izpildīsim funkciju 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
...

Ņemsim lauka vērtību row_ver no pēdējās rindas un vēlreiz izsauciet funkciju:

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

Vēlreiz:

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

Kā redzat, izmantojot Ŕādā veidā, funkcija atgriež visus telpas ierakstus pēc lapas goods. Pēc pēdējās lapas seko tukÅ”a atlase.

Veiksim izmaiņas telpā:

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

Mēs esam mainÄ«juÅ”i lauka vērtÄ«bu name vienam ierakstam un pievienoja divus jaunus ierakstus.

Atkārtosim pēdējo funkcijas izsaukumu:

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

Funkcija atgrieza mainÄ«tos un pievienotos ierakstus. Tātad funkcija get_goods ļauj saņemt datus, kas ir mainÄ«juÅ”ies kopÅ” pēdējā izsaukuma, kas ir aplÅ«kojamās replikācijas metodes pamatā.

Rezultātu izsniegŔana, izmantojot HTTP JSON formātā, netiks iekļauta Ŕī raksta darbības jomā. Par to varat lasīt Ŕeit: https://habr.com/ru/company/mailru/blog/272141/

Klienta/pakalpojuma daļas realizācija

ApskatÄ«sim, kā izskatās saņēmējas puses ievieÅ”ana. Saņēmēja pusē izveidosim vietu lejupielādēto datu glabāŔanai:

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

Telpas struktÅ«ra atgādina telpas struktÅ«ru avotā. Bet, tā kā saņemtos datus nekur citur nenodosim, kolonna row_ver neatrodas adresāta zonā. Laukā id avota identifikatori tiks ierakstÄ«ti. Tāpēc uztvērēja pusē nav nepiecieÅ”ams to automātiski palielināt.

Turklāt mums ir vajadzÄ«ga vieta vērtÄ«bu saglabāŔanai 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
})

Katrai noslogotajai vietai (lauks space_name) mēs Å”eit saglabāsim pēdējo ielādēto vērtÄ«bu row_ver (lauks value). Kolonna darbojas kā primārā atslēga space_name.

Izveidosim funkciju vietas datu ielādei goods izmantojot HTTP. Lai to izdarÄ«tu, mums ir nepiecieÅ”ama bibliotēka, kas ievieÅ” HTTP klientu. Å Ä« rindiņa ielādē bibliotēku un izveido HTTP klientu:

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

Mums ir nepiecieÅ”ama arÄ« bibliotēka JSON deserializācijai:

local json = require('json')

Tas ir pietiekami, lai izveidotu datu ielādes funkciju:

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

Funkcija izpilda HTTP pieprasījumu uz url adresi un nosūta to row_ver kā parametru un atgriež pieprasījuma deserializēto rezultātu.

Saņemto datu saglabāŔanas funkcija izskatās Ŕādi:

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

Cikls datu saglabāŔanai kosmosā goods ievietots darījumā (Ŕim nolūkam tiek izmantota funkcija box.atomic), lai samazinātu diska darbību skaitu.

Visbeidzot, vietējās telpas sinhronizācijas funkcija goods izmantojot avotu, varat to Ä«stenot Ŕādi:

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

Vispirms mēs nolasām iepriekÅ” saglabāto vērtÄ«bu row_ver par telpu goods. Ja tā trÅ«kst (pirmā apmaiņas sesija), tad mēs to uztveram kā row_ver nulle. Tālāk ciklā veicam izmainÄ«to datu lejupielādi pa lappusei no avota norādÄ«tajā URL. Katrā iterācijā mēs saglabājam saņemtos datus attiecÄ«gajā lokālajā telpā un atjauninām vērtÄ«bu row_ver (kosmosā row_ver un mainÄ«gajā row_ver) - ņemiet vērtÄ«bu row_ver no pēdējās ielādēto datu rindas.

Lai pasargātu no nejauŔas cilpas (programmas kļūdas gadījumā), cilpa while var aizstāt ar for:

for _ = 1, max_req do ...

Funkcijas izpildes rezultātā sync_goods telpa goods uztvērējā būs visu kosmosa ierakstu jaunākās versijas goods avotā.

AcÄ«mredzot datu dzÄ“Å”anu nevar pārraidÄ«t Ŕādā veidā. Ja Ŕāda vajadzÄ«ba pastāv, varat izmantot dzÄ“Å”anas atzÄ«mi. Pievienot telpai goods BÅ«la lauks is_deleted un tā vietā, lai fiziski dzēstu ierakstu, mēs izmantojam loÄ£isko dzÄ“Å”anu - mēs iestatām lauka vērtÄ«bu is_deleted nozÄ«mē true. Dažreiz BÅ«la lauka vietā is_deleted ir ērtāk izmantot lauku deleted, kurā tiek saglabāts ieraksta loÄ£iskās dzÄ“Å”anas datums un laiks. Pēc loÄ£iskās dzÄ“Å”anas veikÅ”anas ieraksts, kas atzÄ«mēts dzÄ“Å”anai, tiks pārsÅ«tÄ«ts no avota uz galamērÄ·i (saskaņā ar iepriekÅ” apskatÄ«to loÄ£iku).

Secība row_ver var izmantot datu pārsūtīŔanai no citām telpām: nav nepiecieŔams izveidot atseviŔķu secību katrai pārraidītajai telpai.

Mēs apskatījām efektīvu augsta līmeņa datu replikācijas veidu lietojumprogrammās, izmantojot Tarantool DBVS.

Atzinumi

  1. Tarantool DBMS ir pievilcīgs, daudzsoloŔs produkts lielas slodzes lietojumprogrammu izveidei.
  2. Augsta lÄ«meņa datu replikācijai ir vairākas priekÅ”rocÄ«bas salÄ«dzinājumā ar zema lÄ«meņa replikāciju.
  3. Rakstā aplÅ«kotā augsta lÄ«meņa replikācijas metode ļauj samazināt pārsÅ«tÄ«to datu apjomu, pārsÅ«tot tikai tos ierakstus, kas ir mainÄ«juÅ”ies kopÅ” pēdējās apmaiņas sesijas.

Avots: www.habr.com

Pievieno komentāru