این بار، میخوام چیزایی که راجع به معماری الستیکسرچ یاد گرفتم با شما در میون بذارم. مسائلی که ندونستنشون باعث نمیشه که کار شما تو استفاده از الستیکسرچ گره بخوره، ولی دونستن این که پشت صحنه چه خبره میتونه خیلی به حل کردن مشکلات احتمالی سرعت بده.
در معماری الستیکسرچ، نودها (Nodes) و کلاسترها (Clusters) قسمت اصلی هستن. هر نود یک سِروِره که دادهها رو ذخیره میکنه و قسمتی از یک کلاستره.
هر کدوم از نودها بخشی از دادهها رو ذخیره میکنن و همهی اونها در ذخیره و جستوجوی دادهها مشارکت دارن. یعنی برای هر کوئری جستوجو، هر نود با جستوجو در دادههایی که خودش ذخیره کرده با بقیهی نودهای توی کلاستر همکاری میکنه.
هر کدوم از نودها میتونن درخواستهای HTTP رو بگیرن، پردازش کنن، و جواب بدن. یک نود مشخص در یک کلاستر راجع به همهی نودهای دیگه در همون کلاستر میدونه و میتونه درخواستهایی که براش میاد رو به نودهای دیگه ارجاع بده.
هر نود میتونه به عنوان رئیس بقیهی نودها انتخاب بشه. به این رئیس میگن مستر نود (Master Node). این نود رئیس تغییرات رو توی همهی نودهای یک کلاستر هماهنگ میکنه. مثل اضافه و حذف کردن یک نود یا اضافه و حذف کردن ایندکسها. مستر نود وضعیت کلاستر رو بهروزرسانی میکنه و تنها نودیه که میتونه این کار رو بکنه.
کلاسترها و نودها اسمهای یکتایی دارن. کلاسترها به صورت پیشفرض به نام «elasticsearch» نامگذاری میشن. نودها هم یک شناسهی یکتای جهانی (Universally Unique Identifer) دارند که به اختصار بهش میگن UUID. ما میتونیم این پیشفرضها رو تغییر بدیم. ولی باید حواسمون باشه که اسم نودها خیلی مهمه. چون با این اسمها میتونیم بفهمیم که کدوم ماشین، فیزیکی یا مجازی، متعلق به کدوم نود الستیکسرچ هست.
نودها به صورت پیشفرض به یک کلاستر به نام «elasticsearch» اضافه میشن ولی ما میتونیم این رفتار رو عوض کنیم. بهتره که اسم پیشفرض رو توی محیط پروداکشن عوض کنیم که مطمئن بشیم هیچ نودی تصادفن به کلاستر پروداکشن اضافه نمیشه.
هر دادهای که روی یک کلاستر الستیکسرچ ذخیره میکنیم، هر واحد داده که میتونه ایندکس بشه، یک داکیومنت هست. هر داکیومنت یک JSON هست که میتونه یک سطر در یک پایگاه دادهی رابطهای باشه. داکیومنتها تو یه جایی ذخیره میشن که بهشون میگن ایندکس. هر ایندکس مجموعهای از داکیومنتهای مربوط به همه. به عنوان مثال، ما میتونیم برای کاربران، سفارشها، یا محصولاتمون یک ایندکس داشته باشیم. هر داکیومنت یک ID یکتا داره که میتونه توسط ما یا توسط خود الستیکسرچ و به صورت اتوماتیک مقداردهی بشه. یک داکیومنت میتونه با استفاده از ایندکسش و ID یکتاش پیدا بشه. ایندکسها با اسمشون شناخته میشن که متشکل از حروف کوچیک انگلیسیه.
همونطور که گفتیم، الستیکسرچ خیلی مقیاسپذیر (Scalable) هست. یکی از دلایل این مقیاسپذیری رو میتونیم به ویژگی شاردینگ نسبت بدیم. با استفاده از شاردینگ، الستیکسرچ میتونه هر ایندکس رو به چند شارد تقسیم کنه. هر کدوم از این شاردها به تنهایی میتونن یک ایندکس محسوب بشن و تمام ویژگیهای یک ایندکس رو داشته باشن. هر داکیومنت در یک ایندکس روی یکی از این شاردها ذخیره میشه.
نکتهی جالب راجع به شاردها اینه که هر کدوم از اونها میتونن روی یک نود از کلاستر قرار بگیرن.
تعداد شاردها رو میتونیم زمان ساختن یک ایندکس مشخص کنیم. ولی اگه عددی رو براش مشخص نکنیم، به صورت پیشفرض، عدد ۵ در نظر گرفته میشه. بعدز از ساخته شدن یک ایندکس، دیگه نمیشه تعداد شاردهای اون رو تغییر داد. ولی اگه یه نفر با یک کلت روی شقیقهمون ازمون میخواست که تعداد شاردهای یک ایندکس رو تغییر بدیم، یه راه سخت برای این کار وجود داره. میتونیم یه ایندکس جدید با تعداد شاردهای مورد نظر بسازیم و دیتا رو از ایندکس قدیمی به اون منتقل کنیم.
نرمافزار یا سختافزار ما، با هر حد از توانایی، ممکنه گاهی برای لحظاتی دست از همکاری کردن با ما بکشن. برای همین همیشه لازمه که ما مکانیزمهایی برای مدیریت بحران در این شرایط در نظر گرفته باشیم. اینجاست که الستیکسرچ عزیز با ویژگی Replication یا تکثیر به کمک ما میاد. اینطوری که الستیکسرچ از شاردها کپی میگیره. شاردی که از اون کپی گرفته شده رو با اسم Primary Shard میشناسیم. شاردی که کپی یک شارد دیگه هست رو هم Replica Shard یا Replica صدا میکنیم. به یک پرایمری شارد و تمام رپلیکاهای اون هم میگیم Replication Group.
دو دلیل وجود داره که باعث میشه ویژگی رپلیکیشن خیلی مفید باشه:
تعداد رپلیکاها هم مثل تعداد شاردها همزمان با فرایند ساختن یک ایندکس تعیین میشن. به صورت پیشفرض، یک کلاستر الستیکسرچ که بیش از یک نود داره، برای هر شارد یک رپلیکا میسازه.
تغییراتی که روی داکیومنتها اعمال میش باید روی همهی شاردهایی که اون داکیومنترو دارن اعمال بشن. وگرنه، دادههایی که در جواب کوئریها برامون میان قابل اعتماد نیستن. تمام تغییرات، مثل اضافه کردن، حذف کردن، و تغییر دادن داکیومنتها، در مرحلهی اول روی پرایمری شارد اجرا میشن. پرایمری شارد بعد از اجرا شدن تغییر روی خودش، همون تغییر رو به صورت همزمان برای تمام رپلیکاهاش ارسال میکنه.
بعد از این که فهمیدم داکیومنتها چطوری روی اجزای مختلف یه کلاستر ذخیره میشن، برام سوال شد که الستیکسرچ از کجا میدونه که یه داکیومنت رو کجا باید ذخیره کنه؟ یا داکیومنتی که ذخیره شده کجاست؟
از طرفی به نظر نمیاومد داکیومنتها به صورت تصادفی بین نودها توضیع بشن. چون تصادفی بودن میتونه باعث بشه که تعداد داکیومنتهای ذخیره شده روی نودها خیلی با هم فرق داشته باشن.
تشخیص این که هر شارد کجا باید ذخیره بشه یا کجا ذخیره شده به عهدهی فرایندیه به اسم «Routing». هر درخواستی که برای یک داکیومنت به الستیکسرچ میاد، سیستم روتینگ با یک فرمول ساده تصمیم میگیره که باید سراغ کدوم یکی از شاردها بره.
به صورت پیشفرض، مقدار روتینگ برابر با ID یک داکیومنته. این مقدار وارد یک Hashing Function و تبدیل به یک عدد میشه که میتونه برای تقسیم استفاده بشه. باقیماندهی تقسیم اون عدد به تعداد پرایمری شاردهایی که تو ایندکس وجود دارن شمارهی شاردی که باید سراغش بریم رو بهمون میده.
shard = hash(routing) % total_primary_shards
با توجه به این فرایند، معلوم شد که چرا تعداد شاردهای یک ایندکس نمیتونن بعد از ساخته شدن تغییر کنن. فرض کنید ما تعداد شاردهامون ۵ تا باشه و با این تعداد یه سری داکیومنت رو ذخیره کنیم. حالا اگه تعداد شاردها تغییر کنه، مقدار به دست اومده از فرمول روتینگ هم تغییر میکنه و داکیومنتهای قدیمیمون دیگه در دسترس نیستن. دیوانهکنندهس.
چند خط بالاتر گفتم «به صورت پیشفرض». این یعنی این که فرایند روتینگ میتونه شخصیسازی بشه. که البته فرایند پیچیدهایه. شاید بعدن سراغ اون هم رفتیم.
نکتهای که نباید فراموش کنیم اینه که فرایندی که ذکر شد برای زمانیه که ما با دقیقن یک داکیومنت کار داریم. ولی وقتی که داریم یه کوئری جستوجو رو اجرا میکنیم، کوئری بین تمام شاردها پخش، یا به عبارت دقیقتر Broadcast میشه.