paint-brush
מטמון מתויג עשה זאת בעצמךעל ידי@ayacaste
225 קריאות היסטוריה חדשה

מטמון מתויג עשה זאת בעצמך

על ידי Anton Musatov7m2024/12/09
Read on Terminal Reader

יותר מדי זמן; לקרוא

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


גישה זו נוחה ואוניברסלית כאחד. מטמון מתויג מבטל את הצורך לציין באופן ידני את כל המפתחות לביטול, מה שהופך את התהליך לאוטומטי. עם זאת, זה דורש משאבים נוספים: אחסון נתוני גרסת התג ובדיקת תקפותם בכל בקשה.


אם שירות המטמון שלך מהיר ולא מוגבל מאוד בגודלו, גישה זו לא תשפיע באופן משמעותי על הביצועים. כדי למזער את העומס, אתה יכול לשלב מטמון מתויג עם מנגנוני מטמון מקומיים.


בדרך זו, אחסון במטמון מתויג לא רק מפשט את ביטול התוקף, אלא גם הופך את העבודה עם נתונים לגמישה ומובנת יותר, במיוחד במערכות מורכבות עם כמויות גדולות של נתונים מחוברים.