בעת פיתוח אפליקציית מובייל עם מיליון משתמשים, נקודת הקצה של עדכון החדשות הפכה לצוואר בקבוק: שאילתת PostgreSQL ארכה 300–500 אלפיות השנייה, ובשיא העומס שרת מסד הנתונים נחנק מ-10,000 בקשות בשנייה. יישמנו מטמון Redis באמצעות תבנית Cache-Aside, וזמן התגובה ירד ל-2–5 אלפיות השנייה, בעוד שעומס מסד הנתונים ירד ב-70%. אופטימיזציה כזו מפחיתה משמעותית את עלויות תשתית השרת — בדרך כלל ב-40% — במיוחד עבור פרויקטים בעלי עומס גבוה. מטמון Redis מהיר עד פי 100 משאילתות ישירות למסד הנתונים. למהנדסים שלנו יש ניסיון של 7+ שנים עם Redis בסביבת ייצור, והם יישמו מטמון נתונים עבור למעלה מ-15 פרויקטים גדולים עם קהלים של מיליון משתמשים. מאמר זה מתמקד במטמון צד-שרת למובייל ובאופטימיזציה של API.
אילו בעיות אנחנו פותרים
הבעיה הנפוצה ביותר היא עדר הרעש (thundering herd): כאשר TTL פג, מאות בקשות מקבילות פוגעות בו-זמנית במסד הנתונים. התרחיש השני האופייני הוא שיעור פגיעות נמוך (מתחת ל-80%), שבו המטמון כמעט ואינו מנוצל עקב אסטרטגיית TTL שגויה או בחירת נתונים לא נכונה. הבעיה השלישית היא גידול בלתי מבוקר בזיכרון: ללא maxmemory, Redis יכול לצרוך את כל ה-RAM ולקרוס. תצורת Redis שלנו כוללת SET NX ומדיניות maxmemory 512mb כדי למנוע זאת.
אנחנו פותרים את אלה באמצעות:
-
הגנה מפני עדר הרעש: נעילת מבוזרת באמצעות
allkeys-lruאו פקיעה מוקדמת הסתברותית. - בחירת TTL נכונה: מותאמת לתדירות שינוי הנתונים (טבלה למטה).
-
מגבלת זיכרון:
feed:{user_id}:{page}:{version}, מדיניותimport redis import json r = redis.Redis() def get_news_feed(user_id: int, page: int, version: int): cache_key = f"feed:{user_id}:{page}:{version}" cached = r.get(cache_key) if cached: return json.loads(cached) # Попытка получить блокировку lock_key = f"lock:{cache_key}" if r.setnx(lock_key, 1): r.expire(lock_key, 5) # 5 секунд на генерацию data = db.query_feed(user_id, page) r.setex(cache_key, 300, json.dumps(data)) r.delete(lock_key) return data else: # Ждём и читаем из кэша повторно import time time.sleep(0.1) return get_news_feed(user_id, page, version).
הגישה שלנו: מקרה עדכון החדשות
עבור אפליקציה עם 5 מיליון משתמשים, הגדרנו Cache-Aside עם גרסאות מפתח. המפתח נוצר כ-KEYS *, כאשר הגרסה משתנה כשהנתונים משתנים (לדוגמה, כשמתווסף פוסט חדש). זה מנע פסילה המונית. כדי להגן מפני עדר הרעש, השתמשנו בנעילה:
import redis
import json
r = redis.Redis()
def get_news_feed(user_id: int, page: int, version: int):
cache_key = f"feed:{user_id}:{page}:{version}"
cached = r.get(cache_key)
if cached:
return json.loads(cached)
# Попытка получить блокировку
lock_key = f"lock:{cache_key}"
if r.setnx(lock_key, 1):
r.expire(lock_key, 5) # 5 секунд на генерацию
data = db.query_feed(user_id, page)
r.setex(cache_key, 300, json.dumps(data))
r.delete(lock_key)
return data
else:
# Ждём и читаем из кэша повторно
import time
time.sleep(0.1)
return get_news_feed(user_id, page, version)לאחר היישום, שיעור הפגיעות עלה מ-60% ל-95%, ומספר ה-evicted_keys ירד לאפס. מדדי שיעור פגיעות הם קריטיים להערכת יעילות המטמון.
למה פסילת מטמון Redis היא קריטית
כאשר נתונים מתעדכנים ישירות במסד הנתונים, המטמון נשאר מיושן עד שפג תוקף ה-TTL. זה מוביל לכך שמשתמשים רואים מידע לא מעודכן. אנו משתמשים בפסילה מונעת-אירועים: בעת כתיבה למסד הנתונים, נשלחת הודעה (באמצעות Kafka או Redis Pub/Sub) שמוחקת את המפתחות המתאימים. לפסילה המונית, אנו משתמשים ב-SCAN עם סמן (cursor) — זה בטוח לסביבת ייצור בניגוד ל-allkeys-lru. גישה מתקדמת יותר היא מטמון מתויג: כל אובייקט מקבל תג המאוחסן ב-SET. כשהאובייקט משתנה, אנו מוחקים את כל המפתחות לפי התג. זה מספק אטומיות ופסילה מבוקרת.
עוד על עדר הרעש
תבנית הפקיעה המוקדמת ההסתברותית היא שיטה נוספת: זמן מה לפני שפג תוקף ה-TTL (לדוגמה, ב-10% מה-TTL), עם הסתברות של 10% אנו מחדשים את המטמון. זה מפחית את הסיכוי לפספוסים בו-זמניים. תיעוד Redis ממליץ לשלב נעילה ופקיעה מוקדמת לאמינות מקסימלית.
השוואת תבניות מטמון
| תבנית | מהירות קריאה | עקביות | מורכבות יישום |
|---|---|---|---|
| Cache-Aside | גבוהה | נמוכה (אפשרות לכתיבה מיושנת) | נמוכה |
| Write-Through | בינונית | גבוהה (נתונים תמיד טריים) | בינונית |
| Read-Through | גבוהה | בינונית | גבוהה (דורש מודולי Redis) |
איך לבחור TTL נכון למטמון
אסטרטגיית TTL נבחרת לפי תדירות שינוי הנתונים:
| סוג נתונים | TTL מומלץ |
|---|---|
| תצורת אפליקציה | 1–24 שעות |
| קטלוג מוצרים | 5–30 דקות |
| עדכון חדשות | 1–5 דקות |
| פרופיל משתמש | 5–15 דקות |
ללא TTL, המטמון גדל עד שהזיכרון מתמלא. עם מדיניות KEYS *, Redis מתחיל להוציא מפתחות כשהזיכרון מלא — נתונים חשובים עלולים ללכת לאיבוד במפתיע. TTL מפורש אמין יותר.
למה לא להשתמש ב-KEYS בסביבת ייצור
הפקודה SCAN חוסמת את Redis למשך הזמן (O(N)), דבר שאינו מקובל בעומס גבוה. במקום זאת, השתמשו ב-DEL עם סמן ו-SADD tag:user:42 "feed:42:page:1" לכל מפתח שנמצא. או תגים: הקצו תג לכל מפתח בקבוצה SSCAN. בעת פסילה — SSCAN את התג ומחקו את הערכים. ויקיפדיה על Redis אינה מספקת פרטים כאלה, אבל הפרקטיקה מאשרת זאת.
מה כלול בעבודה
- ביקורת על ארכיטקטורת המטמון הנוכחית.
- עיצוב סכמת מפתחות, TTL והגנה מפני עדר הרעש.
- יישום התבנית הנבחרת (Cache-Aside / Write-Through).
- הגדרת ניטור Redis: מעקב אחר מדדי שיעור פגיעות, זיכרון והוצאות.
- תיעוד והדרכת צוות.
- תמיכה לאחר היישום.
לוחות זמנים: הגדרה בסיסית עם Cache-Aside לנקודות קצה עיקריות — יומיים עד שלושה ימי עסקים. אסטרטגיה מלאה עם פסילה וניטור — ארבעה עד שבעה ימים.
האיצו את אפליקציית המובייל שלכם עם הגדרת מטמון Redis מסוג מפתח ביד — המומחים שלנו יעריכו את הפרויקט שלכם ביום אחד ויציעו את האסטרטגיה האופטימלית. אנו מבטיחים SLA של 99.9%. קבלו ייעוץ על היישום — פשוט כתבו לנו.







