
یک برنامه میتواند چه مقدار حافظه مجازی (virtual memory) را از سیستم عامل درخواست کند؟ مطمئناً، بیشتر از مقدار رم نصب شده در دستگاه نیست؟ آیا میتواند به این بستگی داشته باشد که سایر فرآیندها چقدر حافظه استفاده میکنند؟
ما در Meilisearch پاسخهای دقیق به این سوالات را به سختی پیدا کردیم. در این مقاله، ما در مورد یادگیریهای خود در مورد حافظه مجازی و نحوه تعامل آن با برنامهها به روشهای شگفتانگیز و ظریف صحبت خواهیم کرد.
Meilisearch یک موتور جستجوی متنباز است که هدف آن ارائه جستجوی سریع و فوقالعاده مرتبط با کمترین تلاش یکپارچهسازی است. این امکان را میدهد تا اسناد را در گروههایی به نام ایندکسها روی دیسک ذخیره کرده و این ایندکسها را برای بازیابی اسناد مرتبط با هر کوئری جستجو کنید.
از Meilisearch v1.1 (اکنون در v1.6 هستیم!)، محدودیت در تعداد و حداکثر اندازه ایندکسها را حذف شده است. در این مقاله، ما به اشتراک خواهیم گذاشت که چگونه مدیریت پویای حافظه مجازی برای اجرای این بهبودها کلیدی است.
حافظه مجازی و محدودیتهایی که برای Meilisearch ایجاد کرد
حافظه مجازی چیست؟
حافظه مجازی یک انتزاع ایجاد شده توسط سیستم عامل (OS) برای مدیریت حافظه فیزیکی است. این انتزاع به سیستم عامل اجازه میدهد تا فضای آدرس مجازی را برای فرآیندهای در حال اجرا فراهم کند. فضای آدرس مجازی هر فرآیند از فرآیندهای دیگر جدا است. به این ترتیب، از دسترسی تصادفی (یا مخرب) یک فرآیند به حافظه متعلق به فرآیند دیگر جلوگیری میشود. این در تضاد با دسترسی مستقیم همه فرآیندها به حافظه فیزیکی است، همانطور که در سیستمعاملهای قدیمیتر وجود داشت.
سیستم عامل با ترجمه صفحات حافظه مجازی که توسط برنامه کاربردی دستکاری میشوند به همتایان خود در حافظه فیزیکی، حافظه مجازی را پیادهسازی میکند.
حافظه مجازی همچنین به سیستم عاملها امکان میدهد مکانیزمی به نام swap (همچنین به عنوان pagefile نیز شناخته میشود) را پیادهسازی کنند.
نمودار زیر مثالی از حافظه مجازی برای دو فرآیند را نشان میدهد، که در آن هر فرآیند فقط میتواند به قسمتهایی از حافظه فیزیکی که توسط سیستم عامل به آن اختصاص داده شده است، دسترسی داشته باشد. یک صفحه از فرآیند A در دیسک “swapped out” است و در دسترسی بعدی از دیسک به حافظه فیزیکی بارگذاری خواهد شد.

حافظه مجازی در Meilisearch
در محیطهای یونیکس، میتوانید از ابزارهای رابط خط فرمان مانند htop برای مشاهده میزان حافظه مجازی استفاده شده توسط یک فرآیند استفاده کنید. در اینجا خروجی فرمان برای یک نمونه سرور Meilisearch را مشاهده مینمایید:

همانطور که میبینید، Meilisearch از ۸۵۹۳ گیگابایت حافظه مجازی استفاده میکند – بسیار بیشتر از حافظه فیزیکی (۱۶ گیگابایت) و دیسک (۱۰۰۰ گیگابایت) موجود در این دستگاه. حافظه مجازی میتواند به یک فرآیند، حافظه تقریبا نامحدود ارائه دهد. توجه داشته باشید که استفاده از حافظه فیزیکی – رم واقعی – بسیار کمتر است، و تنها ۳۸۴۶۴ کیلوبایت استفاده میشود.
عامل اصلی مسئول استفاده از حافظه مجازی Meilisearch، نگاشت حافظه یا memory mapping است، جایی که سیستم عامل محتویات یک فایل را به حافظه مجازی map میکند. Meilisearch از LMDB key-value store برای map کردن محتوای ایندکسهای ذخیره شده روی دیسک به حافظه مجازی استفاده میکند. به این ترتیب، هر عملیات خواندن یا نوشتن روی یک فایل ایندکس از طریق بافر حافظه مجازی اتفاق میافتد. سیستم عامل صفحات حافظه را به طور شفاف و کارآمد بین حافظه فیزیکی و دیسک جابهجا میکند.
حالا، مسئله این است که چنین فایلهای نگاشت شده (memory-mapped files) در حافظه میتوانند بخش قابل توجهی از فضای آدرس مجازی را اشغال کنند. هنگام map یک فایل ۱۰ گیگابایتی، شما اساسا به یک بافر حافظه مجازی پیوسته ۱۰ گیگابایتی نیاز دارید.
علاوه بر این، حداکثر اندازه قابل نوشتن در یک فایل از طریق memory mapping باید هنگام ایجاد mapping مشخص شود تا سیستم عامل بتواند اندازه یک بافر حافظه مجازی پیوسته را برای استفاده برای mapping تعیین کند.
با بازگشت به ۸۵۹۳ گیگابایت حافظه مجازی استفاده شده توسط Meilisearch، اکنون میفهمیم که بیشتر آن در واقع برای ایجاد نگاشت حافظه ایندکسهای سند استفاده میشود. Meilisearch این مقدار حافظه را برای اطمینان از اینکه این memory mapping ها برای جای دادن رشد ایندکسها روی دیسک به اندازه کافی بزرگ هستند، اختصاص میدهد.
اما محدودیت چیست؟ حافظه مجازی برای یک فرآیند چقدر میتواند رشد کند؟ و در نتیجه، چند ایندکس میتوانند همزمان وجود داشته باشند و با حداکثر اندازه کدام؟
۱۲۸ ترابایت فقط میتواند ایندکسهای زیادی را جای دهد!
از نظر تئوری، در رایانههای ۶۴ بیتی، فضای آدرس مجازی ۲^۶۴ بایت است. این ۱۸.۴۵ اگزابایت است. بیش از ۱۶ میلیون ترابایت! با این حال، در عمل، فضای آدرس مجازی اختصاص داده شده به یک فرآیند توسط سیستم عامل بسیار کوچکتر است: ۱۲۸ ترابایت در لینوکس و ۸ ترابایت در ویندوز.
۱۲۸ ترابایت ممکن است زیاد به نظر برسد. اما یک تعادل برای در نظر گرفتن بین تعداد ایندکسها (N) که میتواند توسط یک نمونه Meilisearch استفاده شود و حداکثر اندازه ایندکسها (M) وجود دارد. اساسا، ما نیاز داریم که N * M زیر محدودیت ۱۲۸ ترابایتی باقی بماند. و با ایندکسهای سند که گاهی اوقات از صدها گیگابایت فراتر میروند، این میتواند یک چالش باشد.
در نسخههای Meilisearch قبل از v1.0، این تعادل از طریق پارامتر –max-index-size CLI نشان داده میشد. این به توسعهدهندگان اجازه میداد تا اندازه نگاشت هر ایندکس را با پیشفرض ۱۰۰ گیگابایت تعریف کنند. با این نسخههای قبلی، اگر میخواستید ایندکسهایی بزرگتر از ۱۰۰ گیگابایت داشته باشید، باید مقدار –max-index-size را به حداکثر اندازه تخمین زده شده برای یک ایندکس تغییر میدادید.
بنابراین اگرچه این مسئله به راحتی آشکار نبود، تغییر مقدار پارامتر –max-index-size تعداد ایندکسهایی را که یک نمونه Meilisearch میتوانست استفاده کند، محدود میکرد: حدود ۱۰۰۰ ایندکس با مقدار پیشفرض ۱۰۰ گیگابایت در لینوکس و حدود ۸۰ در ویندوز. افزایش پارامتر برای جای دادن ایندکسهای بزرگتر، تعداد حداکثر ایندکسها را کاهش میداد. به عنوان مثال، حداکثر اندازه ایندکس ۱ ترابایتی شما را به ۱۰۰ ایندکس محدود میکند.
بنابراین چگونه میتوان در مورد مقدار –max-index-size تصمیم گرفت؟ پاسخ مستقیمی وجود نداشت. زیرا Meilisearch ساختار دادههایی به نام ایندکسهای معکوس ایجاد میکند که اندازه آنها به ویژگیهای متنی که ایندکس شده است به روشهای غیر بدیهی بستگی دارد. بنابراین، تخمین اندازه یک ایندکس از قبل دشوار بود.
رها کردن این تصمیم با پیامدهای ظریف در دست کاربر، احساس میشد که با هدف ما برای داشتن یک موتور ساده و قابل استفاده با پیشفرضهای خوب در تضاد است. با انتشار قریبالوقوع v1.0، نمیخواستیم پارامتر –max-index-size را تثبیت کنیم. بنابراین تصمیم گرفتیم این گزینه را در v1.0 حذف کنیم. ما موقتاً اندازه نگاشت حافظه یک ایندکس را به ۵۰۰ گیگابایت با برنامه ارائه یک راه حل شهودیتر در نسخه v1.1 کدگذاری کردیم.
به سوی بی نهایت و فراتر از آن با مدیریت پویای فضای آدرس مجازی
بیایید مشکل مورد نظر را با آرایههای با اندازه ثابت مقایسه کنیم. در زبانهای کامپایلشده، آرایههای با اندازه ثابت نیاز دارند که توسعهدهنده اندازه آنها را در زمان کامپایل تعریف کند زیرا نمیتوان آن را در زمان اجرا تغییر داد. با پارامتر منسوخ شده –max-index-size، کاربران Meilisearch با محدودیت مشابهی مواجه بودند. آنها مجبور بودند اندازه بهینه ایندکس را تعیین کنند، با سازش بین اندازه و تعداد کل ایندکسها.
آنچه واقعا این مشکل را غیرقابل حل کرد، دو مورد استفاده متضاد برای ایندکسها بود:
- ذخیره تعداد زیادی سند بزرگ در ایندکسها، بنابراین رسیدن ایندکسها به اندازه صدها گیگابایت؛
- ذخیره ایندکسهای زیاد، شاید به هزاران مورد (اگرچه این اغلب برای رسیدگی به چند مستاجری است که باید با توکنهای مستاجر اجرا شود).
کاربران اساسا با یک دوراهی مواجه بودند: داشتن اندازه ایندکس بزرگ اما محدود کردن تعداد ایندکسها یا محدود کردن اندازه ایندکس برای اجازه دادن به ایندکسهای بیشتر. ما باید به چیزی بهتر میرسیدیم.
در زبانهای کامپایل شده، توسعهدهندگان از آرایههای پویا زمانی استفاده میکنند که اندازه یک آرایه از قبل مشخص نباشد. یک آرایه پویا یک ساختار داده است که از سه بیت اطلاعات تشکیل شده است:
- یک اشاره گر به یک آرایه پیوسته تخصیص پویا؛
- اندازه تخصیص داده شده به این آرایه، نشان داده شده به عنوان ظرفیت؛ و
- تعداد فعلی عناصر ذخیره شده در آرایه.
اضافه کردن یک عنصر به یک آرایه پویا نیاز به بررسی ظرفیت باقی مانده آرایه دارد. اگر عناصر جدید برای آرایه موجود خیلی بزرگ باشند، آرایه با ظرفیت بزرگتر مجددا تخصیص داده میشود تا از سرریز جلوگیری شود. اکثر زبانهای سیستم پیادهسازی آرایههای پویا را به عنوان بخشی از کتابخانه استاندارد خود ارائه میدهند.




