structon: خوندن یک فیلد بدون باز کردن کل رکورد
خلاصهٔ کاملتر
به گفتهی تیم Harper، وقتی یه دیتابیس شرطهای کوئری رو ارزیابی میکنه، خیلی وقتها مجبوره از تعداد زیادی رکورد فقط یک فیلد رو بخونه. برای یه کوئری تکشرطی روی فیلد ایندکسشده میشه مستقیم سراغ ایندکس رفت، ولی برای کوئری چندشرطی با AND فقط شرط اول از ایندکس درمیاد و بقیهی فیلدها باید روی هر رکورد کاندید چک بشن. سؤال اصلی اینه: چقدر ارزون میشه فقط یک فیلد از یه رکورد ذخیرهشده رو خوند؟
روش ساده اینه که کل رکورد رو به یه آبجکت جاوااسکریپت دیسریالایز کنی، یه پراپرتی رو بخونی و بعد آبجکتو دور بندازی. تو مقیاس کوچیک خوبه، ولی تو مقیاس بزرگ تبدیل به گلوگاه میشه، چون هر allocation و هر فیلدی که دیکد میشه ولی لازم نیست و هر فشار روی GC روی هم جمع میشه. راهحل، اِنکودینگ با دسترسی تصادفیه (random-access): رکورد تو یه فرمت باینری ذخیره میشه که هر فیلد رو میشه با پریدن مستقیم به آفست بایتیش خوند.
ایدهی اصلی اینه که آبجکتهایی که مجموعه کلیدهای یکسان (یعنی یک «شکل» یا shape) دارن، یه تعریف ساختار مشترک دارن که چیدمان بایتها رو توصیف میکنه. اِنکودر این تعریفها رو بهمرور که شکلهای جدید میبینه میسازه، و بعد از اون هر رکورد فقط مقدارها رو مینویسه نه اسم فیلدها رو. دیکودر هم از روی یه هدر کوتاه ساختارو تشخیص میده و پراپرتیهای lazy میده که فقط موقع دسترسی، مستقیم از روی بافر خام میخونن.
نویسنده توضیح میده که چرا فرمتهای موجود کافی نیستن. JSON برای رسیدن به هر فیلد باید کل متنو پارس کنه و مفهوم آفست بایتی نداره. MessagePack ساده فیلدها رو ترتیبی بهصورت جفت کلید-مقدار ذخیره میکنه، پس برای پیدا کردن یه فیلد باید تو استریم بایتها جلو بری. BSON مونگو یه جور دسترسی تقریباً تصادفی میده ولی اسم فیلدها رو تو تکتک داکیومنتها تکرار میکنه که هم بایت اضافه میخواد هم باز باید ترتیبی پیمایش بشه.
مزیت structon اینه که تعریف ساختارو بین همهی رکوردهای همشکل به اشتراک میذاره؛ اسم فیلدها یک بار ذخیره میشن و هر رکورد فقط مقدارها رو سر آفستهای ثابت نگه میداره. نتیجهش رکوردهاییه که از BSON فشردهترن و از هر چیزی که نیاز به پیمایش ترتیبی داره سریعتر خونده میشن. موقع دسترسی به یه پراپرتی، گتر مستقیم از همون آفست تو بافر اصلی میخونه:
const record = codec.decode(encoded);
console.log(record.age); // 30
console.log(record.name); // 'Alice'این اِنکودینگ از اول بهصورت ماژول struct.js و آپشن randomAccessStructure: true تو msgpackr بود، ولی برای استفادهی مستقل طراحی نشده بود. تو msgpackr 2.0 این ماژول کامل حذف شده و حالا structon جاش رو گرفته. structon عمومیتره: بهجای اینکه به دل msgpackr گره خورده باشه، یه فکتوری createStructon(BaseClass) میده که هر اِنکودر سازگار (مثل Packr خود msgpackr یا Encoder از cbor-x) رو میپیچه و قابلیت struct بهش اضافه میکنه.
نکتهی مهم اینه که تعریف ساختارها باید بین ریاستارتهای پروسه پایدار بمونن، وگرنه هر پروسهی جدید هیچ شکلی نمیشناسه و نمیتونه دادهی قبلی رو بخونه؛ structon با هوکهای پایداری استاندارد msgpackr و cbor-x کار میکنه و خود Harper این ساختارها رو تو متادیتای جدول نگه میداره. نویسنده میگه تنها جایی که این روش به ضرره، دادهی خیلی پویاست که تقریباً هر رکورد مجموعهی پراپرتی متفاوتی داره — که اون هم معمولاً نشونهی اینه که داده بهتره با Map مدل بشه.
نکات کلیدی:
- structon رکوردها رو طوری باینری میکنه که هر فیلد با آفست بایتی مستقیم خونده بشه، بدون دیسریالایز کل رکورد
- رکوردهای همشکل یه تعریف ساختار مشترک دارن؛ اسم فیلدها یک بار ذخیره میشه و رکورد فقط مقدارها رو نگه میداره
- نسبت به JSON و MessagePack و BSON هم فشردهتره هم برای دسترسی تکفیلد سریعتر
- فرمت باینریش دقیقاً همون چیزیه که struct.js تو msgpackr میساخت، پس دادهی قدیمی کاملاً سازگاره
- بیشترین سود رو تو کوئریهای چندشرطی و اسکن جدولهای بزرگ داره که اکثر فیلدها لازم نمیشن




