Tarantool DBMS дахь өндөр түвшний хуулбар

Сайн байна уу, би DBMS-д зориулсан программ үүсгэж байна Тарантуул нь өндөр үзүүлэлттэй DBMS болон Lua хэл дээрх програмын серверийг хослуулсан Mail.ru группын бүтээсэн платформ юм. Tarantool-д суурилсан шийдлүүдийн өндөр хурд нь ялангуяа DBMS-ийн санах ойн горимыг дэмжиж, мэдээллийн нэг хаягийн орон зайд програмын бизнесийн логикийг гүйцэтгэх чадварын ачаар бий болсон. Үүний зэрэгцээ ACID гүйлгээг ашиглан мэдээллийн тогтвортой байдлыг хангадаг (диск дээр WAL бүртгэл хөтөлдөг). Tarantool нь хуулбарлах, хуваах зориулалттай суурилуулсан дэмжлэгтэй. 2.1 хувилбараас эхлэн SQL хэл дээрх асуулгыг дэмждэг. Tarantool нь нээлттэй эх сурвалж бөгөөд хялбаршуулсан BSD лицензийн дагуу лицензтэй. Мөн арилжааны Enterprise хувилбар байдаг.

Tarantool DBMS дахь өндөр түвшний хуулбар
Хүч чадлыг мэдэр! (... гүйцэтгэлийг сайхан өнгөрүүлээрэй)

Дээр дурдсан бүхэн Tarantool-ийг мэдээллийн сантай ажилладаг өндөр ачаалалтай програмуудыг бий болгох сонирхолтой платформ болгодог. Ийм програмуудад ихэвчлэн өгөгдлийг хуулбарлах шаардлагатай байдаг.

Дээр дурдсанчлан Tarantool-д өгөгдөл хуулбарлах боломжтой. Түүний үйл ажиллагааны зарчим нь мастер бүртгэлд (WAL) агуулагдсан бүх гүйлгээг хуулбар дээр дараалан гүйцэтгэх явдал юм. Ихэнхдээ ийм хуулбар (бид үүнийг цаашид дуудах болно доод түвшин) нь програмын алдааны хүлцлийг хангах ба/эсвэл кластерийн зангилааны хооронд унших ачааллыг хуваарилахад ашиглагддаг.

Tarantool DBMS дахь өндөр түвшний хуулбар
Цагаан будаа. 1. Кластер доторх хуулбарлах

Альтернатив хувилбарын жишээ нь нэг мэдээллийн санд үүсгэсэн өгөгдлийг боловсруулах/хяналт хийх зорилгоор өөр мэдээллийн сан руу шилжүүлэх явдал юм. Сүүлчийн тохиолдолд ашиглах нь илүү тохиромжтой шийдэл байж болно өндөр түвшин хуулбарлах - програмын бизнесийн логик түвшинд өгөгдлийг хуулбарлах. Тэдгээр. Бид DBMS-д суурилуулсан бэлэн шийдлийг ашигладаггүй, харин өөрсдийн боловсруулж буй програмын хүрээнд хуулбарлах ажлыг бие даан хэрэгжүүлдэг. Энэ арга нь давуу болон сул талуудтай. Давуу талуудыг жагсаацгаая.

1. Замын хөдөлгөөний хэмнэлт:

  • Та бүх өгөгдлийг дамжуулах боломжгүй, гэхдээ зөвхөн нэг хэсгийг нь (жишээлбэл, та зөвхөн зарим хүснэгт, тэдгээрийн зарим багана эсвэл тодорхой шалгуурыг хангасан бүртгэлийг шилжүүлж болно);
  • Асинхрон (Tarantool-ийн одоогийн хувилбар - 1.10-д хэрэгжсэн) эсвэл синхрон (Tarantool-ийн дараагийн хувилбаруудад хэрэгжих) горимд тасралтгүй хийгддэг доод түвшний хуулбараас ялгаатай нь өндөр түвшний хуулбарыг сесс (өөрөөр хэлбэл, програм эхлээд өгөгдлийг синхрончилдог - солилцооны сессийн өгөгдөл, дараа нь хуулбарлах завсарлага, дараа нь дараагийн солилцооны сесс үүснэ гэх мэт);
  • хэрэв бичлэг хэд хэдэн удаа өөрчлөгдсөн бол та зөвхөн түүний хамгийн сүүлийн хувилбарыг шилжүүлж болно (бага түвшний хуулбараас ялгаатай нь мастер дээр хийсэн бүх өөрчлөлтийг хуулбарууд дээр дараалан тоглуулах болно).

2. Алсын мэдээллийн санг синхрончлох боломжийг олгодог HTTP солилцоог хэрэгжүүлэхэд ямар ч хүндрэл байхгүй.

Tarantool DBMS дахь өндөр түвшний хуулбар
Цагаан будаа. 2. HTTP дээр хуулбарлах

3. Өгөгдөл дамжуулах өгөгдлийн сангийн бүтэц нь ижил байх албагүй (түүнээс гадна ерөнхий тохиолдолд өөр өөр DBMS, програмчлалын хэл, платформ гэх мэтийг ашиглах боломжтой).

Tarantool 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 дахь хүснэгтүүдийг хоосон зай, бичлэгийг tuple гэж нэрлэдэг. 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 дарааллын дараагийн утга хүртэл хадгалагдсан 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 өөрчлөгдсөн өгөгдлийг хүлээн авах. Давталтын төрөл нь 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. Бодит программд хуудасны хэмжээ ихэвчлэн илүү чухал байдаг. Энэ нь сансрын 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 хэлбэрээр үр дүнг гаргахыг энэ нийтлэлийн хүрээнээс гадуур үлдээх болно. Та энэ тухай эндээс уншиж болно: 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 DBMS ашиглан програмуудад өндөр түвшний өгөгдлийг хуулбарлах үр дүнтэй аргыг авч үзсэн.

үр дүн нь

  1. Tarantool DBMS нь өндөр ачаалалтай програмуудыг бий болгоход зориулсан сэтгэл татам, ирээдүйтэй бүтээгдэхүүн юм.
  2. Өндөр түвшний өгөгдлийн хуулбар нь доод түвшний хуулбараас хэд хэдэн давуу талтай байдаг.
  3. Өгүүлэлд дурдсан өндөр түвшний хуулбарлах арга нь зөвхөн сүүлийн солилцооны сессээс хойш өөрчлөгдсөн бүртгэлийг шилжүүлэх замаар шилжүүлсэн өгөгдлийн хэмжээг багасгах боломжийг олгодог.

Эх сурвалж: www.habr.com

сэтгэгдэл нэмэх