paint-brush
DIY برچسب کشتوسط@ayacaste
259 قرائت

DIY برچسب کش

توسط Anton Musatov7m2024/12/09
Read on Terminal Reader

خیلی طولانی؛ خواندن

توسعه دهندگان اغلب به شوخی می گویند که برنامه نویسی دو چالش اصلی دارد: نام گذاری متغیرها و عدم اعتبار کش. این شوخی دور از واقعیت نیست: مدیریت حافظه های پنهان، به ویژه باطل کردن آنها، واقعاً می تواند به یک کار جدی تبدیل شود. در این مقاله، نحوه اجرای آسان قابلیت کش برچسب گذاری شده بر اساس یک سرویس کش موجود را توضیح خواهم داد. تصور کنید ما سیستمی داریم که در آن کاربران مقالاتی را اضافه می کنند. برای هر کاربر، آماری در مورد مقاله‌های او در داشبورد شخصی‌اش نمایش می‌دهیم: تعداد مقالات، میانگین تعداد کلمات، فراوانی انتشار و غیره. برای افزایش سرعت سیستم، این داده‌ها را در حافظه پنهان ذخیره می‌کنیم. برای هر گزارش یک کلید کش منحصر به فرد ایجاد می شود. این سوال مطرح می شود: چگونه می توان چنین حافظه های پنهان را هنگام تغییر داده ها باطل کرد؟
featured image - DIY برچسب کش
Anton Musatov HackerNoon profile picture


توسعه دهندگان اغلب به شوخی می گویند که برنامه نویسی دو چالش اصلی دارد:

  • نام گذاری متغیرها
  • باطل کردن حافظه پنهان


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


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


این سوال مطرح می شود: چگونه می توان چنین حافظه های پنهان را هنگام تغییر داده ها باطل کرد؟ یک روش پاک کردن دستی حافظه پنهان برای هر رویداد است، به عنوان مثال، هنگامی که یک مقاله جدید اضافه می شود:

 class InvalidateArticleReportCacheOnArticleCreated { public function handle(event: ArticleCreatedEvent): void { this->cacheService->deleteMultiple([ 'user_article_report_count_' . event->userId, 'user_article_report_word_avg_' . event->userId, 'user_article_report_freq_avg_' . event->userId, ]) } }


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


نوشتن یک مقدار در حافظه پنهان با برچسب ها:

 this->taggedCacheService->set( key: 'user_article_report_count_' . user->id, value: value, tagNames: [ 'user_article_cache_tag_' . user->id, 'user_article_report_cache_tag_' . user->id, 'user_article_report' ] )


باطل کردن حافظه پنهان توسط برچسب ها:

 class UpdateCacheTagsOnArticleCreated { public function handle(event: ArticleCreatedEvent): void { this->taggedCacheService->updateTagsVersions([ 'user_article_cache_tag_' . user->id, ]) } }


در اینجا، تگ 'user_article_cache_tag_' . $user->id نشان دهنده تغییرات در مقالات کاربر است. می توان از آن برای باطل کردن حافظه های پنهان وابسته به این داده استفاده کرد. یک برچسب خاص تر 'user_article_report_cache_tag_' . $user->id اجازه می دهد تا فقط گزارش های کاربر پاک شود، در حالی که یک برچسب عمومی 'user_article_report' حافظه پنهان گزارش را برای همه کاربران بی اعتبار می کند.


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


ایجاد کلاس TaggedCache

 class TaggedCache { private cacheService: cacheService }


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

 class TaggedCache { private cacheService: cacheService public function set( key: string, value: mixed, tagNames: string[], ttl: int ): bool { if (empty(tagNames)) { return false } tagVersions = this->getTagsVersions(tagNames) tagsCacheKey = this->getTagsCacheKey(key) return this->cacheService->setMultiple( [ key => value, tagsCacheKey => tagVersions, ], ttl ) } private function getTagsVersions(tagNames: string[]): array<string, string> { tagVersions = [] tagVersionKeys = [] foreach (tagNames as tagName) { tagVersionKeys[tagName] = this->getTagVersionKey(tagName) } if (empty(tagVersionKeys)) { return tagVersions } tagVersionsCache = this->cacheService->getMultiple(tagVersionKeys) foreach (tagVersionKeys as tagName => tagVersionKey) { if (empty(tagVersionsCache[tagVersionKey])) { tagVersionsCache[tagVersionKey] = this->updateTagVersion(tagName) } tagVersions[$tagName] = tagVersionsCache[tagVersionKey] } return tagVersions } private function getTagVersionKey(tagName: string): string { return 'tag_version_' . tagName } private function getTagsCacheKey(key: string): string { return 'cache_tags_tagskeys_' . key }


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

 class TaggedCache { private cacheService: cacheService public function get(key: string): mixed { tagsCacheKey = this->getTagsCacheKey(key) values = this->cacheService->getMultiple([key, tagsCacheKey]) if (empty(values[key]) || empty(values[tagsCacheKey])) { return null } value = values[key] tagVersions = values[tagsCacheKey] if (! this->isTagVersionsValid(tagVersions)) { this->cacheService->deleteMultiple([key, tagsCacheKey]) return null } return value } private function isTagVersionsValid(tagVersions: array<string, string>): bool { tagNames = array_keys(tagVersions) actualTagVersions = this->getTagsVersions(tagNames) foreach (tagVersions as tagName => tagVersion) { if (empty(actualTagVersions[tagName])) { return false } if (actualTagVersions[tagName] !== tagVersion) { return false } } return true } }


پیاده سازی متد updateTagsVersions برای به روز رسانی نسخه های تگ. در اینجا، ما روی همه تگ‌های ارائه شده تکرار می‌کنیم و نسخه‌های آن‌ها را با استفاده از مثلاً زمان فعلی به‌عنوان نسخه به‌روزرسانی می‌کنیم.

 class TaggedCache { private cacheService: cacheService public function updateTagsVersions(tagNames: string[]): void { foreach (tagNames as tagName) { this->updateTagVersion(tagName) } } private function updateTagVersion(tagName: string): string { tagKey = this->getTagVersionKey(tagName) tagVersion = this->generateTagVersion() return this->cacheService->set(tagKey, tagVersion) ? tagVersion : '' } private function generateTagVersion(): string { return (string) hrtime(true) } }


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


اگر سرویس کش شما سریع باشد و از نظر اندازه محدودیت زیادی نداشته باشد، این رویکرد تأثیر قابل توجهی بر عملکرد نخواهد داشت. برای به حداقل رساندن بار، می توانید کش برچسب گذاری شده را با مکانیسم های ذخیره محلی ترکیب کنید.


به این ترتیب، ذخیره برچسب نه تنها عدم اعتبار را ساده می کند، بلکه کار با داده ها را انعطاف پذیرتر و قابل درک تر می کند، به خصوص در سیستم های پیچیده با مقادیر زیادی داده به هم پیوسته.

L O A D I N G
. . . comments & more!

About Author

Anton Musatov HackerNoon profile picture
Anton Musatov@ayacaste
Software Engineer

برچسب ها را آویزان کنید

این مقاله در ارائه شده است...