Tarantool DBMS-də yüksək səviyyəli replikasiya

Salam, mən DBMS üçün proqramlar yaradıram Tarantool yüksək performanslı DBMS və Lua dilində proqram serverini birləşdirən Mail.ru Group tərəfindən hazırlanmış platformadır. Tarantool-a əsaslanan həllərin yüksək sürəti, xüsusən də DBMS-nin yaddaşdaxili rejiminə dəstək və verilənlərlə vahid ünvan məkanında tətbiq biznes məntiqini yerinə yetirmək imkanı sayəsində əldə edilir. Eyni zamanda, ACID əməliyyatlarından istifadə edərək məlumatların davamlılığı təmin edilir (diskdə WAL jurnalı saxlanılır). Tarantool, replikasiya və parçalanma üçün daxili dəstəyə malikdir. 2.1 versiyasından başlayaraq SQL dilində sorğular dəstəklənir. Tarantool açıq mənbədir və Sadələşdirilmiş BSD lisenziyası altında lisenziyalıdır. Kommersiya Enterprise versiyası da var.

Tarantool DBMS-də yüksək səviyyəli replikasiya
Gücü hiss edin! (...aka performansdan həzz alın)

Yuxarıda göstərilənlərin hamısı Tarantool-u verilənlər bazası ilə işləyən yüksək yüklü proqramlar yaratmaq üçün cəlbedici platforma edir. Belə proqramlarda çox vaxt verilənlərin təkrarlanmasına ehtiyac yaranır.

Yuxarıda qeyd edildiyi kimi, Tarantool daxili məlumat replikasiyasına malikdir. Onun işləmə prinsipi əsas jurnalda (WAL) olan bütün əməliyyatları ardıcıl olaraq replikalarda yerinə yetirməkdir. Adətən belə təkrarlama (bunu daha sonra adlandıracağıq aşağı səviyyə) proqram xətalarına dözümlülüyünü təmin etmək və/yaxud klaster qovşaqları arasında oxu yükünü bölüşdürmək üçün istifadə olunur.

Tarantool DBMS-də yüksək səviyyəli replikasiya
düyü. 1. Çoxluq daxilində təkrarlama

Alternativ ssenariyə misal olaraq bir verilənlər bazasında yaradılmış məlumatların emal/monitorinq üçün digər verilənlər bazasına köçürülməsi ola bilər. Sonuncu halda, istifadə etmək daha rahat bir həll ola bilər yüksək səviyyə replikasiya - tətbiqin biznes məntiqi səviyyəsində məlumatların təkrarlanması. Bunlar. Biz DBMS-də quraşdırılmış hazır həllərdən istifadə etmirik, lakin inkişaf etdirdiyimiz proqram daxilində replikasiyanı özümüz həyata keçiririk. Bu yanaşmanın həm üstünlükləri, həm də mənfi cəhətləri var. Üstünlükləri sadalayaq.

1. Trafikə qənaət:

  • Siz bütün məlumatları ötürə bilməzsiniz, ancaq onun yalnız bir hissəsini (məsələn, yalnız bəzi cədvəlləri, onların bəzi sütunlarını və ya müəyyən bir meyara cavab verən qeydləri köçürə bilərsiniz);
  • Davamlı olaraq asinxron (Tarantool-un cari versiyasında tətbiq olunur - 1.10) və ya sinxron (Tarantool-un sonrakı versiyalarında həyata keçiriləcək) rejimdə həyata keçirilən aşağı səviyyəli replikasiyadan fərqli olaraq, yüksək səviyyəli replikasiya sessiyalarda (yəni, proqram əvvəlcə məlumatları sinxronlaşdırır - mübadilə sessiyası məlumatları, sonra təkrarlamada fasilə yaranır, bundan sonra növbəti mübadilə sessiyası baş verir və s.);
  • qeyd bir neçə dəfə dəyişibsə, siz onun yalnız ən son versiyasını köçürə bilərsiniz (aşağı səviyyəli replikasiyadan fərqli olaraq, master-da edilən bütün dəyişikliklər ardıcıl olaraq replikalarda səsləndiriləcək).

2. Uzaq verilənlər bazalarını sinxronlaşdırmağa imkan verən HTTP mübadiləsinin həyata keçirilməsində heç bir çətinlik yoxdur.

Tarantool DBMS-də yüksək səviyyəli replikasiya
düyü. 2. HTTP üzərindən təkrarlama

3. Məlumatların ötürüldüyü verilənlər bazası strukturlarının eyni olması şərt deyil (bundan əlavə, ümumi halda hətta müxtəlif DBMS-lərdən, proqramlaşdırma dillərindən, platformalardan və s. istifadə etmək mümkündür).

Tarantool DBMS-də yüksək səviyyəli replikasiya
düyü. 3. Heterogen sistemlərdə replikasiya

İşin mənfi tərəfi odur ki, orta hesabla proqramlaşdırma konfiqurasiyadan daha çətin/bahalıdır və daxili funksionallığı fərdiləşdirmək əvəzinə, siz öz funksiyanızı həyata keçirməli olacaqsınız.

Əgər sizin vəziyyətinizdə yuxarıda göstərilən üstünlüklər həlledicidirsə (və ya zəruri şərtdirsə), onda yüksək səviyyəli replikasiyadan istifadə etmək məntiqlidir. Tarantool DBMS-də yüksək səviyyəli məlumatların təkrarlanmasının həyata keçirilməsinin bir neçə yoluna baxaq.

Trafikin minimuma endirilməsi

Beləliklə, yüksək səviyyəli təkrarlamanın üstünlüklərindən biri trafikə qənaətdir. Bu üstünlüyün tam şəkildə həyata keçirilməsi üçün hər bir mübadilə sessiyası zamanı ötürülən məlumatların həcmini minimuma endirmək lazımdır. Əlbəttə ki, unutmamalıyıq ki, sessiyanın sonunda məlumat qəbuledicisi mənbə ilə sinxronlaşdırılmalıdır (ən azı məlumatların replikasiyada iştirak edən hissəsi üçün).

Yüksək səviyyəli replikasiya zamanı ötürülən məlumatların miqdarını necə minimuma endirmək olar? Sadə bir həll məlumatı tarix və vaxta görə seçmək ola bilər. Bunu etmək üçün cədvəldə artıq mövcud olan tarix-vaxt sahəsindən istifadə edə bilərsiniz (əgər varsa). Məsələn, "sifariş" sənədində "tələb olunan sifarişin icra müddəti" sahəsi ola bilər - delivery_time. Bu həll ilə bağlı problem, bu sahədəki dəyərlərin sifarişlərin yaradılmasına uyğun gələn ardıcıllıqla olmamasıdır. Beləliklə, maksimum sahə dəyərini xatırlaya bilmirik delivery_time, əvvəlki mübadilə sessiyası zamanı ötürülür və növbəti mübadilə sessiyası zamanı daha yüksək sahə dəyəri olan bütün qeydləri seçin delivery_time. Mübadilə sessiyaları arasında daha aşağı sahə dəyəri olan qeydlər əlavə edilmiş ola bilər delivery_time. Həmçinin, sifariş dəyişikliklərə məruz qala bilərdi, buna baxmayaraq sahəyə təsir etmədi delivery_time. Hər iki halda dəyişikliklər mənbədən təyinat yerinə köçürülməyəcək. Bu problemləri həll etmək üçün məlumatları "üst-üstə düşən" ötürməli olacağıq. Bunlar. hər mübadilə sessiyasında biz bütün məlumatları sahə dəyəri ilə köçürəcəyik delivery_time, keçmişdə bir nöqtəni aşmaq (məsələn, cari andan N saat). Bununla belə, aydındır ki, böyük sistemlər üçün bu yanaşma çox lazımsızdır və səy göstərdiyimiz trafikə qənaəti heçə endirə bilər. Bundan əlavə, köçürülən cədvəldə tarix-vaxt ilə əlaqəli sahə olmaya bilər.

Tətbiq baxımından daha mürəkkəb olan başqa bir həll məlumatların qəbulunu təsdiqləməkdir. Bu halda, hər bir mübadilə sessiyası zamanı, qəbulu alıcı tərəfindən təsdiqlənməmiş bütün məlumatlar ötürülür. Bunu həyata keçirmək üçün mənbə cədvəlinə Boolean sütunu əlavə etməlisiniz (məsələn, is_transferred). Qəbul edən qeydin alınmasını təsdiq edərsə, müvafiq sahə dəyəri götürür true, bundan sonra giriş artıq mübadilələrdə iştirak etmir. Bu tətbiq variantının aşağıdakı çatışmazlıqları var. Birincisi, ötürülən hər bir qeyd üçün bir təsdiq yaradılmalı və göndərilməlidir. Təxminən desək, bu, ötürülən məlumatların miqdarının ikiqat artması ilə müqayisə oluna bilər və gediş-gəliş sayının ikiqat artmasına səbəb ola bilər. İkincisi, eyni qeydi bir neçə qəbulediciyə göndərmək imkanı yoxdur (qəbul edən ilk qəbul edən şəxs özü və bütün digərləri üçün qəbzi təsdiq edəcək).

Yuxarıda göstərilən çatışmazlıqları olmayan bir üsul, sətirlərindəki dəyişiklikləri izləmək üçün ötürülən cədvələ bir sütun əlavə etməkdir. Belə sütun tarix-vaxt tipli ola bilər və hər dəfə qeydlər əlavə edildikdə/dəyişdirildikdə (əlavə/dəyişiklik ilə atomik olaraq) tətbiq tərəfindən cari vaxta təyin edilməli/yenilənməlidir. Nümunə olaraq sütunu çağıraq update_time. Köçürülmüş qeydlər üçün bu sütunun maksimum sahə dəyərini saxlamaqla biz bu dəyərlə növbəti mübadilə sessiyasına başlaya bilərik (sahə dəyəri olan qeydləri seçin) update_time, əvvəllər saxlanmış dəyəri aşır). Sonuncu yanaşma ilə bağlı problem məlumat dəyişikliklərinin qrup halında baş verə bilməsidir. Sütundakı sahə dəyərləri nəticəsində update_time unikal olmaya bilər. Beləliklə, bu sütun hissələrə bölünmüş (səhifə-səhifə) məlumat çıxışı üçün istifadə edilə bilməz. Məlumatları səhifə-səhifə göstərmək üçün, çox güman ki, çox aşağı effektivliyə malik olan əlavə mexanizmlər icad etməli olacaqsınız (məsələn, verilənlər bazasından dəyəri olan bütün qeydləri əldə etmək) update_time verilmişdən yüksəkdir və nümunənin əvvəlindən müəyyən bir ofsetdən başlayaraq müəyyən sayda qeydlər istehsal edir).

Əvvəlki yanaşmanı bir qədər təkmilləşdirməklə məlumat ötürülməsinin səmərəliliyini artıra bilərsiniz. Bunu etmək üçün dəyişiklikləri izləmək üçün sütun sahəsinin dəyərləri kimi tam tipdən (uzun tam ədəd) istifadə edəcəyik. Sütunun adını verək row_ver. Bu sütunun sahə dəyəri hər dəfə qeyd yaradıldıqda/dəyişdirildikdə hələ də təyin edilməli/yenilənməlidir. Ancaq bu halda, sahəyə cari tarix-vaxt təyin edilməyəcək, lakin bəzi sayğacın dəyəri bir artdı. Nəticədə sütun row_ver unikal dəyərləri ehtiva edəcək və yalnız "delta" məlumatlarını (əvvəlki mübadilə sessiyasının sonundan sonra əlavə edilmiş/dəyişdirilmiş məlumatlar) göstərmək üçün deyil, həm də onu səhifələrə sadə və effektiv şəkildə bölmək üçün istifadə edilə bilər.

Yüksək səviyyəli replikasiya çərçivəsində ötürülən məlumatların miqdarını minimuma endirmək üçün təklif olunan sonuncu üsul mənə ən optimal və universal görünür. Gəlin buna daha ətraflı baxaq.

Satır Versiya Sayğacından istifadə edərək məlumatların ötürülməsi

Server/master hissəsinin həyata keçirilməsi

MS SQL Serverdə bu yanaşmanı həyata keçirmək üçün xüsusi bir sütun növü var - rowversion. Hər bir verilənlər bazasında sütunu olan cədvələ hər dəfə qeyd əlavə edildikdə/dəyişdirildikdə bir dəfə artırılan sayğac var rowversion. Bu sayğacın dəyəri avtomatik olaraq əlavə edilmiş/dəyişdirilmiş qeyddə bu sütunun sahəsinə təyin edilir. Tarantool DBMS-də oxşar daxili mexanizm yoxdur. Bununla belə, Tarantool-da onu əl ilə həyata keçirmək çətin deyil. Bunun necə edildiyinə baxaq.

Birincisi, bir az terminologiya: Tarantool-da cədvəllər boşluqlar adlanır və qeydlər tuple adlanır. Tarantool-da siz ardıcıllıq yarada bilərsiniz. Ardıcıllıqlar sifarişli tam ədədlərin adlandırılmış generatorlarından başqa bir şey deyil. Bunlar. bu, bizim məqsədlərimiz üçün lazım olan şeydir. Aşağıda belə bir ardıcıllıq yaradacağıq.

Tarantool-da verilənlər bazası əməliyyatını yerinə yetirməzdən əvvəl aşağıdakı əmri yerinə yetirməlisiniz:

box.cfg{}

Nəticədə, Tarantool cari qovluğa verilənlər bazası anlıq görüntülərini və əməliyyat qeydlərini yazmağa başlayacaq.

Gəlin ardıcıllıq yaradaq row_version:

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

Seçim if_not_exists yaratma skriptinin bir neçə dəfə icrasına imkan verir: əgər obyekt mövcuddursa, Tarantool onu yenidən yaratmağa cəhd etməyəcək. Bu seçim bütün sonrakı DDL əmrlərində istifadə olunacaq.

Nümunə olaraq bir boşluq yaradaq.

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

Burada məkanın adını təyin etdik (goods), sahə adları və onların növləri.

Tarantool-da avtomatik artan sahələr də ardıcıllıqlardan istifadə etməklə yaradılır. Gəlin sahə üzrə avtomatik artan əsas açar yaradaq 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 bir neçə növ indeksi dəstəkləyir. Ən çox istifadə olunan indekslər ada uyğun strukturlara əsaslanan TREE və HASH növləridir. TREE ən çox yönlü indeks növüdür. Bu, mütəşəkkil şəkildə məlumatları əldə etməyə imkan verir. Ancaq bərabərlik seçimi üçün HASH daha uyğundur. Müvafiq olaraq, əsas açar üçün HASH istifadə etmək məsləhətdir (bizim etdiyimiz budur).

Sütun istifadə etmək üçün row_ver dəyişdirilmiş məlumatları ötürmək üçün ardıcıllıq dəyərlərini bu sütunun sahələrinə bağlamaq lazımdır row_ver. Lakin əsas açardan fərqli olaraq, sütun sahəsinin dəyəri row_ver yalnız yeni qeydlər əlavə edərkən deyil, həm də mövcud olanları dəyişdirərkən bir artmalıdır. Bunun üçün triggerlərdən istifadə edə bilərsiniz. Tarantool iki növ kosmik tetikleyiciye malikdir: before_replace и on_replace. Tətiklər boşluqdakı məlumatlar dəyişdikdə işə salınır (dəyişikliklərdən təsirlənən hər bir dəst üçün tetik funksiyası işə salınır). Fərqli on_replace, before_replace-triggerlər, triggerin icra olunduğu dəst məlumatlarını dəyişdirməyə imkan verir. Müvafiq olaraq, son növ tetikler bizə uyğun gəlir.

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

Aşağıdakı trigger sahə dəyərini əvəz edir row_ver ardıcıllığın növbəti dəyərinə saxlanılan dəst row_version.

Kosmosdan məlumat çıxara bilmək üçün goods sütun üzrə row_ver, indeks yaradaq:

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

İndeks növü - ağac (TREE), çünki sütundakı dəyərlərin artan sırası ilə məlumatları çıxarmalıyıq row_ver.

Gəlin boşluğa bəzi məlumatlar əlavə edək:

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}

Çünki Birinci sahə avtomatik artan sayğacdır; bunun əvəzinə biz sıfır keçirik. Tarantool avtomatik olaraq növbəti dəyəri əvəz edəcək. Eynilə, sütun sahələrinin dəyəri kimi row_ver nil keçə bilərsiniz - və ya heç bir dəyəri qeyd etməməlisiniz, çünki bu sütun boşluqda sonuncu yeri tutur.

Daxiletmə nəticəsini yoxlayaq:

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

Gördüyünüz kimi birinci və sonuncu sahələr avtomatik doldurulur. İndi boşluq dəyişikliklərinin səhifə-səhifə yüklənməsi funksiyasını yazmaq asan olacaq 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

Funksiya parametr kimi dəyəri qəbul edir row_ver, ondan başlayaraq dəyişiklikləri boşaltmaq lazımdır və dəyişdirilmiş məlumatların bir hissəsini qaytarır.

Tarantool-da məlumatların seçilməsi indekslər vasitəsilə həyata keçirilir. Funksiya get_goods indeks üzrə iteratordan istifadə edir row_ver dəyişdirilmiş məlumatları almaq üçün. İterator növü GT-dir (Böyük, daha böyük). Bu o deməkdir ki, iterator ötürülən açardan başlayaraq indeks dəyərlərini ardıcıl olaraq keçəcək (sahə dəyəri row_ver).

İterator kortejləri qaytarır. Sonradan HTTP vasitəsilə məlumat ötürə bilmək üçün çubuqları sonrakı serializasiya üçün əlverişli bir quruluşa çevirmək lazımdır. Nümunə bunun üçün standart funksiyadan istifadə edir tomap. İstifadə etmək əvəzinə tomap öz funksiyanızı yaza bilərsiniz. Məsələn, sahənin adını dəyişmək istəyə bilərik name, sahəni keçməyin code və sahə əlavə edin 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

Çıxış məlumatlarının səhifə ölçüsü (bir hissədəki qeydlərin sayı) dəyişən tərəfindən müəyyən edilir page_size. Misalda dəyər page_size 5-dir. Real proqramda adətən səhifə ölçüsü daha çox əhəmiyyət kəsb edir. Bu, boşluq çubuğunun orta ölçüsündən asılıdır. Optimal səhifə ölçüsü məlumatların ötürülmə müddətini ölçməklə empirik olaraq müəyyən edilə bilər. Səhifə ölçüsü nə qədər böyükdürsə, göndərən və qəbul edən tərəflər arasında gediş-gəlişlərin sayı bir o qədər az olar. Bu yolla siz dəyişiklikləri yükləmək üçün ümumi vaxtı azalda bilərsiniz. Bununla belə, səhifə ölçüsü çox böyükdürsə, nümunəni seriallaşdırmaq üçün serverdə çox vaxt sərf edəcəyik. Nəticədə, serverə gələn digər sorğuların emalında gecikmələr ola bilər. Parametr page_size konfiqurasiya faylından yüklənə bilər. Hər bir ötürülən məkan üçün öz dəyərini təyin edə bilərsiniz. Bununla belə, əksər boşluqlar üçün standart dəyər (məsələn, 100) uyğun ola bilər.

Funksiyanı icra edək 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
...

Sahənin dəyərini götürək row_ver sonuncu sətirdən funksiyanı yenidən çağırın:

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

Bir daha:

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

Gördüyünüz kimi, bu şəkildə istifadə edildikdə, funksiya bütün boşluq qeydlərini səhifə-səhifə qaytarır goods. Son səhifədən sonra boş seçim gəlir.

Məkanda dəyişikliklər edək:

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

Sahənin dəyərini dəyişdirdik name bir giriş üçün və iki yeni giriş əlavə etdi.

Son funksiya çağırışını təkrarlayaq:

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

Funksiya dəyişdirilmiş və əlavə edilmiş qeydləri qaytardı. Beləliklə, funksiya get_goods nəzərdən keçirilən replikasiya metodunun əsasını təşkil edən son zəngdən sonra dəyişmiş məlumatları qəbul etməyə imkan verir.

HTTP vasitəsilə nəticələrin JSON formasında verilməsini bu məqalənin əhatə dairəsindən kənarda qoyacağıq. Bu barədə burada oxuya bilərsiniz: https://habr.com/ru/company/mailru/blog/272141/

Müştəri/qul hissəsinin həyata keçirilməsi

Qəbul edən tərəfin həyata keçirməsinin necə göründüyünə baxaq. Yüklənmiş məlumatları saxlamaq üçün qəbul edən tərəfdə boşluq yaradaq:

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

Məkanın quruluşu mənbədəki məkanın quruluşuna bənzəyir. Ancaq alınan məlumatları başqa yerə ötürməyəcəyimiz üçün sütun row_ver alıcının məkanında deyil. Sahədə id mənbə identifikatorları qeydə alınacaq. Buna görə də, qəbuledici tərəfdə onu avtomatik artırmağa ehtiyac yoxdur.

Bundan əlavə, dəyərləri saxlamaq üçün bir yerə ehtiyacımız var 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
})

Hər yüklənmiş boşluq üçün (sahə space_name) son yüklənmiş dəyəri burada saxlayacağıq row_ver (sahə value). Sütun əsas açar kimi çıxış edir space_name.

Kosmik məlumatların yüklənməsi funksiyasını yaradaq goods HTTP vasitəsilə. Bunun üçün bizə HTTP müştərisini həyata keçirən kitabxana lazımdır. Aşağıdakı sətir kitabxananı yükləyir və HTTP müştərisini yaradır:

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

Bizə həmçinin json seriyasından çıxarmaq üçün kitabxana lazımdır:

local json = require('json')

Məlumat yükləmə funksiyası yaratmaq üçün bu kifayətdir:

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

Funksiya url ünvanına HTTP sorğusunu yerinə yetirir və onu göndərir row_ver parametr kimi və sorğunun sıradan çıxarılmış nəticəsini qaytarır.

Qəbul edilmiş məlumatların saxlanması funksiyası belə görünür:

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

Məkanda məlumatların saxlanması dövrü goods əməliyyata yerləşdirilir (bunun üçün funksiya istifadə olunur box.atomic) disk əməliyyatlarının sayını azaltmaq üçün.

Nəhayət, yerli məkan sinxronizasiya funksiyası goods mənbə ilə bunu belə həyata keçirə bilərsiniz:

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

Əvvəlcə əvvəllər saxlanmış dəyəri oxuyuruq row_ver yer üçün goods. Əgər əskikdirsə (ilk mübadilə seansı), onda biz onu qəbul edirik row_ver sıfır. Sonrakı dövrədə göstərilən url-də mənbədən dəyişdirilmiş məlumatların səhifə-səhifə endirilməsini həyata keçiririk. Hər iterasiyada biz alınan məlumatları müvafiq yerli məkanda saxlayırıq və dəyəri yeniləyirik row_ver (kosmosda row_ver və dəyişəndə row_ver) - dəyəri götürün row_ver yüklənmiş məlumatların son sətirindən.

Təsadüfi döngədən qorunmaq üçün (proqramda səhv olduqda), döngə while ilə əvəz edilə bilər for:

for _ = 1, max_req do ...

Funksiyanın icrası nəticəsində sync_goods boşluq goods qəbuledici bütün kosmik qeydlərin ən son versiyalarını ehtiva edəcəkdir goods mənbədə.

Aydındır ki, məlumatların silinməsi bu şəkildə yayımlana bilməz. Belə bir ehtiyac varsa, silmə işarəsindən istifadə edə bilərsiniz. Kosmosa əlavə edin goods boolean sahəsi is_deleted və bir qeydi fiziki olaraq silmək əvəzinə, məntiqi silmədən istifadə edirik - sahə dəyərini təyin edirik is_deleted mənaya keçir true. Bəzən boolean sahəsi əvəzinə is_deleted sahədən istifadə etmək daha rahatdır deleted, qeydin məntiqi silinməsinin tarix-vaxtını saxlayır. Məntiqi silmə həyata keçirildikdən sonra silinmək üçün qeyd olunan qeyd mənbədən təyinat yerinə köçürüləcək (yuxarıda müzakirə olunan məntiqə uyğun olaraq).

Sıra row_ver başqa fəzalardan məlumat ötürmək üçün istifadə edilə bilər: hər bir ötürülən məkan üçün ayrıca ardıcıllıq yaratmağa ehtiyac yoxdur.

Tarantool DBMS-dən istifadə edən tətbiqlərdə yüksək səviyyəli məlumatların təkrarlanmasının effektiv yoluna baxdıq.

Tapıntılar

  1. Tarantool DBMS yüksək yüklü proqramlar yaratmaq üçün cəlbedici, perspektivli məhsuldur.
  2. Yüksək səviyyəli məlumatların təkrarlanması aşağı səviyyəli replikasiya ilə müqayisədə bir sıra üstünlüklərə malikdir.
  3. Məqalədə müzakirə edilən yüksək səviyyəli təkrarlama üsulu, yalnız son mübadilə sessiyasından sonra dəyişmiş qeydləri köçürməklə, ötürülən məlumatların miqdarını minimuma endirməyə imkan verir.

Mənbə: www.habr.com

DDoS mühafizəsi, VPS VDS serverləri olan saytlar üçün etibarlı hostinq alın 🔥 DDoS qorunması, VPS VDS serverləri ilə etibarlı veb sayt hostinqi alın | ProHoster