תארו לעצמכם שיש לנו מערכת שבה משתמשים מוסיפים מאמרים. עבור כל משתמש, אנו מציגים נתונים סטטיסטיים על המאמרים שלו בלוח המחוונים האישי שלו: מספר המאמרים, ספירת המילים הממוצעת, תדירות הפרסום וכו'. כדי להאיץ את המערכת, אנו שומרים את הנתונים הללו במטמון. נוצר מפתח מטמון ייחודי עבור כל דוח.
נשאלת השאלה: כיצד לבטל מטמונים כאלה כאשר הנתונים משתנים? גישה אחת היא לנקות ידנית את המטמון עבור כל אירוע, למשל, כאשר מאמר חדש נוסף:
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) } }
גישה זו נוחה ואוניברסלית כאחד. מטמון מתויג מבטל את הצורך לציין באופן ידני את כל המפתחות לביטול, מה שהופך את התהליך לאוטומטי. עם זאת, זה דורש משאבים נוספים: אחסון נתוני גרסת התג ובדיקת תקפותם בכל בקשה.
אם שירות המטמון שלך מהיר ולא מוגבל מאוד בגודלו, גישה זו לא תשפיע באופן משמעותי על הביצועים. כדי למזער את העומס, אתה יכול לשלב מטמון מתויג עם מנגנוני מטמון מקומיים.
בדרך זו, אחסון במטמון מתויג לא רק מפשט את ביטול התוקף, אלא גם הופך את העבודה עם נתונים לגמישה ומובנת יותר, במיוחד במערכות מורכבות עם כמויות גדולות של נתונים מחוברים.