Altnivela reproduktado en Tarantool DBMS

Saluton, mi kreas aplikaĵojn por DBMS Tarantool estas platformo evoluigita de Mail.ru Group kiu kombinas alt-efikecan DBMS kaj aplikaĵoservilon en la Lua lingvo. La alta rapido de solvoj bazitaj sur Tarantool estas atingita, precipe pro subteno por la en-memora reĝimo de la DBMS kaj la kapablo efektivigi aplikaĵan komercan logikon en ununura adresspaco kun datumoj. Samtempe, datuma persisto estas certigita per ACID-transakcioj (WAL-protokolo estas konservita sur la disko). Tarantool havas enkonstruitan subtenon por reproduktado kaj sharding. Ekde la versio 2.1, demandoj en SQL-lingvo estas subtenataj. Tarantool estas malfermkoda kaj licencita sub la Simplified BSD-licenco. Ekzistas ankaŭ komerca Enterprise versio.

Altnivela reproduktado en Tarantool DBMS
Sentu la potencon! (...alinome ĝuu la agadon)

Ĉio ĉi-supra faras Tarantool alloga platformo por krei altŝarĝajn aplikojn, kiuj funkcias kun datumbazoj. En tiaj aplikoj, ekzistas ofte bezono de datenreproduktado.

Kiel menciite supre, Tarantool havas enkonstruitan kopion de datumoj. La principo de ĝia operacio estas sinsekve ekzekuti sur kopioj ĉiujn transakciojn enhavitajn en la majstra protokolo (WAL). Kutime tia reproduktado (ni plu nomos ĝin malaltnivela) estas uzata por certigi aplikaĵan faŭltoleremon kaj/aŭ por distribui la legadŝarĝon inter aretnodoj.

Altnivela reproduktado en Tarantool DBMS
Rizo. 1. Reproduktado ene de areto

Ekzemplo de alternativa scenaro estus transdoni datumojn kreitajn en unu datumbazo al alia datumbazo por prilaborado/monitorado. En ĉi-lasta kazo, pli oportuna solvo povas esti uzi alta nivelo reproduktado - datenreproduktado ĉe la aplikaĵa komerca logiknivelo. Tiuj. Ni ne uzas pretan solvon enkonstruitan en la DBMS, sed efektivigas reproduktadon memstare en la aplikaĵo, kiun ni disvolvas. Ĉi tiu aliro havas ambaŭ avantaĝojn kaj malavantaĝojn. Ni listigu la avantaĝojn.

1. Ŝparo de trafiko:

  • Vi ne povas transdoni ĉiujn datumojn, sed nur parton de ĝi (ekzemple, vi povas transdoni nur iujn tabelojn, kelkajn el iliaj kolumnoj aŭ registrojn, kiuj plenumas certan kriterion);
  • Male al malaltnivela reproduktado, kiu estas farita ade en nesinkrona (efektivigita en la nuna versio de Tarantool - 1.10) aŭ sinkrona (efektivigota en postaj versioj de Tarantool) reĝimo, altnivela reproduktado povas esti farita en sesioj (t.e., la application first synchronizes the data - interŝanĝa sesio datumoj, tiam estas paŭzo en reproduktado, post kiu okazas la sekva interŝanĝa sesio, ktp.);
  • se rekordo ŝanĝiĝis plurfoje, vi povas transdoni nur ĝian lastan version (malkiel malaltnivela reproduktado, en kiu ĉiuj ŝanĝoj faritaj sur la majstro estos reluditaj sinsekve sur la kopioj).

2. Ne estas malfacilaĵoj kun efektivigo de HTTP-interŝanĝo, kiu permesas vin sinkronigi forajn datumbazojn.

Altnivela reproduktado en Tarantool DBMS
Rizo. 2. Reproduktado per HTTP

3. La datumbazaj strukturoj, inter kiuj oni transdonas datumojn, ne devas esti la samaj (cetere, en la ĝenerala kazo, eĉ eblas uzi malsamajn DBMS-ojn, programlingvojn, platformojn ktp.).

Altnivela reproduktado en Tarantool DBMS
Rizo. 3. Reproduktado en heterogenaj sistemoj

La malavantaĝo estas ke, averaĝe, programado estas pli malfacila/kosta ol agordo, kaj anstataŭ agordi la enkonstruitan funkcion, vi devos efektivigi vian propran.

Se en via situacio la supraj avantaĝoj estas decidaj (aŭ estas necesa kondiĉo), tiam estas senco uzi altnivelan reproduktadon. Ni rigardu plurajn manierojn efektivigi altnivelan datuman reproduktadon en la Tarantool DBMS.

Minimumigo de trafiko

Do, unu el la avantaĝoj de altnivela reproduktado estas trafika ŝparado. Por ke ĉi tiu avantaĝo plene realiĝu, necesas minimumigi la kvanton da datumoj transdonitaj dum ĉiu interŝanĝa sesio. Kompreneble, ni ne forgesu, ke ĉe la fino de la sesio, la datumricevilo devas esti sinkronigita kun la fonto (almenaŭ por tiu parto de la datumoj, kiu estas implikita en reproduktado).

Kiel minimumigi la kvanton de datumoj transdonitaj dum altnivela reproduktado? Simpla solvo povus esti elekti datumojn laŭ dato kaj horo. Por fari tion, vi povas uzi la dattempan kampon jam ekzistantan en la tabelo (se ĝi ekzistas). Ekzemple, "ordo" dokumento povas havi kampon "postulata ordo ekzekuttempo" - delivery_time. La problemo kun ĉi tiu solvo estas, ke la valoroj en ĉi tiu kampo ne devas esti en la sekvenco, kiu respondas al la kreado de ordoj. Do ni ne povas memori la maksimuman kampan valoron delivery_time, transdonita dum la antaŭa interŝanĝsesio, kaj dum la sekva interŝanĝsesio elektu ĉiujn rekordojn kun pli alta kampa valoro delivery_time. Rekordoj kun pli malalta kampa valoro eble estis aldonitaj inter interŝanĝsesioj delivery_time. Ankaŭ, la ordo povus esti suferinta ŝanĝojn, kiuj tamen ne influis la kampon delivery_time. En ambaŭ kazoj, la ŝanĝoj ne estos translokigitaj de la fonto al la celo. Por solvi ĉi tiujn problemojn, ni devos translokigi datumojn "interkovrantaj". Tiuj. en ĉiu interŝanĝa sesio ni transdonos ĉiujn datumojn kun la kampa valoro delivery_time, superante iun punkton en la pasinteco (ekzemple, N horojn de la nuna momento). Tamen, estas evidente, ke por grandaj sistemoj ĉi tiu aliro estas tre superflua kaj povas redukti la trafikŝparojn al kiuj ni strebas al nenio. Krome, la tabelo transdonata eble ne havas kampon asociitan kun dato-tempo.

Alia solvo, pli kompleksa laŭ efektivigo, estas agnoski la ricevon de datumoj. En ĉi tiu kazo, dum ĉiu interŝanĝa sesio, ĉiuj datumoj estas transdonitaj, kies ricevo ne estis konfirmita de la ricevanto. Por efektivigi ĉi tion, vi devos aldoni Bulean kolumnon al la fonta tabelo (ekzemple, is_transferred). Se la ricevilo agnoskas la ricevon de la rekordo, la responda kampo prenas la valoron true, post kio la eniro ne plu estas implikita en interŝanĝoj. Ĉi tiu efektiviga opcio havas la jenajn malavantaĝojn. Unue, por ĉiu rekordo transdonita, agnosko devas esti generita kaj sendita. Malglate parolante, ĉi tio povus esti komparebla al duobligi la kvanton de datumoj transdonitaj kaj konduki al duobligo de la nombro da rondveturoj. Due, ne estas ebleco sendi la saman rekordon al pluraj riceviloj (la unua ricevanto konfirmos kvitancon por si kaj por ĉiuj aliaj).

Metodo kiu ne havas la malavantaĝojn donitajn supre estas aldoni kolumnon al la transdonita tabelo por spuri ŝanĝojn en ĝiaj vicoj. Tia kolumno povas esti de dato-tempa tipo kaj devas esti agordita/ĝisdatigita de la aplikaĵo al la nuna tempo ĉiufoje kiam rekordoj estas aldonitaj/ŝanĝitaj (atome kun la aldono/ŝanĝo). Ekzemple, ni nomu la kolumnon update_time. Konservante la maksimuman kampan valoron de ĉi tiu kolumno por la translokigitaj registroj, ni povas komenci la sekvan interŝanĝan sesion kun ĉi tiu valoro (elektu registrojn kun la kampa valoro update_time, superante la antaŭe konservitan valoron). La problemo kun ĉi-lasta aliro estas ke datumoj ŝanĝoj povas okazi en aroj. Kiel rezulto de la kampovaloroj en la kolumno update_time eble ne estas unika. Tiel, ĉi tiu kolumno ne povas esti uzata por porciigita (paĝo-post-paĝa) datuma eligo. Por montri datumojn paĝon post paĝo, vi devos inventi pliajn mekanismojn, kiuj plej verŝajne havos tre malaltan efikecon (ekzemple, retrovi el la datumbazo ĉiujn rekordojn kun la valoro update_time pli alta ol donita kaj produktanta certan nombron da rekordoj, komencante de certa ofseto de la komenco de la specimeno).

Vi povas plibonigi la efikecon de transdono de datumoj iomete plibonigante la antaŭan aliron. Por fari tion, ni uzos la entjeran tipon (longa entjero) kiel la kolumnkampajn valorojn por spuri ŝanĝojn. Ni nomu la kolumnon row_ver. La kampa valoro de ĉi tiu kolumno ankoraŭ devas esti agordita/ĝisdatigita ĉiufoje kiam rekordo estas kreita/modigata. Sed en ĉi tiu kazo, al la kampo ne estos asignita la nuna dato-tempo, sed la valoro de iu nombrilo, pliigita je unu. Kiel rezulto, la kolumno row_ver enhavos unikajn valorojn kaj uzeblas ne nur por montri "delta" datumojn (datenoj aldonitaj/ŝanĝitaj ekde la fino de la antaŭa interŝanĝa sesio), sed ankaŭ por simple kaj efike dividi ĝin en paĝojn.

La lasta proponita metodo por minimumigi la kvanton de datumoj transdonitaj en la kadro de altnivela reproduktado ŝajnas al mi la plej optimuma kaj universala. Ni rigardu ĝin pli detale.

Transdonado de Datumoj Uzante Vicversio-Nombrilon

Efektivigo de la servilo/majstra parto

En MS SQL Server, ekzistas speciala kolumna tipo por efektivigi ĉi tiun aliron - rowversion. Ĉiu datumbazo havas nombrilon kiu pliiĝas je unu ĉiufoje kiam rekordo estas aldonita/ŝanĝita en tabelo kiu havas kolumnon kiel rowversion. La valoro de ĉi tiu nombrilo estas aŭtomate atribuita al la kampo de ĉi tiu kolumno en la aldonita/ŝanĝita registro. La Tarantool DBMS ne havas similan enkonstruitan mekanismon. Tamen, en Tarantool ne estas malfacile efektivigi ĝin permane. Ni rigardu kiel tio estas farita.

Unue, iom da terminologio: tabeloj en Tarantool nomiĝas spacoj, kaj registroj nomiĝas opoj. En Tarantool vi povas krei sekvencojn. Sekvencoj estas nenio pli ol nomitaj generatoroj de ordigitaj entjervaloroj. Tiuj. ĝuste ĉi tio ni bezonas por niaj celoj. Malsupre ni kreos tian sinsekvon.

Antaŭ ol fari ajnan datumbazan operacion en Tarantool, vi devas ruli la sekvan komandon:

box.cfg{}

Kiel rezulto, Tarantool komencos skribi datumbazajn momentfotojn kaj transakciajn protokolojn al la nuna dosierujo.

Ni kreu sekvencon row_version:

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

Opcio if_not_exists permesas la krean skripton esti ekzekutita plurfoje: se la objekto ekzistas, Tarantool ne provos krei ĝin denove. Ĉi tiu opcio estos uzata en ĉiuj postaj DDL-komandoj.

Ni kreu spacon kiel ekzemplon.

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

Ĉi tie ni fiksas la nomon de la spaco (goods), kamponomoj kaj iliaj specoj.

Aŭto-pliigaj kampoj en Tarantool ankaŭ estas kreitaj uzante sekvencojn. Ni kreu aŭtomate pligrandigan ĉefan ŝlosilon laŭ kampo 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 subtenas plurajn specojn de indeksoj. La plej ofte uzataj indeksoj estas TREE kaj HASH-tipoj, kiuj baziĝas sur strukturoj respondaj al la nomo. ARBO estas la plej diverstalenta indeksa tipo. Ĝi permesas vin preni datumojn en organizita maniero. Sed por egaleca elekto, HASH estas pli taŭga. Sekve, estas konsilinde uzi HASH por la ĉefa ŝlosilo (kion ni faris).

Por uzi la kolumnon row_ver por transdoni ŝanĝitajn datumojn, vi devas ligi sekvencovalorojn al la kampoj de ĉi tiu kolumno row_ver. Sed male al la ĉefa ŝlosilo, la kolumna kampovaloro row_ver devus pliiĝi je unu ne nur kiam oni aldonas novajn rekordojn, sed ankaŭ kiam oni ŝanĝas ekzistantajn. Vi povas uzi ellasilon por ĉi tio. Tarantool havas du specojn de spacaj ellasiloj: before_replace и on_replace. Ellasiloj estas lanĉitaj kiam ajn la datumoj en la spaco ŝanĝiĝas (por ĉiu opo trafita per la ŝanĝoj, ellasilfunkcio estas lanĉita). Male al on_replace, before_replace-triggers permesas modifi la datumojn de la opo por kiu la ellasilo estas ekzekutita. Sekve, la lasta speco de ellasiloj konvenas al ni.

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

La sekva ellasilo anstataŭigas la kampan valoron row_ver stokita opo al la sekva valoro de la sekvenco row_version.

Por povi ĉerpi datumojn el spaco goods per kolono row_ver, ni kreu indekson:

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

Indeksa tipo - arbo (TREE), ĉar ni devos ĉerpi la datumojn en kreskanta ordo de la valoroj en la kolumno row_ver.

Ni aldonu kelkajn datumojn al la spaco:

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}

Ĉar La unua kampo estas aŭtomate pliiga nombrilo; ni pasas anstataŭe. Tarantool aŭtomate anstataŭigos la sekvan valoron. Simile, kiel la valoro de la kolumnaj kampoj row_ver vi povas pasi nil - aŭ tute ne specifi la valoron, ĉar ĉi tiu kolumno okupas la lastan pozicion en la spaco.

Ni kontrolu la enmetrezulton:

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

Kiel vi povas vidi, la unua kaj lasta kampoj estas plenigitaj aŭtomate. Nun estos facile verki funkcion por paĝo-post-paĝa alŝuto de spacŝanĝoj 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

La funkcio prenas kiel parametron la valoron row_ver, ekde kiu necesas malŝarĝi ŝanĝojn, kaj resendas parton de la ŝanĝitaj datumoj.

Datenspecimenigo en Tarantool estas farita per indeksoj. Funkcio get_goods uzas ripetanton per indekso row_ver ricevi ŝanĝitajn datumojn. Iterator-tipo estas GT (Greater Than, pli granda ol). Ĉi tio signifas, ke la iteratoro sinsekve trairos la indeksajn valorojn ekde la pasita ŝlosilo (kampa valoro row_ver).

La iteratoro resendas opoj. Por poste povi transdoni datumojn per HTTP, necesas konverti la opoj al strukturo oportuna por posta seriigo. La ekzemplo uzas la norman funkcion por tio tomap. Anstataŭ uzi tomap vi povas skribi vian propran funkcion. Ekzemple, ni eble volas renomi kampon name, ne preterpasu la kampon code kaj aldonu kampon 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

La paĝgrandeco de la eligdatenoj (la nombro da rekordoj en unu parto) estas determinita de la variablo page_size. En la ekzemplo la valoro page_size estas 5. En reala programo, la paĝa grandeco kutime pli gravas. Ĝi dependas de la averaĝa grandeco de la spaca opo. La optimuma paĝa grandeco povas esti determinita empirie per mezurado de datumtransiga tempo. Ju pli granda estas la paĝa grandeco, des pli malgranda la nombro da rondveturoj inter la sendo kaj ricevanta flankoj. Tiel vi povas redukti la ĝeneralan tempon por elŝuti ŝanĝojn. Tamen, se la paĝa grandeco estas tro granda, ni pasigos tro longe sur la servilo seriigante la specimenon. Kiel rezulto, povas esti prokrastoj en prilaborado de aliaj petoj venantaj al la servilo. Parametro page_size povas esti ŝargita de la agorda dosiero. Por ĉiu elsendita spaco, vi povas agordi sian propran valoron. Tamen, por plej multaj spacoj la defaŭlta valoro (ekzemple, 100) povas esti taŭga.

Ni ekzekutu la funkcion 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
...

Ni prenu la kampan valoron row_ver de la lasta linio kaj voku la funkcion denove:

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

Denove:

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

Kiel vi povas vidi, kiam ĝi estas uzata tiel, la funkcio resendas ĉiujn spacajn rekordojn paĝo post paĝo goods. La lasta paĝo estas sekvata de malplena elekto.

Ni faru ŝanĝojn al la spaco:

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

Ni ŝanĝis la kampovaloron name por unu enskribo kaj aldonis du novajn enskribojn.

Ni ripetu la lastan funkcion:

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

La funkcio resendis la ŝanĝitajn kaj aldonitajn rekordojn. Do la funkcio get_goods permesas al vi ricevi datumojn, kiuj ŝanĝiĝis ekde ĝia lasta voko, kiu estas la bazo de la reprodukta metodo konsiderata.

Ni lasos la emision de rezultoj per HTTP en la formo de JSON ekster la amplekso de ĉi tiu artikolo. Vi povas legi pri tio ĉi tie: https://habr.com/ru/company/mailru/blog/272141/

Efektivigo de la parto kliento/sklavo

Ni rigardu kiel aspektas la efektivigo de la ricevanta flanko. Ni kreu spacon ĉe la riceva flanko por konservi la elŝutitajn datumojn:

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

La strukturo de la spaco similas la strukturon de la spaco en la fonto. Sed ĉar ni ne tuj transdonos la ricevitajn datumojn aliloke, la kolumno row_ver ne estas en la spaco de la ricevanto. En kampo id fontidentigiloj estos registritaj. Tial, ĉe la ricevilo-flanko ne necesas igi ĝin aŭtomate pliigo.

Krome, ni bezonas spacon por konservi valorojn 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
})

Por ĉiu ŝarĝita spaco (kampo space_name) ni konservos la lastan ŝarĝitan valoron ĉi tie row_ver (kampo value). La kolumno funkcias kiel la ĉefa ŝlosilo space_name.

Ni kreu funkcion por ŝargi spacajn datumojn goods per HTTP. Por fari tion, ni bezonas bibliotekon, kiu efektivigas HTTP-klienton. La sekva linio ŝarĝas la bibliotekon kaj instantigas la HTTP-klienton:

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

Ni ankaŭ bezonas bibliotekon por json-deseriligo:

local json = require('json')

Ĉi tio sufiĉas por krei funkcion de ŝarĝo de datumoj:

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

La funkcio efektivigas HTTP-peton al la url-adreso kaj sendas ĝin row_ver kiel parametro kaj resendas la deserialigitan rezulton de la peto.

La funkcio por konservi ricevitajn datumojn aspektas jene:

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

Ciklo de konservado de datumoj al spaco goods metita en transakcion (la funkcio estas uzata por tio box.atomic) por redukti la nombron da diskoperacioj.

Fine, la loka spaca sinkroniga funkcio goods kun fonto vi povas efektivigi ĝin jene:

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

Unue ni legas la antaŭe konservitan valoron row_ver por spaco goods. Se ĝi mankas (la unua interŝanĝa sesio), tiam ni prenas ĝin kiel row_ver nulo. Poste en la ciklo ni elŝutas paĝon post paĝo de la ŝanĝitaj datumoj de la fonto ĉe la specifita url. Ĉe ĉiu ripeto, ni konservas la ricevitajn datumojn al la taŭga loka spaco kaj ĝisdatigas la valoron row_ver (en la spaco row_ver kaj en la variablo row_ver) - prenu la valoron row_ver de la lasta linio de ŝarĝitaj datumoj.

Por protekti kontraŭ hazarda buklo (kaze de eraro en la programo), la buklo while povas esti anstataŭigita per for:

for _ = 1, max_req do ...

Kiel rezulto de ekzekuto de la funkcio sync_goods spaco goods la ricevilo enhavos la lastajn versiojn de ĉiuj spacaj rekordoj goods en la fonto.

Evidente, forigo de datumoj ne povas esti dissendita tiamaniere. Se tia bezono ekzistas, vi povas uzi forigmarkon. Aldonu al spaco goods bulea kampo is_deleted kaj anstataŭ fizike forigi rekordon, ni uzas logikan forigon - ni fiksas la kampan valoron is_deleted en signifon true. Foje anstataŭ bulea kampo is_deleted estas pli oportune uzi la kampon deleted, kiu konservas la daton de la logika forigo de la rekordo. Post plenumi logikan forigon, la rekordo markita por forigo estos transdonita de la fonto al la celloko (laŭ la logiko diskutita supre).

Sinsekvo row_ver povas esti uzata por transdoni datumojn de aliaj spacoj: ne necesas krei apartan sinsekvon por ĉiu elsendita spaco.

Ni rigardis efikan manieron de altnivela reproduktado de datumoj en aplikoj uzantaj la Tarantool DBMS.

trovoj

  1. Tarantool DBMS estas alloga, promesplena produkto por krei altŝarĝajn aplikojn.
  2. Altnivela datenreproduktado havas kelkajn avantaĝojn super malaltnivela reproduktado.
  3. La altnivela reprodukta metodo diskutita en la artikolo permesas vin minimumigi la kvanton de transdonitaj datumoj transdonante nur tiujn rekordojn, kiuj ŝanĝiĝis ekde la lasta interŝanĝa sesio.

fonto: www.habr.com

Aldoni komenton