مفهوم Page Cache در دیتابیس:

چطور کار میکنه و چرا توی سرعت دیتابیس (خواندن و نوشتن) تاثیر بسیار زیادی داره؟


سناریو واقعی از دنیای دیتابیس‌ها: Page Cache در PostgreSQL

با یه مثال، فرض کنیم یه کوئری ساده می‌نویسیم توی PostgreSQL:


'SELECT * FROM users WHERE country = 'IRAN


قبل از اینکه بره از دیسک بخونه، یه سوال مهم از خودش می‌پرسه: "آیا این صفحه از دیتا توی cache هست؟"

  • اگه باشه (که بهش می‌گیم hit)، بدون هیچ دیسک I/O، دیتارو مستقیم از RAM می‌خونه که بسیار سریع و کم‌هزینه هستش✅
  • در غیراینصورت اگر نبود (یعنی miss)، باید بره از دیسک بخونه، صفحه رو توی cache بذاره، و تازه بعد به query پاسخ بده.❌


🟢وقتی با دیتابیس‌ها کار می‌کنیم، سرعت دسترسی به داده نقش کلیدی داره. یکی از ابزارهای پشت‌صحنه‌ای که خیلی روی این سرعت تأثیر می‌ذاره، چیزی به اسم Page Cache هست که حافظه‌ای توی رم هست و نسخه‌هایی از صفحات دیسک رو نگه می‌داره تا در صورت نیاز مجدد، مستقیماً از رم که سریع‌تره خونده بشه، نه از دیسک.


با یه خلاصه خودمونی، وقتی دیتابیس می‌خواد دیتایی رو بخونه یا تغییر بده، اول میره سراغ رم (Page Cache):


 اگه دیتای مدنظر اونجا بود، از همون استفاده می‌کنه

 اگه نبود، از دیسک می‌خونه و میذاره توی حافظه.


 همچنین هر تغییری هم که روی دیتا انجام بشه، اول توی همین حافظه تغییر انجام میشه و اون Page به حالت "کثیف" (dirty) درمیاد و باید یه زمانی این تغییر روی دیسک هم اعمال بشه. یعنی عملیات Write همیشه این مسیر یک‌طرفه حافظه به دیسک رو طی می‌کنه. (البته همزمان با تغییر حافظه، لاگ تغییرات هم ذخیره میشه که Durability هم حفظ بشه؛ مثلاً توی PostgreSQL از WAL استفاده میشه که اگه سیستم کرش کرد، تغییرات مجدداً روی دیسک ذخیره بشه.)



🔵خیلی مهمه که بدونیم باید چیارو توی حافظه نگه داریم چون حافظه محدوده. مثلاً توی DBMSهایی که از Storage Structure B-Tree استفاده می‌کنن، چون معمولاً هرچی به بالای درخت نزدیک‌تر می‌شیم به نودهایی می‌رسیم که برای پیدا کردن دیتاها بیشتر استفاده می‌شن، می‌تونیم همیشه اون‌ها رو توی حافظه نگه داریم یا به اصطلاح اون‌ها رو Pin کنیم.


 یا مثلاً وقتی داریم یه کوئری Range می‌زنیم، قسمتی از دیتای قبل و بعد از اون Range هم توی حافظه بیاریم چون احتمال می‌دیم که استفاده بشن. یا Pageهایی که در اثر کوئری‌ها متوجه می‌شیم اهمیت زیادی دارن رو هم Pin کنیم. همچنین این نکته مهمه که Pageهایی که به حالت dirty دراومدن، تا وقتی تغییراتشون روی دیسک اعمال نشده، نباید از حافظه حذف بشن.



🟢از اونجایی که حافظه محدود داریم، همیشه نمی‌تونیم همه صفحات رو توی cache نگه داریم. پس باید تصمیم بگیریم که کدوم صفحات رو بیرون بندازیم. این تصمیم رو الگوریتم‌های Page Replacement می‌گیرن. در ادامه، مهم‌ترین‌هاش رو معرفی می‌کنیم:

FIFO (First In, First Out)

  • ساده‌ترین الگوریتم.
  • همونطور که از اسمش هم مشخصه هر صفحه‌ای که اول وارد cache شده، اول هم خارج می‌شه.
  • مشکل: به دسترسی مجدد کاری نداره و اهمیتی به استفاده یا عدم استفاده از صفحات نمی‌ده.. مثلاً ممکنه صفحه‌ای که مدام استفاده می‌شه، چون زودتر اومده، زودتر هم بیرون انداخته بشه!

 البته یه حالت بهینه داره به اسم LRU که اگه یه Page مجدداً استفاده بشه، میره ته صف و دیرتر حذف میشه.


CLOCK (و CLOCK-sweep)

  • یه الگوریتم محبوب در سیستم‌عامل‌ها مثل Linux.
  • توی یه بافر دایره‌ای (شبیه عقربه ساعت)، صفحات و یه بیت به اسم access bit رو نگه می‌داره.
  • اگر صفحه‌ای استفاده شده باشه → access bit می‌شه ۱ → عقربه فقط از روش رد می‌شه و مقدارش رو صفر می‌کنه.
  • اگر صفحه‌ای استفاده نشده باشه و access bit صفر باشه → کاندیدای حذف می‌شه.


بنابراین، بسته به کاربرد، خیلی مهمه که الگوریتم Cache Replacement چی باشه. همه این الگوریتم‌ها trade-off خودشون رو دارن. برای دیتابیس‌ها، صرفاً "آخرین بار کی استفاده شد؟" معیار خوبی نیست؛ گاهی تعداد دفعات استفاده مهم‌تره. مثلاً در Queryهای تحلیلی سنگین، ممکنه الگوریتمی مثل CLOCK یا LRU بهتر جواب بده.


🔵این مطلب از کتاب ارزشمند Database Internals الهام گرفته شده.


#DatabaseInternals #Caching #PostgreSQL #Performance #DataEngineering #PageCache #BufferPool #DataBase #SystemsDesign #SQL #TechTips