فیس‌بوک، اینستاگرام، آمازون، فلیپ‌کارت…. این برنامه‌ها برنامه‌های مورد علاقه بسیاری از مردم هستند و احتمالاً اینها پربازدیدترین وب‌سایت‌هایی هستند که در لیست شما قرار دارند.

آیا تا به حال متوجه شده اید که این وب سایت ها نسبت به یک وب سایت کاملاً جدید زمان کمتری برای بارگذاری نیاز دارند؟ و آیا تا به حال متوجه شده اید که هنگام مرور یک وب سایت در یک اتصال اینترنتی کند، متن ها قبل از هر تصویر با کیفیت بالا بارگذاری می شوند؟

چرا این اتفاق می افتد؟ پاسخ Caching است.

اگر صفحه اینستاگرام خود را با اتصال اینترنت کند بررسی کنید، متوجه خواهید شد که تصاویر همچنان بارگذاری می شوند اما متن نمایش داده می شود. برای هر نوع کسب و کاری، این چیزها اهمیت زیادی دارند. تجربه مشتری/کاربر بهتر مهمترین چیز است و ممکن است به دلیل تجربه کاربری ضعیف با وب سایت خود، مشتریان زیادی را از دست بدهید. اگر کاربر متوجه شود که وب سایت فعلی زمان بیشتری برای بارگیری یا نمایش نتایج می گیرد، بلافاصله به وب سایت دیگری تغییر مکان می دهد. می توانید تماشای سریال های مورد علاقه خود را در هر برنامه پخش ویدیویی مثال بزنید. اگر ویدیو همیشه در بافر بماند، چه احساسی خواهید داشت؟ احتمال اینکه به آن سرویس پایبند نباشید و اشتراک را قطع کنید بیشتر است.

تمام مشکلات فوق را می توان با بهبود حفظ و تعامل در وب سایت شما و با ارائه بهترین تجربه کاربری حل کرد. و یکی از بهترین راه حل ها Caching است.

Caching – مقدمه

فرض کنید شما هر روز شام را آماده می کنید و برای تهیه غذا به موادی نیاز دارید. آیا هر زمان که غذا را تهیه کردید، برای خرید این مواد به نزدیکترین مغازه خود می روید؟ قطعا نه. این یک فرآیند زمان‌بر است و هر بار به جای بازدید از نزدیک‌ترین مغازه، می‌خواهید یک بار مواد را بخرید و آن را در یخچال خود نگهداری کنید. که باعث صرفه جویی در زمان زیادی می شود. این حافظه پنهان است و یخچال شما مانند یک حافظه پنهان/فروشگاه محلی/فروشگاه موقت کار می کند. اگر مواد غذایی از قبل در یخچال شما موجود باشد، زمان پخت کاهش می یابد.

همین اتفاقات در سیستم می افتد. در یک سیستم، دسترسی به داده ها از حافظه اصلی (RAM) سریعتر از دسترسی به داده ها از حافظه ثانویه (دیسک) است. حافظه پنهان به‌عنوان ذخیره محلی داده‌ها عمل می‌کند و بازیابی داده‌ها از این حافظه محلی یا موقت آسان‌تر و سریع‌تر از بازیابی آن از پایگاه داده است. آن را به‌عنوان یک حافظه کوتاه‌مدت در نظر بگیرید که فضای محدودی دارد اما سریع‌تر است و حاوی مواردی است که اخیراً به آنها دسترسی پیدا کرده‌اید. بنابراین اگر نیاز دارید که اغلب بر روی یک قطعه خاص از داده تکیه کنید، داده ها را کش کرده و سریعتر از حافظه به جای دیسک بازیابی کنید.

توجه: شما مزایای Cacheرا می دانید اما این بدان معنا نیست که تمام اطلاعات را برای دسترسی سریعتر در حافظه نهان ذخیره می کنید. به دلایل متعدد نمی توانید این کار را انجام دهید. یکی از دلایل آن سخت افزار Cache است که بسیار گرانتر از یک پایگاه داده معمولی است. همچنین، اگر تعداد زیادی داده را در حافظه پنهان خود ذخیره کنید، زمان جستجو افزایش می یابد. بنابراین به طور خلاصه، یک Cacheباید مطابق درخواستی که در آینده ارائه می شود، مرتبط ترین اطلاعات را داشته باشد.

cashing with redis

caching with redis

 

حافظه پنهان تقریباً در هر لایه از محاسبات استفاده می شود. برای مثال در سخت افزار، شما لایه های مختلفی از حافظه Cacheدارید. شما یک حافظه Cache لایه 1 دارید که حافظه نهان CPU است، سپس حافظه Cacheلایه 2 و در نهایت رم معمولی (حافظه دسترسی تصادفی) خواهید داشت. شما همچنین باید در سیستم عامل هایی مانند Cacheکردن پسوندهای مختلف هسته یا فایل های برنامه، Cache داشته باشید. شما همچنین در یک مرورگر وب برای کاهش زمان بارگذاری وب سایت، Cacheدارید. بنابراین حافظه پنهان تقریباً در هر لایه قابل استفاده است: سخت‌افزار، سیستم‌عامل، مرورگرهای وب و برنامه‌های کاربردی وب، اما اغلب در نزدیک‌ترین قسمت به جلویی یافت می‌شوند.

 

Cache چگونه کار می کند؟

به طور معمول، برنامه وب داده ها را در یک پایگاه داده ذخیره می کند. هنگامی که یک کلاینت مقداری داده را درخواست می کند، از پایگاه داده دریافت می شود و سپس به کاربر بازگردانده می شود. خواندن داده ها از پایگاه داده نیاز به تماس های شبکه و عملیات I/O دارد که فرآیندی زمان بر است. Cache  تماس شبکه با پایگاه داده را کاهش می دهد و عملکرد سیستم را افزایش می دهد. به عنوان مثال توییتر(سیستم دیزاین توییتر)را در نظر بگیرید: وقتی یک توییت ویروسی می شود، تعداد زیادی از مشتریان همان توییت را درخواست می کنند. توییتر یک وب سایت غول پیکر است که میلیون ها کاربر دارد. خواندن داده ها از دیسک برای این حجم زیاد از درخواست های کاربر ناکارآمد است. برای کاهش تعداد تماس‌های پایگاه داده، می‌توانیم از Cache استفاده کنیم و توییت‌ها بسیار سریع‌تر ارائه شوند.

در یک برنامه وب معمولی، می‌توانیم یک حافظه پنهان سرور برنامه، یک نمونه in-memory databaseمانند Redis را در کنار سرور برنامه خود اضافه کنیم. هنگامی که برای اولین بار درخواستی انجام می شود، برای پردازش پرس و جو باید با پایگاه داده تماس گرفته شود. این به عنوان از دست دادن حافظه پنهان شناخته می شود. قبل از بازگرداندن نتیجه به کاربر، نتیجه در حافظه پنهان ذخیره می شود. هنگامی که برای بار دوم کاربر همان درخواست را انجام می دهد، برنامه ابتدا حافظه پنهان شما را بررسی می کند تا ببیند آیا نتیجه آن درخواست ذخیره شده است یا خیر. اگر اینطور باشد، نتیجه از نمونه ذخیره شده درون حافظه بازگردانده می شود. زمان پاسخ برای درخواست بار دوم بسیار کمتر از بار اول خواهد بود.

in-memory database

in-memory database

انواع Cache

به طور معمول چهار نوع کش وجود دارد…

1. کش سرور برنامه

در «Cache چگونه کار می کند؟»  ما بحث کردیم که چگونه Cache سرور برنامه را می توان به یک برنامه وب اضافه کرد. در یک برنامه وب، فرض کنید یک وب سرور دارای یک گره است. یک Cache را می توان در حافظه در کنار سرور برنامه اضافه کرد. درخواست کاربر در این Cache ذخیره می شود و هر زمان که همان درخواست دوباره آمد، از Cache برگردانده می شود. برای درخواست جدید، داده ها از دیسک واکشی می شوند و سپس برگردانده می شوند. هنگامی که درخواست جدید از دیسک بازگردانده می شود، برای درخواست دفعه بعدی کاربر در همان Cache ذخیره می شود. قرار دادن حافظه پنهان روی گره لایه درخواست، ذخیره سازی لوکال را فعال می کند.

 

توجه: هنگامی که کش خود را در حافظه قرار می دهید، مقدار حافظه موجود در سرور توسط کش مصرف می شود. اگر تعداد نتایجی که با آنها کار می کنید بسیار کم است، می توانید کش را در حافظه نگه دارید.

مشکل زمانی به وجود می آید که شما نیاز دارید سیستم خود را مقیاس بندی کنید (مقیاس پذیری عمودی و افقی). شما چندین سرور را در برنامه وب خود اضافه می کنید (زیرا یک گره نمی تواند حجم زیادی از درخواست ها را انجام دهد) و یک load balancer دارید که درخواست ها را به هر گره ای ارسال می کند. در این سناریو، شما با تعداد زیادی از حافظه پنهان مواجه خواهید شد، زیرا هر گره از درخواست ذخیره شده قبلی آگاه نخواهد بود. این عالی نیست و برای غلبه بر این مشکل دو انتخاب داریم: Distribute Cache و Global Cache. که در ادامه به این دو می پردازیم

2. Distributed Cache

در Distributed Cache ، هر گره بخشی از کل فضای کش را خواهد داشت و سپس با استفاده از تابع هش سازگار، هر درخواست را می توان به جایی که درخواست کش پیدا کرد هدایت کرد. فرض کنید 10 گره در یک سیستم توزیع شده داریم و از یک متعادل کننده بار برای مسیریابی درخواست استفاده می کنیم و سپس…

هر یک از گره‌های آن بخش کوچکی از داده‌های کش را خواهد داشت.
برای تشخیص اینکه کدام گره دارای کدام درخواست است، حافظه نهان با استفاده از یک تابع هش ثابت تقسیم می شود، هر درخواست را می توان به جایی که درخواست ذخیره شده در حافظه پنهان یافت می شود، هدایت کرد. اگر یک گره درخواست کننده به دنبال قطعه خاصی از داده باشد، می تواند به سرعت بداند که در کش توزیع شده کجا را جستجو کند تا بررسی کند که داده ها در دسترس هستند یا خیر.
ما به راحتی می توانیم حافظه کش را با اضافه کردن گره جدید به استخر درخواست افزایش دهیم.

Distributed Cache

Distributed Cache

3. Global Cache

همانطور که از نام آن پیداست، شما یک فضای کش خواهید داشت و همه گره ها از این فضای واحد استفاده می کنند. هر درخواستی به این فضای کش می رود. دو نوع کش جهانی وجود دارد

اولاً، هنگامی که یک درخواست کش در کش جهانی یافت نمی شود، این وظیفه کش است که داده های گمشده را از هر جایی که در زیر فروشگاه قرار دارد (پایگاه داده، دیسک و غیره) پیدا کند.
دوم، اگر درخواست بیاید و کش داده ها را پیدا نکند، گره درخواست کننده مستقیماً با DB یا سرور برای واکشی داده های درخواستی ارتباط برقرار می کند.

Global Cache

Global Cache

4. CDN (شبکه توزیع محتوا)

CDN در جایی استفاده می شود که مقدار زیادی محتوای ثابت توسط وب سایت ارائه می شود. این می تواند یک فایل HTML، فایل CSS، فایل جاوا اسکریپت، تصاویر، ویدئوها و غیره باشد. ابتدا از CDN درخواست داده کنید، اگر وجود داشت، داده ها برگردانده می شوند. اگر نه، CDN سرورهای پشتیبان را پرس و جو می کند و سپس آن را به صورت محلی کش می کند.

content delivery network

content delivery network

عدم اعتبار کش

حافظه پنهان عالی است، اما در مورد داده هایی که دائماً در پایگاه داده به روز می شوند، چطور؟ اگر داده ها در DB اصلاح شده باشند، برای جلوگیری از رفتار ناهماهنگ برنامه، باید آنها را باطل کرد. بنابراین چگونه می‌توانید داده‌ها را در حافظه پنهان خود با داده‌های منبع حقیقت خود در پایگاه داده هماهنگ نگه دارید؟ برای آن، ما باید از برخی رویکردهای عدم اعتبار کش استفاده کنیم. سه طرح مختلف عدم اعتبار کش وجود دارد. بیایید یک به یک در مورد آن بحث کنیم…

1. نوشتن از طریق کش

 

همانطور که از نام آن پیداست، داده ها ابتدا در حافظه پنهان نوشته می شوند و سپس در پایگاه داده نوشته می شوند. به این ترتیب می توانید ثبات داده های خود را بین پایگاه داده و حافظه پنهان خود حفظ کنید. هر خواندنی که در حافظه پنهان انجام می‌شود، از آخرین نوشتن پیروی می‌کند.

Write Through Cache

Write Through Cache

مزیت این روش این است که شما خطر از دست دادن داده ها را به حداقل می رسانید زیرا هم در حافظه پنهان و هم در پایگاه داده نوشته شده است. اما نقطه ضعف این روش تأخیر بالاتر برای عملیات نوشتن است زیرا برای یک درخواست به‌روزرسانی باید داده‌ها را در دو مکان بنویسید. اگر حجم زیادی از داده ندارید، خوب است، اما اگر عملیات نوشتن سنگین دارید، این روش در آن موارد مناسب نیست.

ما می‌توانیم از این رویکرد برای برنامه‌هایی استفاده کنیم که داده‌های مکرر را پس از ماندگاری در پایگاه داده بازخوانی می‌کنند. در این برنامه‌ها تأخیر نوشتن را می‌توان با تأخیر و ثبات خواندن پایین‌تر جبران کرد.

2. اطراف کش بنویسید

مشابه نوشتن از طریق نوشتن در پایگاه داده، اما در این مورد، حافظه پنهان را به روز نمی کنید. بنابراین داده ها مستقیماً در حافظه ذخیره می شوند و از حافظه پنهان عبور می کنند. نیازی نیست حافظه پنهان را با داده هایی بارگیری کنید که دوباره خوانده نمی شوند. این رویکرد، عملیات نوشتن flooded را در مقایسه با حافظه پنهان رایت از طریق کاهش می دهد. نقطه ضعف این رویکرد این است که درخواست خواندن برای داده های اخیراً نوشته شده منجر به از دست دادن حافظه پنهان می شود و باید از یک Backend کندتر خوانده شود. بنابراین این رویکرد برای برنامه‌هایی مناسب است که اغلب جدیدترین داده‌ها را بازخوانی نمی‌کنند.

Write Around Cache

Write Around Cache

ما مفاهیم زیادی از ذخیره سازی را مورد بحث قرار داده ایم …. اکنون ممکن است یک سوال در ذهن خود داشته باشید. چه زمانی باید یک ورودی به حافظه پنهان ایجاد کنیم/بارگذاری کنیم و کدام داده ها را باید از حافظه پنهان حذف کنیم؟

کش در سیستم شما می تواند در هر لحظه از زمان پر باشد. بنابراین، ما باید از برخی الگوریتم ها یا استراتژی ها برای حذف داده ها از حافظه پنهان استفاده کنیم و باید داده های دیگری را بارگذاری کنیم که احتمال دسترسی آنها در آینده بیشتر است. برای این تصمیم می‌توانیم از برخی سیاست‌های تخلیه حافظه پنهان استفاده کنیم. بیایید یک به یک برخی از سیاست های تخلیه حافظه پنهان را مورد بحث قرار دهیم…

سیاست های حذف Cache

1. LRU (کمترین استفاده اخیر)

LRU به دلایل مختلف محبوب ترین سیاست است. ساده است، عملکرد خوبی در زمان اجرا دارد و نرخ ضربه مناسبی در بارهای کاری رایج دارد. همانطور که از نام آن پیداست، این خط‌مشی موردی که اخیراً کمتر استفاده شده را ابتدا از حافظه پنهان خارج می‌کند. هنگامی که حافظه پنهان پر می شود، کمترین داده های اخیراً استفاده شده را حذف می کند و آخرین ورودی به حافظه پنهان اضافه می شود.

هر زمان که نیاز به افزودن ورودی به حافظه پنهان داشتید، آن را در بالا نگه دارید و کمترین ورودی‌ها را از حافظه پنهانی که اخیراً کمتر استفاده شده است حذف کنید. ورودی‌های برتر ممکن است چند ثانیه پیش باشند و سپس شما به پایین‌تر شدن لیست چند دقیقه قبل، ساعت‌ها قبل، سال‌ها پیش ادامه می‌دهید و سپس آخرین ورودی را حذف می‌کنید (که اخیراً کمتر از آن استفاده شده است).

مثالی از هر سایت رسانه اجتماعی را در نظر بگیرید، یک سلبریتی وجود دارد که پستی گذاشته یا نظر گذاشته است و همه می خواهند آن نظر را بنویسند. بنابراین شما آن پست را در بالای حافظه پنهان نگه می دارید و بسته به اینکه آخرین پست چقدر باشد، در بالای کش باقی می ماند. وقتی پست سردتر می‌شود یا افراد دیگر آن پست را نگاه نمی‌کنند یا آن را مشاهده نمی‌کنند، همچنان در انتهای حافظه پنهان فشار داده می‌شود و سپس به طور کامل از حافظه پنهان حذف می‌شود. ما می‌توانیم LRU را با استفاده از یک لیست دارای پیوند دوگانه و یک تابع هش حاوی مرجع گره در لیست پیاده‌سازی کنیم.

2. LFU (کمترین استفاده از آن)

این خط‌مشی فراوانی هر مورد درخواستی را شمارش می‌کند و کمترین تعداد مورد را از حافظه پنهان حذف می‌کند. بنابراین در اینجا تعداد دفعاتی که به یک آیتم داده دسترسی پیدا می‌کند را می‌شماریم و فرکانس هر مورد را پیگیری می‌کنیم. هنگامی که اندازه کش به آستانه معینی رسید، ورودی را با کمترین فرکانس حذف می کنیم.

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

3. MRU (اخیراً استفاده شده)

این روش آخرین مورد استفاده شده را از حافظه پنهان حذف می کند. ما به آیتم قدیمی‌تر برای ماندن در حافظه پنهان ترجیح می‌دهیم. این رویکرد در مواردی مناسب است که کاربر علاقه کمتری به بررسی آخرین داده ها یا موارد دارد. اکنون ممکن است فکر کنید که اغلب کاربران به آخرین داده ها یا ورودی ها علاقه مند هستند، بنابراین کجا می توان از آنها استفاده کرد؟ خوب، می توانید برنامه دوستیابی Tinder را مثال بزنید که در آن می توان از MRU استفاده کرد.

Tinder حافظه پنهان همه موارد بالقوه یک کاربر را حفظ می کند. هنگامی که کاربر نمایه را در برنامه به چپ/راست می‌کشد، نمایه مشابهی را به کاربر توصیه نمی‌کند. اگر همان نمایه بارها و بارها توصیه شود، منجر به تجربه کاربری ضعیفی خواهد شد. بنابراین tinder نمایه را از حافظه پنهان حذف می کند که اخیراً مشاهده شده است، یعنی نمایه هایی که به چپ یا راست کشیده شده اند.

4. جایگزینی تصادفی

همانطور که از نام آن پیداست، ما به صورت تصادفی یک مورد را انتخاب می کنیم و آن را از حافظه پنهان حذف می کنیم تا هر زمان که لازم باشد فضا ایجاد شود.