سلام من در حال ایجاد اپلیکیشن برای DBMS هستم
قدرت را احساس کن! (...از اجرا لذت ببرید)
همه موارد فوق Tarantool را به یک پلتفرم جذاب برای ایجاد برنامه های کاربردی با بار بالا که با پایگاه های داده کار می کنند تبدیل می کند. در چنین برنامه هایی، اغلب نیاز به تکرار داده ها وجود دارد.
همانطور که در بالا ذکر شد، Tarantool دارای تکثیر داده داخلی است. اصل عملکرد آن این است که تمام تراکنش های موجود در گزارش اصلی (WAL) را به صورت متوالی بر روی کپی ها اجرا کند. معمولاً چنین تکراری (ما آن را بیشتر می نامیم سطح پایین) برای اطمینان از تحمل خطای برنامه و/یا برای توزیع بار خواندن بین گره های خوشه استفاده می شود.
برنج. 1. همانندسازی در یک خوشه
نمونه ای از یک سناریوی جایگزین، انتقال داده های ایجاد شده در یک پایگاه داده به پایگاه داده دیگر برای پردازش/نظارت است. در مورد دوم، یک راه حل راحت تر ممکن است استفاده شود سطح بالا تکرار - تکرار داده ها در سطح منطق تجاری برنامه. آن ها ما از یک راه حل آماده ساخته شده در DBMS استفاده نمی کنیم، بلکه به تنهایی در برنامه ای که در حال توسعه هستیم، Replication را پیاده سازی می کنیم. این رویکرد هم مزایا و هم معایبی دارد. بیایید مزایا را فهرست کنیم.
1. صرفه جویی در ترافیک:
- شما نمی توانید تمام داده ها را منتقل کنید، بلکه فقط بخشی از آن را منتقل کنید (به عنوان مثال، می توانید فقط برخی از جداول، برخی از ستون ها یا رکوردهای آنها را که معیار خاصی را برآورده می کنند) منتقل کنید.
- بر خلاف تکرار سطح پایین، که به طور مداوم در حالت ناهمزمان (اجرا شده در نسخه فعلی Tarantool - 1.10) یا همزمان (که در نسخههای بعدی Tarantool پیادهسازی میشود) انجام میشود، تکرار سطح بالا را میتوان در جلسات انجام داد (به عنوان مثال، برنامه ابتدا داده ها را همگام می کند - داده های جلسه تبادل، سپس مکثی در تکرار وجود دارد، پس از آن جلسه تبادل بعدی رخ می دهد و غیره).
- اگر رکوردی چندین بار تغییر کرده باشد، میتوانید فقط آخرین نسخه آن را انتقال دهید (برخلاف تکرار سطح پایین، که در آن همه تغییرات ایجاد شده در Master بهطور متوالی بر روی ماکتها پخش میشوند).
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 فضاها و رکوردها تاپل نامیده می شوند. در 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
-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
تاپل را به مقدار بعدی دنباله ذخیره کرد 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 انتقال داد، لازم است تاپل ها را به ساختاری مناسب برای سریال سازی بعدی تبدیل کنیم. مثال برای این کار از تابع استاندارد استفاده می کند 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
در یک برنامه واقعی، اندازه صفحه معمولا اهمیت بیشتری دارد. این بستگی به اندازه متوسط تاپل فضایی دارد. اندازه بهینه صفحه را می توان به صورت تجربی با اندازه گیری زمان انتقال داده تعیین کرد. هرچه اندازه صفحه بزرگتر باشد، تعداد دفعات رفت و برگشت بین طرف ارسال کننده و گیرنده کمتر است. به این ترتیب می توانید زمان کلی دانلود تغییرات را کاهش دهید. با این حال، اگر اندازه صفحه خیلی بزرگ باشد، زمان زیادی را صرف سریال سازی نمونه توسط سرور خواهیم کرد. در نتیجه، ممکن است در پردازش سایر درخواستهایی که به سرور ارسال میشوند، تاخیر وجود داشته باشد. پارامتر 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()
ما همچنین به یک کتابخانه برای deserialization 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
به عنوان یک پارامتر و نتیجه deserialized درخواست را برمی گرداند.
تابع ذخیره داده های دریافتی به صورت زیر است:
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
صفر در مرحله بعد، ما یک بارگیری صفحه به صفحه از داده های تغییر یافته را از منبع در آدرس مشخص شده انجام می دهیم. در هر تکرار، داده های دریافتی را در فضای محلی مناسب ذخیره می کنیم و مقدار را به روز می کنیم 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 محصولی جذاب و امیدوارکننده برای ایجاد برنامه های کاربردی با بار بالا است.
- تکثیر داده های سطح بالا دارای چندین مزیت نسبت به تکرار سطح پایین است.
- روش تکرار سطح بالا که در مقاله مورد بحث قرار گرفت به شما این امکان را می دهد که با انتقال تنها آن دسته از رکوردهایی که از آخرین جلسه تبادل تغییر کرده اند، مقدار داده های منتقل شده را به حداقل برسانید.
منبع: www.habr.com