Tarantool ДҚБЖ жоғары деңгейлі репликация

Сәлеметсіз бе, мен ДҚБЖ үшін қосымшалар жасаймын Тарантол — жоғары өнімді ДҚБЖ мен Lua тіліндегі қолданбалы серверді біріктіретін Mail.ru Group әзірлеген платформа. Tarantool негізіндегі шешімдердің жоғары жылдамдығына, атап айтқанда, ДҚБЖ жадтағы режимін қолдау және деректермен бір мекенжай кеңістігінде қолданбалы бизнес логикасын орындау мүмкіндігі арқасында қол жеткізілді. Бұл ретте деректердің тұрақтылығы ACID транзакцияларының көмегімен қамтамасыз етіледі (дискіде WAL журналы сақталады). Tarantool-да репликация мен бөлшектеуге арналған кірістірілген қолдау бар. 2.1 нұсқасынан бастап SQL тіліндегі сұрауларға қолдау көрсетіледі. Tarantool ашық бастапқы коды болып табылады және оңайлатылған BSD лицензиясы бойынша лицензияланған. Коммерциялық Enterprise нұсқасы да бар.

Tarantool ДҚБЖ жоғары деңгейлі репликация
Күшті сезініңіз! (...қойылымнан ләззат алыңыз)

Жоғарыда айтылғандардың барлығы Tarantool-ті дерекқорлармен жұмыс істейтін жоғары жүктемелі қосымшаларды жасау үшін тартымды платформаға айналдырады. Мұндай қолданбаларда жиі деректерді репликациялау қажеттілігі туындайды.

Жоғарыда айтылғандай, Tarantool-да кірістірілген деректер репликасы бар. Оның жұмыс істеу принципі репликада негізгі журналда (WAL) қамтылған барлық транзакцияларды дәйекті түрде орындау болып табылады. Әдетте мұндай репликация (біз оны әрі қарай атаймыз төмен деңгейлі) қолданбалы ақауларға төзімділікті қамтамасыз ету және/немесе оқу жүктемесін кластер түйіндері арасында бөлу үшін пайдаланылады.

Tarantool ДҚБЖ жоғары деңгейлі репликация
Күріш. 1. Кластер ішіндегі репликация

Баламалы сценарийдің мысалы бір дерекқорда жасалған деректерді өңдеу/бақылау үшін басқа дерекқорға тасымалдау болады. Соңғы жағдайда пайдалану ыңғайлырақ шешім болуы мүмкін жоғары деңгей репликация – қолданбаның іскерлік логикалық деңгейінде деректерді репликациялау. Анау. Біз ДҚБЖ-ға енгізілген дайын шешімді пайдаланбаймыз, бірақ біз жасап жатқан қолданбаның ішінде репликацияны өз бетімізше жүзеге асырамыз. Бұл тәсілдің артықшылықтары да, кемшіліктері де бар. Артықшылықтарын тізіп көрейік.

1. Трафикті үнемдеу:

  • Сіз барлық деректерді тасымалдай алмайсыз, бірақ оның бір бөлігін ғана (мысалы, тек кейбір кестелерді, олардың кейбір бағандарын немесе белгілі бір критерийге сәйкес келетін жазбаларды тасымалдауға болады);
  • Үздіксіз асинхронды (Tarantool – 1.10 нұсқасының ағымдағы нұсқасында енгізілген) немесе синхронды (Tarantool бағдарламасының келесі нұсқаларында жүзеге асырылатын) режимде орындалатын төменгі деңгейлі репликациядан айырмашылығы, жоғары деңгейлі репликация сеанстарда (яғни, қолданба алдымен деректерді синхрондайды - алмасу сеансы деректері, содан кейін репликацияда үзіліс болады, содан кейін келесі алмасу сеансы орын алады және т.б.);
  • егер жазба бірнеше рет өзгерсе, оның соңғы нұсқасын ғана тасымалдауға болады (төмен деңгейлі репликациядан айырмашылығы, онда негізгі құрылғыда жасалған барлық өзгертулер көшірмелерде дәйекті түрде ойнатылады).

2. Қашықтағы дерекқорларды синхрондауға мүмкіндік беретін HTTP алмасуды енгізуде қиындықтар жоқ.

Tarantool ДҚБЖ жоғары деңгейлі репликация
Күріш. 2. HTTP арқылы репликация

3. Деректер тасымалданатын деректер қорының құрылымдары бірдей болуы міндетті емес (сонымен қатар, жалпы жағдайда тіпті әртүрлі ДҚБЖ, бағдарламалау тілдері, платформалар және т.б. пайдалануға болады).

Tarantool ДҚБЖ жоғары деңгейлі репликация
Күріш. 3. Гетерогенді жүйелердегі репликация

Кемшілігі мынада, орта есеппен бағдарламалау конфигурациядан гөрі қиынырақ/қымбатты және кірістірілген функционалдылықты теңшеудің орнына сіз өзіңізді іске асыруға тура келеді.

Егер сіздің жағдайыңызда жоғарыда аталған артықшылықтар шешуші болса (немесе қажетті шарт болса), онда жоғары деңгейлі репликацияны пайдалану мағынасы бар. Tarantool ДҚБЖ жоғары деңгейлі деректер репликациясын жүзеге асырудың бірнеше жолдарын қарастырайық.

Трафикті азайту

Сонымен, жоғары деңгейлі репликацияның артықшылықтарының бірі трафикті үнемдеу болып табылады. Бұл артықшылық толығымен жүзеге асуы үшін әрбір алмасу сеансы кезінде берілетін деректер көлемін барынша азайту қажет. Әрине, сеанстың соңында деректерді қабылдаушы көзбен синхрондалу керек екенін ұмытпау керек (кем дегенде репликацияға қатысатын деректер бөлігі үшін).

Жоғары деңгейлі репликация кезінде тасымалданатын деректер көлемін қалай азайтуға болады? Күні мен уақыты бойынша деректерді таңдау оңай шешім болуы мүмкін. Ол үшін кестеде бұрыннан бар күн-уақыт өрісін пайдалануға болады (егер ол бар болса). Мысалы, «тапсырыс» құжатында «тапсырысты орындаудың талап етілетін уақыты» өрісі болуы мүмкін - delivery_time. Бұл шешімнің мәселесі мынада, бұл өрістегі мәндер тапсырыстарды құруға сәйкес келетін дәйектілікте болмауы керек. Сондықтан біз максималды өріс мәнін есте сақтай алмаймыз delivery_time, алдыңғы алмасу сеансы кезінде жіберіледі және келесі алмасу сеансы кезінде өріс мәні жоғары барлық жазбаларды таңдаңыз delivery_time. Төмен өріс мәні бар жазбалар алмасу сеанстары арасында қосылған болуы мүмкін delivery_time. Сондай-ақ, тапсырыс өзгерістерге ұшырауы мүмкін еді, бірақ бұл өріске әсер етпеді delivery_time. Екі жағдайда да өзгертулер көзден тағайындалған жерге тасымалданбайды. Бұл мәселелерді шешу үшін бізге деректерді «қабатталатын» тасымалдау қажет болады. Анау. әрбір алмасу сеансында біз өріс мәні бар барлық деректерді тасымалдаймыз delivery_time, өткендегі кейбір нүктеден асып түседі (мысалы, ағымдағы сәттен бастап N сағат). Дегенмен, үлкен жүйелер үшін бұл тәсіл өте артық және біз ұмтылып отырған трафикті үнемдеуді азайта алатыны анық. Бұған қоса, тасымалданатын кестеде күн-уақытпен байланысты өріс болмауы мүмкін.

Басқа шешім, іске асыру тұрғысынан күрделірек, деректердің қабылданғанын растау болып табылады. Бұл жағдайда әрбір алмасу сеансы кезінде барлық деректер жіберіледі, олардың алынғаны алушымен расталмаған. Мұны жүзеге асыру үшін бастапқы кестеге логикалық баған қосу керек (мысалы, is_transferred). Егер қабылдаушы жазбаны алғанын растаса, сәйкес өріс мәнді қабылдайды true, одан кейін жазба енді алмасуға тартылмайды. Бұл іске асыру опциясының келесі кемшіліктері бар. Біріншіден, әрбір тасымалданған жазба үшін растау жасалуы және жіберілуі керек. Шамамен айтқанда, бұл тасымалданатын деректер көлемін екі есе көбейтумен салыстыруға болады және айналма сапарлар санын екі есе арттыруға әкеледі. Екіншіден, бір жазбаны бірнеше қабылдағышқа жіберу мүмкіндігі жоқ (бірінші алған қабылдаушы өзі үшін де, қалғандары үшін де түбіртекті растайды).

Жоғарыда келтірілген кемшіліктері жоқ әдіс жолдарындағы өзгерістерді бақылау үшін тасымалданатын кестеге баған қосу болып табылады. Мұндай баған күн-уақыт түрі болуы мүмкін және жазбалар қосылған/өзгертілген сайын (қосу/өзгертумен бірге атомдық түрде) ағымдағы уақытқа қолданба арқылы орнатылуы/жаңартуы қажет. Мысал ретінде бағанды ​​шақырайық update_time. Тасымалданған жазбалар үшін осы бағанның максималды өріс мәнін сақтау арқылы келесі алмасу сеансын осы мәнмен бастауға болады (өріс мәні бар жазбаларды таңдаңыз. update_time, бұрын сақталған мәннен асып кетеді). Соңғы тәсілдегі мәселе деректердің өзгерістері пакеттерде болуы мүмкін. Бағандағы өріс мәндерінің нәтижесінде update_time бірегей болмауы мүмкін. Осылайша, бұл бағанды ​​бөліктерге бөлінген (бет бойынша) деректерді шығару үшін пайдалану мүмкін емес. Деректерді бет бойынша көрсету үшін сізге тиімділігі өте төмен болатын қосымша механизмдерді ойлап табуға тура келеді (мысалы, дерекқордан мәні бар барлық жазбаларды шығарып алу). update_time берілгеннен жоғары және үлгінің басынан белгілі бір орын ауыстырудан бастап белгілі бір жазбалар санын шығарады).

Алдыңғы тәсілді сәл жақсарту арқылы деректерді беру тиімділігін арттыруға болады. Мұны істеу үшін біз өзгерістерді бақылау үшін баған өрісінің мәндері ретінде бүтін түрін (ұзын бүтін) қолданамыз. Бағанға ат қояйық row_ver. Бұл бағанның өріс мәні жазба жасалған/өзгертілген сайын орнатылуы/жаңартылуы керек. Бірақ бұл жағдайда өріске ағымдағы күн-уақыт тағайындалмайды, бірақ кейбір есептегіштің мәні біреуге артады. Нәтижесінде, баған row_ver бірегей мәндерден тұрады және оны тек «дельта» деректерін көрсету үшін ғана емес (алдыңғы алмасу сеансы аяқталғаннан бері қосылған/өзгертілген деректер), сонымен қатар оны жай және тиімді беттерге бөлу үшін де пайдалануға болады.

Жоғары деңгейлі репликация шеңберінде тасымалданатын деректер көлемін азайтудың соңғы ұсынылған әдісі маған ең оңтайлы және әмбебап болып көрінеді. Оны толығырақ қарастырайық.

Жол нұсқасы санауышы арқылы деректерді беру

Серверді/басты бөлікті іске асыру

MS SQL серверінде бұл тәсілді жүзеге асыру үшін арнайы баған түрі бар - rowversion. Әрбір дерекқорда осындай бағанасы бар кестеге жазба қосылған/өзгертілген сайын бір есе өсетін есептегіш бар rowversion. Бұл есептегіштің мәні қосылған/өзгертілген жазбадағы осы бағанның өрісіне автоматты түрде тағайындалады. Tarantool ДҚБЖ-да ұқсас кіріктірілген механизм жоқ. Дегенмен, Tarantool-да оны қолмен жүзеге асыру қиын емес. Мұның қалай жасалатынын қарастырайық.

Біріншіден, кішкене терминология: Tarantool-тегі кестелер бос орындар деп аталады, ал жазбалар кортеждер деп аталады. Tarantool бағдарламасында тізбектер жасауға болады. Тізбектер реттелген бүтін мәндердің атаулы генераторларынан басқа ештеңе емес. Анау. бұл біздің мақсаттарымызға қажет нәрсе. Төменде біз осындай реттілік жасаймыз.

Tarantool бағдарламасында кез келген дерекқор әрекетін орындамас бұрын келесі пәрменді орындау керек:

box.cfg{}

Нәтижесінде Tarantool ағымдағы каталогқа дерекқор суреттері мен транзакция журналдарын жаза бастайды.

Тізбекті құрайық row_version:

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

Опция if_not_exists жасау сценарийін бірнеше рет орындауға мүмкіндік береді: нысан бар болса, Tarantool оны қайтадан жасауға әрекеттенбейді. Бұл опция барлық келесі DDL пәрмендерінде пайдаланылады.

Мысал ретінде кеңістік құрайық.

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

Мұнда біз кеңістіктің атын қоямыз (goods), өріс атаулары және олардың түрлері.

Tarantool бағдарламасындағы автоматты ұлғайту өрістері де реттіліктер арқылы жасалады. Өріс бойынша автоматты түрде өсетін бастапқы кілт жасайық 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 бірнеше индекс түрлерін қолдайды. Ең жиі қолданылатын индекстер TREE және HASH типтері болып табылады, олар атауға сәйкес құрылымдарға негізделген. TREE – индекстің ең әмбебап түрі. Ол деректерді ұйымдасқан түрде алуға мүмкіндік береді. Бірақ теңдік таңдау үшін HASH қолайлырақ. Тиісінше, бастапқы кілт үшін HASH қолданған жөн (біз осылай істедік).

Бағанды ​​пайдалану үшін row_ver өзгертілген деректерді тасымалдау үшін осы бағанның өрістеріне реттілік мәндерін байланыстыру керек row_ver. Бірақ негізгі кілттен айырмашылығы, баған өрісінің мәні row_ver тек жаңа жазбаларды қосқанда ғана емес, бұрыннан барларын өзгерткенде де бір есе артуы керек. Ол үшін триггерлерді пайдалануға болады. Tarantool-да ғарыштық триггерлердің екі түрі бар: before_replace и on_replace. Триггерлер кеңістіктегі деректер өзгерген сайын іске қосылады (өзгертулер әсер еткен әрбір кортеж үшін триггер функциясы іске қосылады). Ұнайды on_replace, before_replace-триггерлер триггер орындалатын кортеж деректерін өзгертуге мүмкіндік береді. Тиісінше, триггерлердің соңғы түрі бізге сәйкес келеді.

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

Келесі триггер өріс мәнін ауыстырады row_ver тізбектің келесі мәніне сақталған кортеж row_version.

Ғарыштан мәліметтерді алу үшін goods баған бойынша row_ver, индексті құрайық:

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

Индекс түрі - ағаш (TREE), өйткені біз деректерді бағандағы мәндердің өсу ретімен шығаруымыз керек row_ver.

Кеңістікке кейбір деректерді қосамыз:

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}

Өйткені Бірінші өріс - автоматты түрде ұлғайтылатын есептегіш; біз оның орнына нөлге өтеміз. Tarantool келесі мәнді автоматты түрде ауыстырады. Сол сияқты, баған өрістерінің мәні ретінде row_ver нөлді өтуге болады немесе мәнді мүлде көрсетпейсіз, себебі бұл баған кеңістіктегі соңғы орынды алады.

Енгізу нәтижесін тексерейік:

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

Көріп отырғаныңыздай, бірінші және соңғы өрістер автоматты түрде толтырылады. Енді кеңістіктегі өзгерістерді бет-бет жүктеп салу функциясын жазу оңай болады 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

Функция параметр ретінде мәнді қабылдайды row_ver, одан бастап өзгерістерді алып тастау қажет және өзгертілген деректердің бір бөлігін қайтарады.

Tarantool бағдарламасында деректер таңдау индекстер арқылы жүзеге асырылады. Функция get_goods индекс бойынша итераторды пайдаланады row_ver өзгертілген деректерді алу үшін. Итератор түрі GT (Үлкен, одан үлкен). Бұл итератор берілген кілттен (өріс мәні) бастап индекс мәндерін дәйекті түрде айналып өтетінін білдіреді. row_ver).

Итератор кортеждерді қайтарады. HTTP арқылы деректерді кейіннен тасымалдау мүмкіндігін алу үшін кортеждерді кейінгі сериялау үшін ыңғайлы құрылымға түрлендіру қажет. Мысал бұл үшін стандартты функцияны пайдаланады tomap. Қолданудың орнына tomap сіз өзіңіздің функцияңызды жаза аласыз. Мысалы, біз өрістің атын өзгертуді қалауымыз мүмкін name, өрісті өткізбеңіз code және өрісті қосыңыз 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

Шығарылатын деректердің бет өлшемі (бір бөліктегі жазбалар саны) айнымалымен анықталады page_size. Мысалда мән page_size 5. Нақты бағдарламада бет өлшемі әдетте маңыздырақ. Бұл кеңістік кортежінің орташа өлшеміне байланысты. Оңтайлы бет өлшемін деректерді беру уақытын өлшеу арқылы эмпирикалық түрде анықтауға болады. Бет өлшемі неғұрлым үлкен болса, жіберуші және қабылдаушы тараптар арасындағы айналма сапарлар саны соғұрлым аз болады. Осылайша сіз өзгерістерді жүктеп алудың жалпы уақытын қысқартуға болады. Дегенмен, бет өлшемі тым үлкен болса, үлгіні сериялау үшін серверде тым ұзақ уақыт жұмсаймыз. Нәтижесінде серверге келетін басқа сұрауларды өңдеуде кешігулер болуы мүмкін. Параметр page_size конфигурация файлынан жүктеуге болады. Әрбір жіберілген кеңістік үшін өз мәнін орнатуға болады. Дегенмен, бос орындардың көпшілігі үшін әдепкі мән (мысалы, 100) қолайлы болуы мүмкін.

Функцияны орындайық 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
...

Өріс мәнін алайық row_ver соңғы жолдан бастап функцияны қайта шақырыңыз:

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

Тағы бір рет:

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

Көріп отырғаныңыздай, осылай пайдаланылғанда, функция барлық бос орын жазбаларын бет бойынша қайтарады goods. Соңғы бетте бос таңдау бар.

Кеңістікке өзгерістер енгізейік:

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

Біз өріс мәнін өзгерттік name бір жазба үшін және екі жаңа жазба қосылды.

Соңғы функция шақыруын қайталайық:

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

Функция өзгертілген және қосылған жазбаларды қайтарды. Сонымен, функция get_goods қарастырылып отырған репликация әдісінің негізі болып табылатын соңғы шақырудан кейін өзгерген деректерді алуға мүмкіндік береді.

Біз HTTP арқылы JSON түрінде нәтижелерді шығаруды осы мақаланың шеңберінен тыс қалдырамыз. Бұл туралы мына жерден оқи аласыз: https://habr.com/ru/company/mailru/blog/272141/

Клиент/құлдық бөлігін жүзеге асыру

Қабылдаушы тараптың іске асыруы қандай болатынын қарастырайық. Жүктелген деректерді сақтау үшін қабылдау жағында бос орын жасайық:

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

Кеңістіктің құрылымы көздегі кеңістіктің құрылымына ұқсайды. Бірақ біз алынған деректерді басқа жерде өткізбейтіндіктен, баған row_ver алушының кеңістігінде емес. Алаңда id дереккөз идентификаторлары жазылады. Сондықтан қабылдағыш жағында оны автоматты түрде ұлғайтудың қажеті жоқ.

Бұған қоса, мәндерді сақтау үшін бізге бос орын қажет 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
})

Әрбір жүктелген кеңістік үшін (өріс space_name) соңғы жүктелген мәнді осында сақтаймыз row_ver (өріс value). Баған негізгі кілт ретінде әрекет етеді space_name.

Кеңістіктік деректерді жүктеу функциясын құрайық goods HTTP арқылы. Ол үшін бізге HTTP клиентін іске асыратын кітапхана қажет. Келесі жол кітапхананы жүктейді және HTTP клиентін жасайды:

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

Бізге json сериясын жою үшін кітапхана қажет:

local json = require('json')

Бұл деректерді жүктеу функциясын жасау үшін жеткілікті:

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

Функция url мекенжайына HTTP сұрауын орындайды және оны жібереді row_ver параметр ретінде және сұраудың сериядан шығарылған нәтижесін қайтарады.

Алынған деректерді сақтау функциясы келесідей:

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

Деректерді кеңістікке сақтау циклі goods транзакцияға орналастырылған (бұл үшін функция пайдаланылады box.atomic) диск операцияларының санын азайту үшін.

Соңында, жергілікті кеңістікті синхрондау функциясы goods көзбен оны келесідей жүзеге асыруға болады:

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

Алдымен біз бұрын сақталған мәнді оқимыз row_ver кеңістік үшін goods. Егер ол жоқ болса (бірінші алмасу сеансы), онда біз оны ретінде қабылдаймыз row_ver нөл. Келесі циклде біз көрсетілген URL мекенжайындағы көзден өзгертілген деректерді беттік жүктеуді орындаймыз. Әрбір итерацияда біз алынған деректерді сәйкес жергілікті кеңістікке сақтаймыз және мәнді жаңартамыз row_ver (ғарышта row_ver және айнымалыда row_ver) - мәнін алыңыз row_ver жүктелген деректердің соңғы жолынан.

Кездейсоқ циклден қорғау үшін (бағдарламада қате болған жағдайда) цикл while арқылы ауыстыруға болады for:

for _ = 1, max_req do ...

Функцияны орындау нәтижесінде sync_goods ғарыш goods қабылдағыш барлық ғарыштық жазбалардың соңғы нұсқаларын қамтиды goods көзде.

Деректерді жоюды бұлай тарату мүмкін емес екені анық. Егер мұндай қажеттілік болса, жою белгісін пайдалануға болады. Кеңістікке қосыңыз goods логикалық өріс is_deleted және жазбаны физикалық түрде жоюдың орнына біз логикалық жоюды қолданамыз - өріс мәнін орнатамыз is_deleted мағынаға true. Кейде логикалық өрістің орнына is_deleted өрісті пайдалану ыңғайлырақ deleted, ол жазбаның логикалық жойылған күні-уақытын сақтайды. Логикалық жоюды орындағаннан кейін жою үшін белгіленген жазба көзден тағайындалған орынға (жоғарыда талқыланған логикаға сәйкес) тасымалданады.

Кезектілік row_ver басқа кеңістіктерден деректерді беру үшін пайдаланылуы мүмкін: әрбір жіберілетін кеңістік үшін жеке тізбекті құрудың қажеті жоқ.

Біз Tarantool ДҚБЖ көмегімен қолданбаларда деректерді жоғары деңгейлі репликациялаудың тиімді әдісін қарастырдық.

қорытындылар

  1. Tarantool ДҚБЖ – жоғары жүктемелі қосымшаларды жасауға арналған тартымды, перспективалы өнім.
  2. Жоғары деңгейлі деректердің репликациясының төменгі деңгейлі репликацияға қарағанда бірқатар артықшылықтары бар.
  3. Мақалада талқыланған жоғары деңгейлі репликация әдісі соңғы алмасу сеансынан бері өзгерген жазбаларды ғана тасымалдау арқылы тасымалданатын деректердің көлемін азайтуға мүмкіндік береді.

Ақпарат көзі: www.habr.com

пікір қалдыру