הגדרת maxmemory ומדיניות פינוי ב-Redis לפרודקשן

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

היכולות שלנו:

שאלות נפוצות

העבודות האחרונות

  • פיתוח אפליקציית ווב עבור FEEDME
    פיתוח אפליקציית ווב עבור FEEDME
    1359
  • פיתוח חנות מקוונת לחברת FURNORO
    פיתוח חנות מקוונת לחברת FURNORO
    1330
  • עיצוב לוגו לחברת B2B Advance
    עיצוב לוגו לחברת B2B Advance
    776
  • פיתוח אפליקציית ווב עבור Enviok
    פיתוח אפליקציית ווב עבור Enviok
    1077
  • פיתוח לוגו חברת AIDER
    פיתוח לוגו חברת AIDER
    1022
  • פיתוח CRM עבור Chasseurs
    פיתוח CRM עבור Chasseurs
    1113

Redis maxmemory: למה מופע הייצור נופל בגלל זיכרון

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

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

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

השנייה בתור נפגעת היא השכפול: חוצץ פיגור שגדל, ניתוק החיבור לרפליקה וסנכרון מלא מחדש, שמעמיס על הצומת הראשי בדיוק ברגע שכבר קשה לו. במקביל מגיעה הידרדרות בזמן התגובה תחת עומס — fork לשמירה ברקע ודפים שנדחקו ל-swap מותחים את עיבוד הבקשות, וזמני ה-timeout של לקוחות מתרבים בשרשרת.

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

מה נותנת מדיניות פינוי מפתחות מוגדרת

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

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

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

אפשרויות להגבלת זיכרון ב-Redis maxmemory

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

אפשרות מתי מתאימה במה היא מגבילה
noeviction מטמון כפוף לנתונים: תורים, מונים, נעילות — אובדן מפתח אינו קביל כתיבה נכשלת בשגיאה, קריאה ממשיכה לעבוד; האפליקציה חייבת להתדרדר באופן תקין
allkeys-lru / allkeys-lfu מטמון טהור, כל מפתח ניתן לשחזור מהמקור מפנה גם מפתחות חמים בעת פרץ — פספוסים מגיעים בקבוצות
volatile-lru נתונים מעורבים: חלק מהמפתחות עם TTL, חלק קבועים משחרר רק מפתחות עם תפוגה; אם חלקם קטן — הופך בפועל ל-noeviction
volatile-ttl סשנים, טוקנים זמניים, מכסות — חשוב סדר השרידות עובד אך ורק לפי TTL; מפתחות בלי תפוגה לא משחררים זיכרון
allkeys-random עומס הומוגני בלי מפתחות חמים מובהקים לא מתחשב בתדירות הפניות, שיעור הפספוסים אינו צפוי

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

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

סדר הטמעת תקרות זיכרון בייצור

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

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

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

  3. חישוב הגבול. יוצאים מהפרופיל, לא מהזיכרון הפנוי במארח: משאירים מרווח לפרגמנטציה, לשמירה ברקע ולשכפול. את המדיניות מתאימים לפי סוג הנתונים — allkeys-lru למטמון, volatile-* במקום שבו TTL הוגדר במודע, noeviction לתורים ולסשנים.

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

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

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

קונפיגורציות, סכמות ונהלים לאחר ההטמעה

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

  • קבצי קונפיגורציה של redis: maxmemory ומדיניות פינוי קבועים לפרופיל העומס, מצב שמירה לדיסק, מגבלות חיבורי לקוחות וזמני timeout.
  • סכמת שכפול וזמינות גבוהה: טופולוגיית "ראשי — רפליקות", כללי מעבר אוטומטי, קוורום, פריסה לפי אזורי זמינות.
  • מדיניות גישה ותפקידים: חשבונות נפרדים לאפליקציה, לניטור ולמנהל, הרשאות לשינוי פרמטרים ולפקודות עזר, רוטציה של פרטי גישה.
  • לוחות ניטור וכללי התראה: מילוי זיכרון, שיעור מפתחות שפונו, שיעור פספוסי מטמון, פיגור רפליקות, ספים לתורנות לילה.
  • נוהל למשמרת תורנות: מה לבדוק בעת גידול בצריכת זיכרון, באיזה שלב להסלים, אילו פקודות בטוחות על צומת פעיל.
  • תוכנית למקרה גידול: קריטריונים למעבר לכמה צמתים, סדר העברת מפתחות, בדיקה שהאפליקציה שורדת החלפת טופולוגיה.
  • הוראות חזרה אחורה: תמונת מצב, סדר החזרת הפרופיל הקודם, הרצת שחזור בסביבת בדיקות.

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

מקרה בוחן: המופע הפסיק ליפול בעומס שיא

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

המחיר התבטא לא רק בנפילה. בשיא כ-6% מהכתיבות — הוספה לעגלה, עדכון מונה מבצע — קיבלו שגיאת זיכרון, וחלק מההזמנות נאספו מחדש ידנית. בזרימת ההזמנות הלילית זמן התגובה צנח פי 4-5: תור הבקשות למסד גדל מהר יותר ממה שהמופע הספיק להשיב.

מה שינו

את הנתונים הפרידו לפי סוגים ומדיניות פינוי: למטמון המדריך הפעילו allkeys-lru, לסשנים — volatile-ttl, והרשומות הקריטיות קיבלו מגבלה רק לפי זמן חיים של המפתח, כדי שהפינוי לא יפגע בהזמנות פעילות.

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

מה היה בסוף

סירובי כתיבה בשיא — 0 במקום 6%: אף הזמנה לא אבדה. זמן התגובה באחוזון ה-95 חזר לרמת יום רגוע, הירידה פי 4-5 נעלמה. הפינוי מתרחש, בשעות שיא מפונים עד חמישית מהנפח, אבל תחתיו נכנסים מדריכים ועגלות שפג תוקפם, ולא הזמנות פעילות.

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

ספקות טכניים בנוגע לפינוי ולשמירות

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

האם הפינוי ישמיד מפתחות חמים?

המדיניות מפנה רק את מה שההגדרה מרשה לה. ב-volatile-* למחיקה נכנסים אך ורק מפתחות עם זמן חיים — בנתונים קבועים לא נוגעים כלל. אם כל הסט הפכפך, אנחנו מסמנים מפתחות קריטיים בלי TTL ומעבירים אותם למופע נפרד, ואת שיעור המפתחות שפונו מחזיקים תחת ניטור: פרץ נראה מיד, ולא אחרי תלונות.

האם הפינוי מתנגש עם תמונות מצב לדיסק?

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

האם אפשר להחליף מצב בלי השבתה?

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

נדבר על המשימה ונחזור עם הערכה ותוכנית

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

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

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

נחזור עם סט קונקרטי:

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

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

במה שונה פינוי לפי זמן חיים מפינוי לפי נפח?

maxmemory קובע תקרה קשיחה לזיכרון, ו-maxmemory-policy קובע מה יקרה כשמגיעים אליה. המדיניות מתחלקות לשני סוגים: כאלה שמפנות רק מפתחות עם זמן חיים (volatile-lru, volatile-lfu, volatile-ttl) וכאלה שמוכנות לקחת כל מפתח (allkeys-lru, allkeys-lfu). ההבדל ביניהן אינו במהירות שחרור הזיכרון, אלא באיזה נתונים אתם מאבדים.

פינוי לפי זמן חיים מסיר בראש ובראשונה את מה שממילא היה אמור לפוג — למפתחות עם TTL יש פרופיל האובדן הצפוי ביותר. אם במסד חיים זה לצד זה מטמון ונתונים בלי זמן חיים, מצב volatile-* מבטיח: תחת לחץ ייעלם המטמון, ולא המדריך או המונה.

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

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

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