Sajad Kardel
Sajad Kardel
خواندن ۸ دقیقه·۴ سال پیش

آموزش Cache در Asp.Net Core (قسمت اول : مفاهیم اولیه)

در این مقاله برای نمایش کد ها از GitHub Gist استفاده شده و ممکن است Load شدن کد ها کمی طول بکشد یا به نرم افزار های رفع تحریم نیاز داشته باشید.

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

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

این حافظه Ram هست و عمل Caching به اینصورت خواهد بود که هر وقت دیتای مورد نظر یکبار از دیتابیس واکشی شود از دفعات بعد آن دیتا را در Ram ذخیره میکند و برای درخواست های بعدی به دیتابیس Query نمیزند و دیتای مورد نیازش را از Ram میگیرد.

این امر در کنار مزایایی که دارد ، حساسیت بالایی هم بهمراه خواهد داشت چرا که حافظه مورد استفاده Ram و یک حافظه محدود هست همچنین میتواند برای هر سخت افزاری متفاوت باشد پس پیاده سازی این سیستم نیاز به دو دو تا چهارتا و ساختار درست دارد ، در غیر اینصورت Cache کردن دیتای غلط میتواند به تنهایی وبسایتتان را Down کند پس خیلی باید به این موضوع دقت داشت.

چه زمانی از کش استفاده کنیم؟

  • وقتی دیتایی داریم که به تکرار از آن در برنامه استفاده میکنیم.
  • وقتی بعد از گرفتن دیتایی از دیتابیس محاسبات روی آن انجام میدهیم و پاسخ نهایی محاسبه را به کاربر نمایش میدهیم میتوانیم یکبار پاسخ را کش کنیم تا از محاسبه هر باره آن جلوگیری شود.

آیا همه اطلاعات را میتوان کش کرد؟

خیر.

  • سخت افزاری که برای کش استفاده میکنیم یعنی Ram بسیار گران تر از دیتابیس برای ما تمام میشود چرا که محدود است.
  • اگر همه دیتاهارا کش کنید عمل سرچ میان آن زمان بیشتری خواهد برد.

پس اکنون میدانید که میتوانیم داده های بی نهایت در دیتابیس ذخیره کنیم و فقط با ارزش ترین ها و پر مصرف ترین هارا در حافظه کش ذخیره میکنیم.

عملیات Cache در Asp.Net Core توسط اینترفیس های IMemoryCache و IDistributedCache مدیریت میشود و میتوانید با تزریق این اینترفیس ها براحتی از متدهایشان استفاده کنید اما قبل از استفاده لازم است با عملکرد هر یک آشنا شویم.

روش اول : In-memory Caching (Local Caching)

معمول ترین و ابتدایی ترین روش برای کش کردن اطلاعات روش Local Caching و بصورت In-Memory است که اطلاعات را در حافظه Ram همان سروری که برنامه در آن اجرا میشود کش میکند.

این روش تا زمانی که برنامه ما برای اجرا شدن تنها از یک سرور استفاده کند بهترین انتخاب خواهد بود چرا که به دلیل نزدیک بودن سریع ترین بازخورد را نیز به درخواست ها اراعه میدهد.

اما شرایطی را فرض کنید که برنامه از چندین سرور برای اجرا شدن استفاده میکند و به طبع هر سرور درخواست های خودش را داراست که ما باید برای هر یک بصورت جداگانه یک کش In-Memory در حافظه Ram هرکدام ایجاد کنیم.

فرض کنید دیتای ما 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 باشد. بخشی از دیتا در Server 1 کش میشود (1 , 3 , 5 , 9) و بخشی دیگر در Server 2 کش خواهد شد (2 , 4 , 6 ,7 , 8 , 10).

در اینجا مشکلات و ضعف هایی به وجود خواهد آمد :

  • برای مثال اگر Server 1 به هر دلیلی از بین برود یا Down شود اطلاعات کش درون آن نیز پاک خواهد شد و بعد از راه اندازی باید همه آن را دوباره از دیتابیس بخواند.
  • هر کدام از سرور ها کش های جدایی دارند و باهم Sync نیستند و امکان وجود یک داده حیاتی در یکی و عدم وجود آن در دیگری بالاست. فرض کنید برنامه برای هر درخواست نیاز به اطلاعات دسترسی کاربر دارد. دسترسی های کاربر در Server 1 کش شده اما در Server 2 موجود نیست. در Server 2 به دلیل عدم وجود این کش ، برنامه برای درخواست های معمول خود و چک کردن دسترسی کاربر یا باید هربار به دیتابیس درخواست ارسال کند که این برخلاف خواسته ماست و یا باید دیتای مربوط به دسترسی های کاربر را بعد از یکبار درخواست از دیتابیس در خودش کش کند که اینهم دوباره کاری به حساب میاید و دوبار کش کردن یک دیتا امر مطلوبی نخواهد بود.

روش هایی وجود دارد که بتوان از سیستم Local Caching در حالت چند سروری هم استفاده کرد و این مشکلات را از بین برد اما روش استاندارد در حالت چند سروری استفاده از Distributed Cache ها است.


روش دوم : Distributed Caching

در این روش برنامه ما برای اجرا شدن از چندین سرور شبکه شده به هم در حال استفاده هست و Cache برنامه توسط سرورها به اشتراک گذاشته شده.

در این حالت سرور های ما از یک کش عمومی استفاده میکنند که مزایای آن شامل :

■ درخواست ها به چندین سرور مختلف از هم ارسال شده اما دیتای کش بصورت منسجم در هریک وجود خواهد داشت.

■ با خراب شدن یا Down شدن یک سرور ، کش موجود در سرور های دیگر پاک نمیشود و کماکان قابل استفاده است.

■ به حافظه Ram یک سرور محدود نیست و مشکلات زیادی همچون کمبود سخت افزاری و محدودیت های حافظه Ram را تا حد معقولی کاهش میدهد.


طریقه استفاده از Cache در Asp.Net Core :

  • بر خلاف ASP.NET web forms و ASP.NET MVC در نسخه های Core به بعد Cache بصورت توکار وجود ندارد. کش در Asp.Net Core با فراخوانی سرویس های مربوطه آن قابل استفاده است و نیاز است قبل از استفاده سرویس آن را در کلاس Startup برنامه فراخوانی کنید.
https://gist.github.com/sajadkardel/adf2583f5716e4e8e023222d90818e76
  • اینترفیس IMemoryCache از سیستم تزریق وابستگی در Core استفاده میکند و برای استفاده از اینترفیس آن ، پس از اضافه کردن MemoryCache به Startup ، باید در کنترلر عمل تزریق وابستگی (DI) را انجام دهید سپس متد های مورد نیاز برای کش در دسترس خواهد بود.
https://gist.github.com/sajadkardel/a633ea81d0c319a59aeed725f10f9e69
  • برای ذخیره کش میتوانید از متد Set موجود در این اینترفیس استفاده کنید.
https://gist.github.com/sajadkardel/fccb7e121bb00aa43c6de9b17ac31b01

در پارامتر اول این متد (CacheKey) یک کلید برای اطلاعاتی که میخواهیم کش کنیم قرار میدهیم. دقت کنید که این کلید شناسه دیتای شماست و باید طوری آن را در نظر گرفت که با صدا زدن این کلید از سرویس کش همان دیتای مورد نظر را برگشت دهد (هر Object دیتا باید کلید Uniq خود را داشته باشد).

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

  • برای دسترسی به اطلاعات کش شده میتوانید از متد Get استفاده کنید.
https://gist.github.com/sajadkardel/60ffe9261caf7ae4f4c0233520d61387

تنها پارامتر ورودی این متد کلید از قبل نسبت داده شده به اطلاعات کش هست که با استفاده از یکسان بودن کلید در ورودی این متد و کلید Set شده از قبل در حافظه Ram ، دیتا مربوط به آن را برگشت میدهد.

  • متد TryGetValue برای بررسی وجود یا عدم وجود یک کلید در حافظه کش هست و یک Boolean خروجی میدهد.
https://gist.github.com/sajadkardel/50d09798263aa7491d9ab49569eb8f19

این متد ابتدا بررسی میکند که کلیدی با نام "CacheKey" وجود دارد یا خیر. در صورت عدم وجود آن را میسازد و دیتای مورد نظر را به آن نسبت میدهد.

  • با استفاده از متد GetOrCreate میتوانید کار متد های Get و Set را باهم انجام دهید و در یک متد وجود یا عدم وجود کش را بررسی و در صورت وجود مقدار را return و در صورت عدم وجود ، ابتدا ایجاد کش و بعد return مقدار کش شده را انجام دهید.
https://gist.github.com/sajadkardel/a6047b1c2d9b014ca1d3b5d1da4dbbb6






  • برای مدیریت حافظه Ram شما باید یک Expiration Time برای کش های خود مشخص کنید تا هم حافظه Ram را حجیم نکنید و هم در هر بازه زمانی دیتای بروز را از دیتابیس بخوانید. برای این کار option های متفاوتی از جمله absolute expiration و sliding expiration وجود دارد.

در اینجا absolute expiration به این معنی است که یک زمان قطعی را برای منقضی شدن کش ها مشخص میکند به عبارتی میگوییم کش با کلید فلان در تاریخ و ساعت فلان حذف شود.

اما در sliding expiration یک بازه زمانی برای منقضی شدن کش ها مشخص میکنیم یعنی میگوییم بعد از گذشت فلان دقیقه از ایجاد کش ، آن را حذف کن.

این تنظیمات را میتوانید در قالب یک option زمان Set کردن یک کش به آن بدهید.

https://gist.github.com/sajadkardel/b7d47616f1bd28a64eb232542c169c51

در مثال بالا هردو option اضافه شده یک کار را انجام میدهند با این تفاوت که absolute expiration تاریخ now را گرفته و یک دقیقه بعد را به آن اضافه کرده و تاریخ انقضای کش را با آن تاریخ set میکند. اما sliding expiration از حالا بمدت یک دقیقه اعتبار دارد.

  • یکی از روش های مدیریت حافظه Ram در کش ها این است که برای حذف شدن کش ها از حافظه اولویت بندی هایی تعریف کنید. اولویت ها در چهار سطح قابل دسترسی است:
1_ NeverRemove = 3
2_ High = 2
4_ Normal = 1
5_ Low = 0


این الویت بندی ها زمانی کاربرد خواهد داشت که حافظه اختصاصی Ram برای کش ها پر شده باشد و در این حالت سیستم کشینگ بصورت خودمختار کش های با الویت پایین را از حافظه حذف میکند و کش ها با الویت ببیشتر در حافظه باقی میماند. این با شماست که الویت را برای دیتا های خود تعیین کنید پس باید با دقت و فکر شده این کار را انجام دهید.

https://gist.github.com/sajadkardel/56c4ebcbc98938e12073ef48db094e13

به این صورت میتوانید الویت های متفاوت را در قالب option به کش های خود اختصاص دهید.


در این مقاله سعی شد مفاهیم اولیه Cache طوری گفته شود تا برای افرادی که میخواهند به تازگی این سیستم را بیاموزند و در پروژه های خود استفاده کنند کاربردی باشد و درک نسبی را نسبت به مزایا و محدودیت های این سیستم بدست آورند.

در قسمت دوم همین مقاله بطور تخصصی تر به این مبحث میپردازیم و یک پکیج آماده را معرفی میکنیم که خیلی راحت تر و اصولی تر کش را برای ما پیاده سازی میکند.


https://coffeebede.ir/buycoffee/sajadkardel


مقالات بیشتر در دات نت زوم

https://t.me/DotNetZoom

cacheکشcachingperformance
برنامه نویس
شاید از این پست‌ها خوشتان بیاید