Héichniveau Replikatioun an Tarantool DBMS

Hallo, ech erstellen Uwendungen fir DBMS Tarantool ass eng Plattform entwéckelt vun der Mail.ru Group déi e High-Performance DBMS an en Applikatiounsserver an der Lua Sprooch kombinéiert. Déi héich Geschwindegkeet vu Léisungen baséiert op Tarantool gëtt erreecht, besonnesch duerch Ënnerstëtzung vum In-Memory Modus vun der DBMS an d'Fäegkeet fir Applikatiounsgeschäftslogik an engem eenzegen Adressraum mat Daten auszeféieren. Zur selwechter Zäit gëtt d'Datepersistenz mat ACID Transaktiounen geséchert (e WAL Log gëtt op der Disk gehal). Tarantool huet gebaut-an Ënnerstëtzung fir Replikatioun a Sharding. Vun der Versioun 2.1 un, ginn Ufroen an der SQL Sprooch ënnerstëtzt. Tarantool ass Open Source a lizenzéiert ënner der Simplified BSD Lizenz. Et gëtt och eng kommerziell Enterprise Versioun.

Héichniveau Replikatioun an Tarantool DBMS
Fillt d'Kraaft! (...aka genéisst d'Leeschtung)

All déi uewendriwwer mécht Tarantool eng attraktiv Plattform fir High-load Uwendungen ze kreéieren déi mat Datenbanken schaffen. An esou Uwendungen gëtt et dacks e Bedierfnes fir Datereplikatioun.

Wéi uewen erwähnt, huet Tarantool gebaut-an Datereplikatioun. De Prinzip vu senger Operatioun ass sequenziell op Repliken all Transaktiounen auszeféieren, déi am Master Log (WAL) enthale sinn. Normalerweis sou Replikatioun (mir nennen et weider niddereg-Niveau) gëtt benotzt fir Applikatioun Feeler Toleranz ze garantéieren an / oder d'Liesbelaaschtung tëscht Clusternoden ze verdeelen.

Héichniveau Replikatioun an Tarantool DBMS
Reis. 1. Replikatioun bannent engem Cluster

E Beispill vun engem alternativen Szenario wier d'Iwwerdroung vun Daten erstallt an enger Datebank an eng aner Datebank fir d'Veraarbechtung / Iwwerwaachung. Am leschte Fall kann eng méi praktesch Léisung sinn ze benotzen héijen Niveau Replikatioun - Datereplikatioun op der Applikatioun Business Logik Niveau. Déi. Mir benotzen keng fäerdeg Léisung, déi an der DBMS agebaut ass, awer implementéiere Replikatioun eleng an der Applikatioun déi mir entwéckelen. Dës Approche huet souwuel Virdeeler an Nodeeler. Loosst eis d'Virdeeler lëschten.

1. Verkéier spueren:

  • Dir kënnt net all d'Donnéeën Transfermaart, mä nëmmen en Deel vun et (zum Beispill, Dir kënnt nëmmen e puer Dëscher Transfert, e puer vun hire Sailen oder records datt e bestëmmte Critère treffen);
  • Am Géigesaz zu Low-Level Replikatioun, déi kontinuéierlech an asynchron (implementéiert an der aktueller Versioun vun Tarantool - 1.10) oder Synchron (fir an de spéider Versioune vum Tarantool ëmgesat ze ginn) Modus, kann héich-Niveau Replikatioun a Sessiounen ausgefouert ginn (dh. D'Applikatioun synchroniséiert d'éischt d'Donnéeën - eng Austauschsessiondaten, da gëtt et eng Paus an der Replikatioun, no där déi nächst Austauschsession geschitt, etc.);
  • wann e Rekord e puer Mol geännert huet, kënnt Dir nëmmen seng lescht Versioun transferéieren (am Géigesaz zu Low-Level Replikatioun, an där all Ännerungen, déi um Master gemaach goufen, sequenziell op de Repliken gespillt ginn).

2. Et gi keng Schwieregkeeten mat der Implementatioun vun HTTP-Austausch, wat Iech erlaabt Remote-Datenbanken ze synchroniséieren.

Héichniveau Replikatioun an Tarantool DBMS
Reis. 2. Replikatioun iwwer HTTP

3. D'Datebankstrukturen, tëscht deenen d'Donnéeën iwwerdroe ginn, mussen net d'selwecht sinn (ausserdeem ass et am allgemenge Fall souguer méiglech verschidde DBMSen, Programméierungssproochen, Plattformen, etc.) ze benotzen.

Héichniveau Replikatioun an Tarantool DBMS
Reis. 3. Replikatioun an heterogen Systemer

De Nodeel ass datt am Duerchschnëtt d'Programméiere méi schwéier / deier ass wéi d'Konfiguratioun, an amplaz vun der agebauter Funktionalitéit ze personaliséieren, musst Dir Är eege implementéieren.

Wann an Ärer Situatioun déi uewe genannte Virdeeler entscheedend sinn (oder eng noutwendeg Bedingung sinn), dann ass et Sënn fir High-Level Replikatioun ze benotzen. Loosst eis e puer Weeër kucken fir High-Level Datereplikatioun an der Tarantool DBMS ëmzesetzen.

Verkéier minimiséieren

Also, ee vun de Virdeeler vun héijer Replikatioun ass Verkéiersspueren. Fir dëse Virdeel voll ze realiséieren, ass et néideg, d'Quantitéit vun den Daten, déi während all Austauschsessioun transferéiert ginn, ze minimiséieren. Natierlech sollte mir net vergiessen datt um Enn vun der Sessioun den Dateempfänger mat der Quell synchroniséiert muss ginn (op d'mannst fir deen Deel vun den Donnéeën, deen an der Replikatioun involvéiert ass).

Wéi miniméiert d'Quantitéit vun Daten, déi während héijer Replikatioun transferéiert ginn? Eng einfach Léisung kéint sinn Daten no Datum an Zäit ze wielen. Fir dëst ze maachen, kënnt Dir d'Datum-Zäitfeld benotzen, déi schonn an der Tabell existéiert (wann et existéiert). Zum Beispill, en "Bestellung" Dokument kann e Feld "erfuerderlech Ausféierungszäit vun der Bestellung" hunn - delivery_time. De Problem mat dëser Léisung ass datt d'Wäerter an dësem Feld net an der Uerdnung musse sinn datt d'Bestellungen erstallt ginn. Also mir kënnen de maximale Feldwäert net erënneren delivery_time, iwwerdroen während der viregter Austausch Sessioun, a während der nächster Austausch Sessioun wielt all records mat engem méi héich Feld Wäert delivery_time. Opzeechnunge mat engem nidderegen Feldwäert kënnen tëscht Austauschsessiounen bäigefüügt ginn delivery_time. Och d'Uerdnung hätt Ännerunge kënnen erhalen, déi awer net den Terrain beaflosst delivery_time. A béide Fäll ginn d'Ännerungen net vun der Quell op d'Destinatioun transferéiert. Fir dës Problemer ze léisen, brauche mir Daten "iwwerlappend" ze transferéieren. Déi. an all Austausch Sessioun wäerte mir all Daten mam Feldwäert transferéieren delivery_time, Iwwerschreiden e puer Punkt an der Vergaangenheet (zum Beispill, N Stonnen vum aktuellen Moment). Wéi och ëmmer, et ass evident, datt fir grouss Systemer dës Approche héich redundant ass an d'Verkéiersspueren, déi mir striewen, op näischt reduzéieren. Zousätzlech kann den Dësch, deen transferéiert gëtt, keen Feld mat engem Datum-Zäit assoziéiert hunn.

Eng aner Léisung, méi komplex wat d'Ëmsetzung ugeet, ass d'Empfang vun Daten unzeerkennen. An dësem Fall, während all Austausch Sessioun, ginn all Daten iwwerdroen, de Empfang vun deem net vum Empfänger bestätegt gouf. Fir dëst ëmzesetzen, musst Dir eng boolesch Kolonn an d'Quelltabel addéieren (zum Beispill, is_transferred). Wann den Empfänger den Empfang vum Rekord unerkennt, hëlt dat entspriechend Feld de Wäert true, duerno ass d'Entrée net méi an den Austausch involvéiert. Dës Ëmsetzungsoptioun huet déi folgend Nodeeler. Als éischt, fir all iwwerdroen Rekord muss eng Unerkennung generéiert a geschéckt ginn. Grof geschwat, dëst kéint vergläichbar sinn mat der Verdueblung vun der Quantitéit vun den iwwerdroenen Donnéeën a féiert zur Verdueblung vun der Unzuel vun de Rondreesen. Zweetens gëtt et keng Méiglechkeet fir dee selwechte Rekord un e puer Empfänger ze schécken (den éischten Empfänger deen ze kréien wäert d'Empfang fir sech selwer a fir all déi aner bestätegen).

Eng Method déi net déi uewe genannte Nodeeler huet ass eng Kolonn un den transferéierten Dësch ze addéieren fir Ännerungen a senge Reihen ze verfolgen. Sou eng Kolonn kann vun Datum-Zäit-Typ sinn a muss vun der Applikatioun op déi aktuell Zäit gesat/aktualiséiert ginn all Kéier wann d'Records dobäigesat/ännert ginn (atomesch mat der Zousatz/Ännerung). Als Beispill ruffe mir d'Kolonn update_time. Andeems Dir de maximale Feldwäert vun dëser Kolonn fir déi transferéiert records späichert, kënne mir déi nächst Austauschsession mat dësem Wäert starten (wielt records mam Feldwäert aus update_time, iwwerschratt de virdru gespäicherten Wäert). De Problem mat der leschter Approche ass datt Datenännerunge kënnen a Chargen optrieden. Als Resultat vun de Feldwäerter an der Kolonn update_time vläicht net eenzegaarteg ginn. Also kann dës Kolonn net fir portionéiert (Säit-fir-Säit) Datenausgang benotzt ginn. Fir Daten Säit fir Säit ze weisen, musst Dir zousätzlech Mechanismen erfannen, déi héchstwahrscheinlech ganz niddereg Effizienz hunn (zum Beispill, all Rekorder mat dem Wäert aus der Datebank zréckzéien update_time méi héich wéi e bestëmmten a produzéiert eng gewëssen Unzuel vun Opzeechnungen, ugefaange vun engem bestëmmte Offset vum Ufank vun der Probe).

Dir kënnt d'Effizienz vum Datentransfer verbesseren andeems Dir déi viregt Approche liicht verbessert. Fir dëst ze maachen, benotze mir den ganzen Typ (laang ganz Zuel) als Spaltfeldwäerter fir Ännerungen ze verfolgen. Loosst eis d'Kolonn nennen row_ver. De Feldwäert vun dëser Kolonn muss nach ëmmer gesat/aktualiséiert ginn all Kéier wann e Rekord erstallt/ännert gëtt. Awer an dësem Fall gëtt d'Feld net den aktuellen Datum-Zäit zougewisen, awer de Wäert vun e puer Konter, erhéicht duerch een. Als Resultat, der Kolonn row_ver wäert eenzegaarteg Wäerter enthalen a kënnen net nëmme benotzt ginn fir "Delta" Donnéeën ze weisen (daten bäigefüügt / geännert zënter dem Enn vun der viregter Austauschsessioun), awer och fir se einfach an effektiv op Säiten opzedeelen.

Déi lescht proposéiert Method fir d'Quantitéit vun den Daten, déi am Kader vun der High-Level Replikatioun transferéiert ginn, ze minimiséieren schéngt mir déi optimal an universell. Loosst eis et méi am Detail kucken.

Passéiere vun Donnéeën mat engem Row Versioun Konter

Ëmsetzung vum Server / Master Deel

Am MS SQL Server gëtt et e spezielle Kolonnentyp fir dës Approche ëmzesetzen - rowversion. All Datebank huet e Comptoir, deen ëm eng eropgeet all Kéier wann e Rekord bäigefüügt / geännert gëtt an enger Tabell déi eng Kolonn huet wéi rowversion. De Wäert vun dësem Konter gëtt automatesch op d'Feld vun dëser Kolonn an der dobäi / geännert Rekord zougewisen. Den Tarantool DBMS huet keen ähnlechen agebaute Mechanismus. Wéi och ëmmer, am Tarantool ass et net schwéier et manuell ëmzesetzen. Loosst eis kucken wéi dat gemaach gëtt.

Éischten, e bëssen Terminologie: Dëscher am Tarantool sinn Plazen genannt, an records sinn Tuples genannt. Am Tarantool kënnt Dir Sequenzen erstellen. Sequenzen sinn näischt méi wéi benannt Generatore vun bestallten ganzer Wäerter. Déi. dat ass genee wat mir fir eis Zwecker brauchen. Drënner wäerte mir esou eng Sequenz erstellen.

Ier Dir eng Datebankoperatioun am Tarantool ausféiert, musst Dir de folgende Kommando ausféieren:

box.cfg{}

Als Resultat wäert Tarantool ufänken Datebank Snapshots an Transaktiounsprotokoller an den aktuellen Verzeechnes ze schreiwen.

Loosst eis eng Sequenz erstellen row_version:

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

Optioun if_not_exists erlaabt de Kreatiounsskript e puer Mol auszeféieren: wann den Objet existéiert, probéiert Tarantool et net erëm ze kreéieren. Dës Optioun gëtt an all spéider DDL Kommandoen benotzt.

Loosst eis e Raum als Beispill erstellen.

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

Hei setzen mir den Numm vum Raum (goods), Feldnimm an hir Aarte.

Auto-incrementing Felder am Tarantool ginn och erstallt mat Sequenzen. Loosst eis en auto-inkrementéierende primäre Schlëssel per Feld erstellen 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 ënnerstëtzt verschidden Aarte vun Indexen. Déi meescht benotzt Indexer sinn TREE an HASH Typen, déi op Strukturen baséieren, déi dem Numm entspriechen. TREE ass déi villsäitegst Indextyp. Et erlaabt Iech Daten op eng organiséiert Manéier ze recuperéieren. Awer fir d'Gläichheet Auswiel ass HASH méi gëeegent. Deementspriechend ass et ubruecht HASH fir de primäre Schlëssel ze benotzen (wat ass wat mir gemaach hunn).

Fir d'Kolonn ze benotzen row_ver fir geännert Donnéeën ze transferéieren, musst Dir Sequenzwäerter un d'Felder vun dëser Kolonn binden row_ver. Awer am Géigesaz zum primäre Schlëssel ass de Kolonnfeldwäert row_ver soll vun engem Erhéijung net nëmmen wann nei records dobäi, mä och wann bestehend déi änneren. Dir kënnt Ausléiser fir dëst benotzen. Tarantool huet zwou Aarte vu Raumausléiser: before_replace и on_replace. Ausléiser ginn ausgeléist wann d'Donnéeën am Raum änneren (fir all Tupel, déi vun den Ännerungen betraff ass, gëtt eng Ausléiserfunktioun gestart). Géigesaz on_replace, before_replace-Triggers erlaben Iech d'Donnéeën vum Tupel z'änneren, fir deen den Ausléiser ausgefouert gëtt. Deementspriechend passt eis déi lescht Zort Ausléiser.

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

Déi folgend Ausléiser ersetzt de Feldwäert row_ver gespäichert tuple op den nächste Wäert vun der Sequenz row_version.

Fir kënnen Daten aus dem Weltraum extrahéieren goods duerch Kolonn row_ver, loosst eis en Index erstellen:

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

Index Typ - Bam (TREE), well mir mussen d'Donnéeën an opsteigend Uerdnung vun de Wäerter an der Kolonn extrahéieren row_ver.

Loosst eis e puer Daten an de Raum addéieren:

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}

Well Dat éischt Feld ass en Auto-inkrementéierende Konter; Mir passéieren amplaz Null. Tarantool ersetzt automatesch den nächste Wäert. Ähnlech wéi de Wäert vun de Kolonnfelder row_ver Dir kënnt null Passe - oder guer net de Wäert uginn, well dës Kolonn besetzt déi lescht Positioun am Raum.

Loosst eis d'Insertiounsresultat iwwerpréiwen:

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

Wéi Dir gesitt, ginn déi éischt a lescht Felder automatesch ausgefëllt. Elo wäert et einfach sinn eng Funktioun fir Säit-fir-Säit Eroplueden vu Raumännerungen ze schreiwen 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

D'Funktioun hëlt als Parameter de Wäert row_ver, ugefaange vun deem et néideg ass Ännerungen ze entlaaschten, a gëtt en Deel vun de geännerten Donnéeën zréck.

Dateprobe am Tarantool gëtt duerch Indizes gemaach. Funktioun get_goods benotzt en Iterator vum Index row_ver geännert Donnéeën ze kréien. Iterator Typ ass GT (Grouss wéi, méi wéi). Dëst bedeit datt den Iterator sequenziell d'Indexwäerter duerch de passéierte Schlëssel (Feldwäert) duerchkreest row_ver).

Den Iterator gëtt Tuples zréck. Fir dono fäeg sinn Daten iwwer HTTP ze transferéieren, ass et néideg d'Tupelen an eng Struktur ze konvertéieren déi bequem ass fir déi spéider Serialiséierung. D'Beispill benotzt d'Standardfunktioun fir dëst tomap. Amplaz ze benotzen tomap Dir kënnt Är eege Funktioun schreiwen. Zum Beispill kënne mir e Feld ëmbenennen name, net laanscht den Terrain code an dobäi e Feld 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

D'Säitgréisst vun den Ausgangsdaten (d'Zuel vun den Opzeechnungen an engem Portioun) gëtt vun der Variabel bestëmmt page_size. Am Beispill de Wäert page_size ass 5. An engem richtege Programm ass d'Säitgréisst normalerweis méi wichteg. Et hänkt vun der duerchschnëttlecher Gréisst vum Raumtupel of. Déi optimal Säitgréisst kann empiresch festgeluecht ginn andeems d'Datentransferzäit gemooss gëtt. Wat méi grouss d'Säitgréisst ass, dest méi kleng ass d'Zuel vun de Roundtrips tëscht de Sendungs- an Empfangssäiten. Op dës Manéier kënnt Dir d'Gesamtzäit fir d'Ännerungen eroflueden reduzéieren. Wéi och ëmmer, wann d'Säitgréisst ze grouss ass, verbrénge mir ze laang um Server fir d'Probe ze serialiséieren. Als Resultat kann et Verspéidungen bei der Veraarbechtung vun anere Ufroe ginn, déi op de Server kommen. Parameter page_size kann aus der Konfiguratiounsdatei gelueden ginn. Fir all iwwerdroe Raum kënnt Dir säin eegene Wäert setzen. Wéi och ëmmer, fir déi meescht Plazen kann de Standardwäert (zum Beispill 100) passend sinn.

Loosst eis d'Funktioun ausféieren 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
...

Loosst eis de Feldwäert huelen row_ver vun der leschter Zeil a rufft d'Funktioun nach eng Kéier un:

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

Nach eemol:

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

Wéi Dir kënnt gesinn, wann Dir dës Manéier benotzt, gëtt d'Funktioun all Raumrecords Säit fir Säit zréck goods. Déi lescht Säit gëtt gefollegt vun enger eidel Auswiel.

Loosst eis Ännerungen am Raum maachen:

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

Mir hunn de Feldwäert geännert name fir eng Entrée an dobäi zwee nei Entréen.

Loosst eis de leschte Funktiounsruff widderhuelen:

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

D'Funktioun huet déi geännert a bäigefüügt records zréckginn. Also d'Funktioun get_goods erlaabt Iech Donnéeën ze kréien, déi zënter hirem leschten Uruff geännert hunn, wat d'Basis vun der Replikatiounsmethod ass, déi ënnersicht gëtt.

Mir loossen d'Emissioun vun de Resultater iwwer HTTP a Form vu JSON ausserhalb vum Ëmfang vun dësem Artikel. Dir kënnt iwwer dëst hei liesen: https://habr.com/ru/company/mailru/blog/272141/

Ëmsetzung vum Client / Sklaven Deel

Loosst eis kucken wéi d'Ëmsetzung vun der Empfangssäit ausgesäit. Loosst eis e Raum op der Empfangssäit erstellen fir déi erofgeluede Donnéeën ze späicheren:

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

D'Struktur vum Raum gläicht d'Struktur vum Raum an der Quell. Awer well mir déi kritt Donnéeën net soss anzwousch weiderginn, ass d'Kolonn row_ver ass net am Empfängerraum. Am Feld id Quellidentifizéierer ginn opgeholl. Dofir, op der Empfänger Säit ass et net néideg et automatesch ze erhéijen.

Zousätzlech brauche mir e Raum fir Wäerter ze spueren 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
})

Fir all gelueden Plaz (Feld space_name) mir späichere dee leschte geluedene Wäert hei row_ver (Feld value). D'Kolonn handelt als primäre Schlëssel space_name.

Loosst eis eng Funktioun erstellen fir Raumdaten ze lueden goods iwwer HTTP. Fir dëst ze maachen, brauche mir eng Bibliothéik déi en HTTP Client implementéiert. Déi folgend Linn lued d'Bibliothéik an instantiéiert den HTTP Client:

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

Mir brauchen och eng Bibliothéik fir json Deserialiséierung:

local json = require('json')

Dëst ass genuch fir eng Datenlaaschtfunktioun ze kreéieren:

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

D'Funktioun féiert eng HTTP-Ufro un d'URL Adress aus a schéckt se row_ver als Parameter a gëtt d'deserialiséiert Resultat vun der Ufro zréck.

D'Funktioun fir kritt Daten ze späicheren gesäit esou aus:

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

Zyklus vun späicheren Daten zu Weltraum goods an enger Transaktioun gesat (d'Funktioun gëtt dofir benotzt box.atomic) fir d'Zuel vun den Diskoperatiounen ze reduzéieren.

Endlech, lokal Raum Synchroniséierung Funktioun goods mat enger Quell kënnt Dir et esou ëmsetzen:

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

Als éischt liesen mir de virdru gespäicherten Wäert row_ver fir Plaz goods. Wann et fehlt (déi éischt Austauschsessioun), dann huelen mir et als row_ver null. Als nächst am Zyklus maache mir e Säit-fir-Säit-Download vun de geännerten Daten aus der Quell op der spezifizéierter URL. Bei all Iteratioun späichere mir déi kritt Donnéeën op de passenden lokale Raum an aktualiséieren de Wäert row_ver (am Raum row_ver an an der Variabel row_ver) - de Wäert huelen row_ver vun der leschter Linn vun gelueden Donnéeën.

Fir géint zoufälleg Looping ze schützen (am Fall vun engem Feeler am Programm), gëtt d'Loop while kann duerch ersat ginn for:

for _ = 1, max_req do ...

Als Resultat vun der Ausféierung vun der Funktioun sync_goods Plaz goods den Empfänger enthält déi lescht Versioune vun all Raumrecords goods an der Quell.

Natierlech kann d'Dateläschung net op dës Manéier iwwerdroe ginn. Wann esou e Besoin existéiert, kënnt Dir e Läschmark benotzen. Füügt op Plaz goods boolesche Feld is_deleted an amplaz vun engem Rekord kierperlech läschen, mir benotzen logesch Läschen - mir setzen den Terrain Wäert is_deleted an Bedeitung true. Heiansdo amplaz vun engem boolesche Feld is_deleted et ass méi bequem d'Feld ze benotzen deleted, déi den Datum-Zäit vun der logescher Läschung vum Rekord späichert. Nodeems Dir eng logesch Läsch gemaach hutt, gëtt de Rekord fir d'Läsche markéiert vun der Quell op d'Destinatioun transferéiert (no der Logik hei uewen diskutéiert).

Sequenz row_ver ka benotzt ginn fir Daten aus anere Raum ze vermëttelen: et ass net néideg eng separat Sequenz fir all iwwerdroe Raum ze kreéieren.

Mir hunn eng effektiv Manéier fir High-Level Datereplikatioun an Uwendungen gekuckt mat der Tarantool DBMS.

Conclusiounen

  1. Tarantool DBMS ass en attraktivt, verspriechend Produkt fir High-load Uwendungen ze kreéieren.
  2. Héichniveau Datereplikatioun huet eng Rei Virdeeler iwwer Low-Level Replikatioun.
  3. D'High-Level Replikatiounsmethod, déi am Artikel diskutéiert gëtt, erlaabt Iech d'Quantitéit vun iwwerdroenen Donnéeën ze minimiséieren andeems Dir nëmmen déi Rekorder transferéiert, déi zënter der leschter Austauschsitzung geännert hunn.

Source: will.com

Setzt e Commentaire