Բարև Ձեզ, ես ստեղծում եմ հավելվածներ DBMS-ի համար Mail.ru Group-ի կողմից մշակված հարթակ է, որը միավորում է բարձր արդյունավետությամբ DBMS-ը և Lua լեզվով հավելվածի սերվերը: Tarantool-ի վրա հիմնված լուծումների բարձր արագությունը ձեռք է բերվել, մասնավորապես, շնորհիվ DBMS-ի հիշողության ռեժիմի աջակցության և կիրառական բիզնես տրամաբանությունը տվյալների հետ մեկ հասցեի տարածքում գործարկելու հնարավորության շնորհիվ: Միևնույն ժամանակ տվյալների պահպանումն ապահովվում է ACID գործարքների միջոցով (սկավառակի վրա պահպանվում է WAL մատյան): Tarantool-ն ունի ներկառուցված աջակցություն կրկնօրինակման և բաշխման համար: Սկսած 2.1 տարբերակից՝ աջակցվում են հարցումները SQL լեզվով: Tarantool-ը բաց կոդով է և լիցենզավորված է պարզեցված BSD լիցենզիայի ներքո: Առկա է նաև կոմերցիոն Enterprise տարբերակը։

Զգացեք ուժը: (…նա վայելեք կատարումը)
Վերոնշյալ բոլորը Tarantool-ը դարձնում են գրավիչ հարթակ տվյալների բազաների հետ աշխատող բարձր բեռնված հավելվածներ ստեղծելու համար: Նման հավելվածներում հաճախակի է լինում տվյալների կրկնօրինակման անհրաժեշտություն:
Ինչպես նշվեց վերևում, Tarantool-ն ունի ներկառուցված տվյալների կրկնօրինակում: Նրա գործողության սկզբունքն է կրկնօրինակների վրա հաջորդաբար կատարել հիմնական մատյանում (WAL) պարունակվող բոլոր գործարքները: Սովորաբար նման կրկնօրինակում (մենք այն հետագայում կանվանենք ցածր մակարդակի) օգտագործվում է կիրառման սխալների հանդուրժողականությունն ապահովելու և/կամ կլաստերի հանգույցների միջև ընթերցման բեռը բաշխելու համար:

Բրինձ. 1. Կրկնօրինակում կլաստերի ներսում
Այլընտրանքային սցենարի օրինակ կարող է լինել մեկ տվյալների բազայում ստեղծված տվյալների փոխանցումը մեկ այլ տվյալների բազա՝ մշակման/մոնիթորինգի համար: Վերջին դեպքում ավելի հարմար լուծում կարող է լինել օգտագործելը բարձր մակարդակ replication - տվյալների կրկնօրինակում կիրառական բիզնես տրամաբանության մակարդակում: Նրանք. Մենք չենք օգտագործում DBMS-ում ներկառուցված պատրաստի լուծում, այլ ինքնուրույն իրականացնում ենք կրկնօրինակում մեր մշակվող հավելվածում: Այս մոտեցումն ունի և՛ առավելություններ, և՛ թերություններ: Թվարկենք առավելությունները.
1. Երթևեկության խնայողություն.
- Դուք չեք կարող փոխանցել բոլոր տվյալները, այլ միայն դրանց մի մասը (օրինակ, կարող եք փոխանցել միայն որոշ աղյուսակներ, դրանց որոշ սյունակներ կամ գրառումներ, որոնք համապատասխանում են որոշակի չափանիշի);
- Ի տարբերություն ցածր մակարդակի կրկնօրինակման, որը շարունակաբար իրականացվում է ասինխրոն (իրականացված Tarantool-ի ընթացիկ տարբերակում - 1.10) կամ համաժամանակյա (կիրականացվի Tarantool-ի հետագա տարբերակներում), բարձր մակարդակի կրկնօրինակումը կարող է իրականացվել նիստերում (այսինքն՝ հավելվածը նախ համաժամացնում է տվյալները՝ փոխանակման սեսիայի տվյալները, այնուհետև տեղի է ունենում կրկնօրինակման դադար, որից հետո տեղի է ունենում փոխանակման հաջորդ նիստը և այլն);
- եթե գրառումը մի քանի անգամ փոխվել է, կարող եք փոխանցել միայն դրա վերջին տարբերակը (ի տարբերություն ցածր մակարդակի կրկնօրինակման, որում հիմնականի վրա կատարված բոլոր փոփոխությունները հաջորդաբար կհնչեն կրկնօրինակների վրա):
2. HTTP փոխանակման իրականացման հետ կապված դժվարություններ չկան, ինչը թույլ է տալիս համաժամեցնել հեռավոր տվյալների բազաները:

Բրինձ. 2. Կրկնօրինակում HTTP-ով
3. Պարտադիր չէ, որ տվյալների բազայի կառուցվածքները, որոնց միջև փոխանցվում են տվյալները (ավելին, ընդհանուր դեպքում նույնիսկ հնարավոր է օգտագործել տարբեր DBMS-ներ, ծրագրավորման լեզուներ, հարթակներ և այլն)։

Բրինձ. 3. Վերարտադրությունը տարասեռ համակարգերում
Բացասական կողմն այն է, որ միջին հաշվով ծրագրավորումն ավելի դժվար/թանկ է, քան կազմաձևումը, և ներկառուցված ֆունկցիոնալությունը հարմարեցնելու փոխարեն դուք ստիպված կլինեք իրականացնել ձերը:
Եթե ձեր իրավիճակում վերը նշված առավելությունները վճռորոշ են (կամ անհրաժեշտ պայման են), ապա իմաստ ունի օգտագործել բարձր մակարդակի կրկնօրինակումը: Եկեք նայենք Tarantool DBMS-ում բարձր մակարդակի տվյալների կրկնօրինակման իրականացման մի քանի եղանակների:
Երթևեկության նվազեցում
Այսպիսով, բարձր մակարդակի կրկնօրինակման առավելություններից մեկը երթևեկության խնայողությունն է: Որպեսզի այս առավելությունն ամբողջությամբ իրացվի, անհրաժեշտ է նվազագույնի հասցնել փոխանակման յուրաքանչյուր նիստի ընթացքում փոխանցվող տվյալների քանակը: Իհարկե, չպետք է մոռանալ, որ նիստի վերջում տվյալների ստացողը պետք է համաժամանակացվի աղբյուրի հետ (գոնե տվյալների այն մասի համար, որը ներգրավված է վերարտադրության մեջ):
Ինչպե՞ս նվազագույնի հասցնել փոխանցվող տվյալների քանակը բարձր մակարդակի կրկնօրինակման ժամանակ: Պարզ լուծում կարող է լինել տվյալների ընտրությունն ըստ ամսաթվի և ժամի: Դա անելու համար կարող եք օգտագործել աղյուսակում արդեն գոյություն ունեցող ամսաթիվ-ժամ դաշտը (եթե այն գոյություն ունի): Օրինակ, «պատվեր» փաստաթուղթը կարող է ունենալ «Պահանջվող պատվերի կատարման ժամանակ» դաշտը. 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 Server-ում այս մոտեցումն իրականացնելու համար կա հատուկ սյունակի տեսակ. rowversion. Յուրաքանչյուր տվյալների բազա ունի հաշվիչ, որը մեծանում է մեկով ամեն անգամ, երբ գրառումը ավելացվում է/փոխվում է աղյուսակում, որն ունի նման սյունակ. rowversion. Այս հաշվիչի արժեքը ավտոմատ կերպով վերագրվում է այս սյունակի դաշտին ավելացված/փոփոխված գրառման մեջ: Tarantool DBMS-ը չունի նմանատիպ ներկառուցված մեխանիզմ: Այնուամենայնիվ, Tarantool-ում դժվար չէ այն իրականացնել ձեռքով: Եկեք նայենք, թե ինչպես է դա արվում:
Նախ, մի փոքր տերմինաբանություն. Tarantool-ում աղյուսակները կոչվում են բացատներ, իսկ գրառումները՝ tuples: 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 տեսակները, որոնք հիմնված են անվանմանը համապատասխան կառուցվածքների վրա։ ԾԱՌԸ ցուցիչի ամենաբազմակողմանի տեսակն է: Այն թույլ է տալիս հավաքել տվյալները կազմակերպված կերպով: Բայց հավասարության ընտրության համար ավելի հարմար է HASH-ը: Համապատասխանաբար, նպատակահարմար է օգտագործել HASH հիմնական բանալին (ինչը մենք արեցինք):
Սյունակն օգտագործելու համար row_ver փոխված տվյալները փոխանցելու համար անհրաժեշտ է այս սյունակի դաշտերին կապել հաջորդականության արժեքները row_ver. Բայց ի տարբերություն հիմնական բանալիի, սյունակի դաշտի արժեքը row_ver պետք է ավելանա մեկով ոչ միայն նոր գրառումներ ավելացնելիս, այլև եղածները փոխելիս: Դրա համար կարող եք օգտագործել տրիգերներ: Tarantool-ն ունի երկու տեսակի տիեզերական ձգան. before_replace и on_replace. Գործարկիչները գործարկվում են ամեն անգամ, երբ տարածության տվյալները փոխվում են (փոփոխություններից տուժած յուրաքանչյուր բազմակի համար գործարկվում է ձգանման գործառույթ): Ի տարբերություն on_replace, before_replace- triggers-ը թույլ է տալիս փոփոխել այն բազմակի տվյալները, որոնց համար գործարկվում է գործարկիչը: Ըստ այդմ, վերջին տիպի ձգանները համապատասխանում են մեզ։
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 պահվում է tuple հաջորդականության հաջորդ արժեքին 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 փոխված տվյալներ ստանալու համար: Iterator տեսակը GT է (Մեծ քան, ավելի մեծ): Սա նշանակում է, որ կրկնիչը հաջորդաբար կանցնի ինդեքսի արժեքները՝ սկսած անցած բանալիից (դաշտի արժեքը row_ver).
Կրկնիչը վերադարձնում է բազմակի: Որպեսզի հետագայում կարողանանք տվյալներ փոխանցել HTTP-ի միջոցով, անհրաժեշտ է վերափոխել tuples-ը հետագա սերիականացման համար հարմար կառուցվածքի: Օրինակը դրա համար օգտագործում է ստանդարտ ֆունկցիա 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 է: Իրական ծրագրում էջի չափը սովորաբար ավելի մեծ նշանակություն ունի: Դա կախված է տիեզերական tuple-ի միջին չափից: Էջի օպտիմալ չափը կարելի է որոշել էմպիրիկ կերպով՝ չափելով տվյալների փոխանցման ժամանակը: Որքան մեծ է էջի չափը, այնքան փոքր է շրջագայությունների քանակը ուղարկող և ստացող կողմերի միջև: Այս կերպ Դուք կարող եք կրճատել փոփոխությունները ներբեռնելու ընդհանուր ժամանակը: Այնուամենայնիվ, եթե էջի չափը չափազանց մեծ է, մենք չափազանց երկար կծախսենք սերվերի վրա՝ սերիականացնելու նմուշը: Որպես հետեւանք, կարող են լինել ուշացումներ սպասարկող այլ հարցումների մշակման հարցում: Պարամետր 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-ի տեսքով այս հոդվածի շրջանակներից դուրս: Այս մասին կարող եք կարդալ այստեղ՝
Հաճախորդ/ստրուկ մասի իրականացում
Տեսնենք, թե ինչ տեսք ունի ստացող կողմի իրականացումը: Եկեք ստացող կողմում ստեղծենք տարածք՝ ներբեռնված տվյալները պահելու համար.
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
Ֆունկցիան կատարում է HTTP հարցում url հասցեին և ուղարկում այն 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 DBMS-ի օգտագործող հավելվածներում տվյալների բարձր մակարդակի վերարտադրության արդյունավետ միջոցին:
Արդյունքները
- Tarantool DBMS-ը գրավիչ, խոստումնալից արտադրանք է բարձր բեռնվածության հավելվածներ ստեղծելու համար:
- Բարձր մակարդակի տվյալների կրկնօրինակումը մի շարք առավելություններ ունի ցածր մակարդակի կրկնօրինակման նկատմամբ:
- Հոդվածում քննարկված վերարտադրման բարձր մակարդակի մեթոդը թույլ է տալիս նվազագույնի հասցնել փոխանցված տվյալների քանակը՝ փոխանցելով միայն այն գրառումները, որոնք փոխվել են վերջին փոխանակման նիստից հետո:
Source: www.habr.com
