בדף הזה מוסבר איך להשתמש ב-Memorystore for Redis בצורה אופטימלית. בדף הזה מצוינות גם בעיות פוטנציאליות שכדאי להימנע מהן.
לרשימה של תרחישים לפתרון בעיות, ראו פתרון בעיות.
שיטות מומלצות לזמינות גבוהה ולעמידות נתונים
מומלץ להשתמש ברמת Standard כמנגנון הראשי לזמינות גבוהה (HA). במסלול הרגיל יש מכונה עם זמינות גבוהה ועם כמה רפליקות, ואפשר לשחזר במהירות באמצעות יתירות כשל אוטומטית אם המכונה הראשית נכשלת. בנוסף, הפעלת קובצי Snapshot של RDB במכונות במסלול Standard מספקת הגנה נוספת מפני כשלים בצומת.
במקרים מסוימים, יכול להיות שתרצו לוודא שאתם יכולים לשחזר נתונים מגיבויים של קובצי snapshot. במקרים כאלה, גיבויים והיכולת לשחזר נתונים מתמונות מצב של RDB יכולים לספק הגנה נוספת מפני אובדן נתונים.
אם תפעילו קובצי RDB snapshot ותצטרכו לשחזר את הנתונים, Memorystore for Redis יוכל לעשות זאת מה-snapshot האחרון. תמונות המצב האלה מיועדות לשחזור מערכת פנימי בלבד, ואין לכם גישה אליהן.
מגבלות עמידות ושיטות מומלצות
למרות שרמת Standard מספקת זמינות גבוהה באמצעות שכפול ויתירות כשל אוטומטית, זמינות גבוהה לא מבטיחה עמידות מלאה של הנתונים. באירועים קטסטרופליים מסוימים, כמו כשלים אזוריים זמניים, Memorystore for Redis לא יכול לספק עמידות מלאה של הנתונים, גם אם מופעלת זמינות גבוהה.
כדי להגן על הנתונים ולספק רמה בסיסית של עמידות, תמונות המצב של RDB מופעלות כברירת מחדל כשיוצרים מכונה של Memorystore for Redis באמצעות מסוף Google Cloud .
בעומסי עבודה בסביבת ייצור, כדאי ליישם את השיטות המומלצות הבאות כדי לשפר עוד יותר את עמידות הנתונים:
- תוכנית התאוששות מאסון (DR): Memorystore for Redis לא תומך ברפליקציה בין אזורים. אם אסטרטגיית תוכנית ההתאוששות מאסון (DR) דורשת שכפול בין אזורים כדי להשיג חוסן (resilience) אזורי אמיתי, מומלץ להשתמש במכונה של Memorystore for Valkey. מידע נוסף על הגדרה וניהול של מכונות משניות באזורים שונים זמין במאמר עבודה עם שכפול בין אזורים במסמכי התיעוד של Memorystore for Valkey.
- עמידות מקסימלית: אם האפליקציה דורשת את רמת העמידות הגבוהה ביותר של הנתונים, מומלץ להשתמש במכונה של Memorystore for Valkey שמוגדרת עם שכפול חוצה-אזורים, גיבויים יומיים אוטומטיים ושמירת נתונים בקובץ מסוג Append-Only File (AOF). השילוב הזה מספק את ההגנה החזקה ביותר מפני אובדן נתונים. מידע נוסף מופיע במאמר סקירה כללית על שמירת נתונים במאמרי העזרה של Memorystore for Valkey.
שיטות מומלצות לתכנון קיבולת
כדי לוודא שתוכלו להקצות משאבים בהצלחה ולבנות ארכיטקטורות גמישות, מומלץ לפעול לפי השיטות המומלצות הבאות לתכנון קיבולת:
- פריסה בכמה מיקומים: אם אתם לא מצליחים ליצור מכונה באזור המועדף בגלל אילוצי משאבים זמניים, נסו ליצור את המכונה באזור אחר באותו מחוז או במחוז אחר.
- שימוש בגישה חוצת-אזורים: מגדירים את הלקוחות כך שיוכלו לגשת למכונות שנמצאות באזורים שונים. אם אתם צריכים להקצות מכונה באזור אחר בגלל אילוצים של משאבים מקומיים, הלקוחות שלכם יכולים להתחבר למכונה.
- בחירת רמת קיבולת אחרת: אם נתקלתם במגבלות משאבים בגלל גודל המכונה, אתם יכולים לבחור רמת קיבולת אחרת או לשנות את גודל המכונה. מידע נוסף זמין במאמר ביצועים של רמות קיבולת.
ייצוא RDB
כשמייצאים גיבוי של RDB, צריך לפעול לפי ההנחיות הבאות:
- ייצוא במהלך תקופה של קצב כתיבה נמוך.
- אם מייצאים במהלך תקופה של קצב כתיבה גבוה, מנמיכים באופן זמני את ההגדרה של
maxmemoryל-50% מהקיבולת של המכונה כדי לספק מספיק תקורה לפעולה מוצלחת.
פעולות שצורכות הרבה משאבים
במכונות Redis במסלול Standard, הפעולות הבאות משתמשות בזיכרון נוסף למשך הפעולה:
שדרוג גרסה, התאמה לעומס ויתירות כשל ידנית משתמשים בזיכרון נוסף (במכונות ברמת Standard) בגלל הרפליקציה. הפעולות האלה מתבצעות לפי תהליך הרפליקציה שמתואר בקטע התנהגות השדרוג של מכונה ברמת Standard.
פעולות ייבוא וייצוא דורשות זיכרון נוסף בגלל תהליך ה-Redis המפוצל וניהול הנתונים מסוג copy-on-write שמשויך לפעולות האלה.
כדי לצמצם את החסרונות של פעולות שצורכות הרבה משאבים, כדאי:
- מפחיתים את ההגדרה maxmemory ל-80% מקיבולת המכונה למשך הפעולה. כך יש מספיק זמן כדי שהפעולה תסתיים בהצלחה.
- עוקבים אחרי המדד של יחס השימוש בזיכרון המערכת, ומוודאים שהמדד הזה נמוך מ-80% לפני שמריצים את אחת מהפעולות האלה.
- מומלץ להריץ את הפעולות האלה בתקופות שבהן התנועה במכונה נמוכה (למשל, בלילה או בסוף השבוע).
- לפני שמריצים את הפעולות האלה, צריך להגדיר לוגיקה של ניסיון חוזר עם השהיה מעריכית לפני ניסיון חוזר (exponential backoff).
פעולות ותרחישים שדורשים ניסיון חוזר להתחברות
הפעולות והתרחישים הבאים קוטעים את החיבור לרשת בין הרשת שלכם לבין המכונה של Redis:
- שדרוג גרסה
- הגדלה או הקטנה של התצוגה
- ייבוא
- מעבר ידני לגיבוי במקרה של כשל
- תחזוקת המערכת
- רוטציה של רשות האישורים במכונות של Redis שמופעלת בהן הצפנה במעבר
- יתירות כשל במקרה חירום
הפעולות האלה משנות את המכונה, ולכן נדרש ניתוק זמני של החיבור. לפני שמפעילים את הפעולות האלה, צריך להגדיר לוגיקה של ניסיון חוזר עם השהיה מעריכית לפני ניסיון חוזר (exponential backoff), כדי שהאפליקציה תתחבר מחדש באופן אוטומטי ותמשיך לפעול כרגיל.
תחזוקה שוטפת
מכונות של Memorystore for Redis עוברות תחזוקה מדי פעם. פרטים נוספים זמינים במאמר מדיניות התחזוקה של Memorystore for Redis.
כדי להתכונן לתחזוקה שוטפת, כדאי ליישם את השיטות המומלצות הבאות:
- להגדיר חלון זמן לתחזוקה שבו יתבצעו עדכוני התחזוקה.
- תזמנו חלונות זמן לתחזוקה בשעות שבהן נפח התנועה במכונה נמוך ויש מספיק זיכרון פנוי. מידע נוסף זמין במאמר ההשפעות של עדכוני תחזוקה.
- הפעילו התראות על חלונות זמן לתחזוקה כדי לקבל התראות על תחזוקה קרובה.
- הגדירו לוגיקה לניסיון נוסף עם השהיה מעריכית לפני ניסיון חוזר (exponential backoff).
- במכונות ברמת Standard, אתם יכולים לדמות אירוע תחזוקה באמצעות מעבר ידני לגיבוי במקרה של כשל, כדי לראות איך המעבר לגיבוי במקרה של כשל שנגרם כתוצאה מתחזוקה משפיע על האפליקציה.
- במכונות במסלול הבסיסי, אפשר לדמות את ההשפעה של עדכון תחזוקה על ידי הגדלה זמנית של המכונה. אחרי שרואים את ההשפעה, אפשר להחזיר את התקציב לגודל המקורי.
ניהול זיכרון
ניהול הזיכרון יכול להיות מאתגר בגלל פרגמנטציית הזיכרון הידועה שמתרחשת ב-Redis בקוד פתוח. מומלץ להקטין את ההגדרה maxmemory במכונה כדי להשאיר מרווח ביטחון למקרה של עומס גבוה על הזיכרון.
הדרך הטובה ביותר לעקוב אחרי העומס על הזיכרון במכונה של Memorystore היא להשתמש במדד System Memory Usage Ratio. במאמר שיטות מומלצות לניהול זיכרון מוסבר בפירוט איך לנהל את הזיכרון ב-Memorystore for Redis.
ניהול חיבורים לא פעילים
אם החיבורים לא נסגרים כמו שצריך, יכול להיות שעם הזמן מספר החיבורים למכונה של Memorystore יגדל. זה יכול להשפיע לרעה על הביצועים, במיוחד אם אתם משתמשים בהצפנה במעבר, שמטילה מגבלות מקסימליות על החיבורים בהתאם לרמת הקיבולת שלכם. כדי לצמצם את הבעיה, מומלץ להשתמש בtimeout פרמטר ההגדרה של Redis שמאפשר להגדיר את מספר השניות לפני שחיבורי לקוח לא פעילים ינותקו באופן אוטומטי.
שמות המשאבים ב-Access Transparency
אל תאחסנו מידע רגיש בשמות של משאבים ב-Memorystore for Redis. שמות המשאבים הם שמות המכונות של Memorystore for Redis והמטא-נתונים של המכונות, כמו תגים. אין ערובה לכך שנתונים שמאוחסנים בשמות של משאבים יהיו מוגנים על ידי Google Cloud Access Transparency, ויכול להיות שהם לא יעמדו בדרישות התאימות של הארגון ל-Access Transparency.
חיבור לרשת VPC שנדרש לחלק מהסביבות בלי שרת (serverless)
בחלק מהסביבות בלי שרת (serverless) צריך מחבר של חיבור לרשת (VPC) מאפליקציית serverless כדי להתחבר ל-Memorystore for Redis. אם אתם רוצים להתחבר באמצעות אחת מהסביבות האלה, הגדירו את המחבר של חיבור לרשת (VPC) מאפליקציית serverless בפרויקט.
Networking
מומלץ להשתמש במצב החיבור גישה לשירותים פרטיים. ב-Memorystore for Redis יש שני מצבי חיבור: גישה לשירותים פרטיים וקישור ישיר בין רשתות שכנות (direct peering). מצב החיבור של גישה לשירותים פרטיים מפשט את ניהול טווח ה-IP ומאפשר לכם להשתמש ב-VPC משותף אם תרצו.
אחרי שיוצרים מכונה, אי אפשר לשנות את מצב החיבור.
לפרטים נוספים, ראו רשתות.
מעקב והתראות
מומלץ להשתמש במעקב ובהתראות כי הם מספקים אותות חשובים לגבי השימוש בזיכרון של מכונות Redis. הם גם מספקים תובנות לגבי היעילות שבה מכונת Redis מגיבה לבקשות מטמון נכנסות.
מומלץ להגדיר את ההתראות הבאות כברירת מחדל:
- הגדרת התראה ב-Cloud Monitoring על שימוש בזיכרון
- הגדרת התראה ב-Cloud Monitoring על יחס השימוש בזיכרון המערכת
שיטות מומלצות לשימוש ב-CPU
שימוש לא נכון בפקודות Redis יקרות גורם לזמן אחזור ארוך, לחוסר תגובה או לבעיות בקישוריות. מכונות ברמת Standard מספקות זמינות גבוהה במהלך תוכנית התאוששות מאסון (DR), ומסתמכות על שכפול אסינכרוני בין צומתי המאסטר והרפליקה. אם באחד מהצמתים יש עיבוד פקודות יקר שחוסם את ה-thread הראשי של Redis, יכול להיות שהשכפול יושפע. אם הבעיה נמשכת ויש הפסקה במיקום, יכול להיות שהנתונים האחרונים שנכתבו במיקום שבו הייתה ההפסקה לא יהיו זמינים במיקום השני.
מומלץ להשתמש ב-Cloud Monitoring כדי להגדיר התראות למדד Main Thread CPU Seconds (redis.googleapis.com/stats/cpu_utilization_main_thread), כדי לוודא שניצול המעבד לא חורג מ-0.8 שניות לצומת הראשי או מ-0.5 שניות לכל צומת רפליקה, כשהרפליקה מוגדרת כרפליקה לקריאה בלבד.
אם מכסת המשאבים של מכונת Redis חורגת מהערכים המומלצים, מומלץ להגדיל את המכונה לרמת קיבולת גבוהה יותר או לפעול לפי ההוראות לפתרון בעיות כדי להימנע מפעולות שדורשות שימוש אינטנסיבי במעבד (CPU).
אם המכונה מנצלת את המעבד (CPU) בשיעור גבוה או שהמשאבים שלה מוצו (למשל, אם יש יותר מדי חיבורים), יכול להיות שהמכונה לא תפעל כמו שצריך ושהמדדים החיצוניים יהיו חסרים.
פקודות שדורשות הרבה משאבים
מומלץ מאוד להימנע משימוש בפקודות Redis שדורשות משאבים רבים. שימוש בפקודות האלה עלול לגרום לבעיות הבאות בביצועים:
- זמן אחזור ארוך וזמן קצוב לתפוגה של לקוח
- עומס על הזיכרון שנגרם בגלל פקודות שמגדילות את השימוש בזיכרון
- אובדן נתונים במהלך שכפול וסנכרון של צמתים, כי ה-thread הראשי של Redis חסום
- בדיקות תקינות, ניראות (observability) ויצירת רפליקות במצב של מחסור במשאבים
בטבלה הבאה מפורטות דוגמאות לפקודות Redis שצורכות הרבה משאבים, ומוצעות חלופות שצורכות פחות משאבים.
| קטגוריה | פקודה שצורכת הרבה משאבים | חלופה חסכונית במשאבים |
|---|---|---|
| הפעלה לכל מרחב המפתחות | KEYS |
SCAN |
| הפעלה של קבוצת מפתחות באורך משתנה | LRANGE |
הגבילו את גודל הטווח שבו אתם משתמשים בשאילתה. |
ZRANGE |
הגבילו את גודל הטווח שבו אתם משתמשים בשאילתה. | |
HGETALL |
HSCAN |
|
SMEMBERS |
SSCAN |
|
| חסימת הרצה של סקריפט | EVAL |
מוודאים שהסקריפט לא פועל ללא הגבלת זמן. |
EVALSHA |
מוודאים שהסקריפט לא פועל ללא הגבלת זמן. | |
| הסרת קבצים וקישורים | DEL |
UNLINK |
| פרסום והרשמה | PUBLISH |
SPUBLISH |
SUBSCRIBE |
SSUBSCRIBE |
שיטות מומלצות לשימוש בלקוח Redis
בקטע הזה מוסבר איך להשתמש בלקוח Redis בצורה אופטימלית.
זיהוי וטיפול בחיבורים שלא מגיבים
מומלץ מאוד להגדיר את אפליקציית הלקוח כך שתזהה חיבורים לא מגיבים ל-Memorystore for Redis. כשהלקוח מזהה חיבור שלא מגיב, הוא צריך לאפס אותו. כדי ליצור אפליקציה עמידה, מומלץ להשתמש בהגדרות הלקוח הבאות:
- הגדרת פרמטרים של TCP keep-alive: מגדירים את הפרמטרים
TCP keepalive time,TCP keepalive intervalו-TCP keepalive probesכך שהלקוחות יזהו חיבורים שלא מגיבים וינתקו אותם באופן יזום, גם כשהחיבורים לא פעילים. לדוגמה, אם מגדירים את הפרמטרTCP keepalive timeל-30 שניות, אתTCP keepalive intervalל-10 שניות ואתTCP keepalive probesל-3, הלקוחות מאפסים חיבורים לא פעילים שלא מגיבים תוך דקה. - הגדרת זמן קצוב לתפוגה של משתמשי TCP: מגדירים את הזמן הקצוב לתפוגה בלקוחות כדי לאפס חיבורים עם בקשות שלא טופלו שהפסיקו להגיב. לדוגמה, אם מגדירים את הזמן הקצוב לתפוגה ל-15 שניות, הלקוחות מאפסים חיבורים שלא מגיבים עם בקשות פתוחות אחרי 15 שניות.
שיטות מומלצות ללקוחות ספציפיים
אם מרחיבים את האפליקציות אופקית בהתאם לעומס (scale out), יכול להיות שיופיעו שגיאות READONLY בפקודות כתיבה. ב-Memorystore for Redis נעשה שימוש בפריסה ללא Sentinel עם נקודות קצה סטטיות, והלקוחות לא מקבלים מידע על תפקידים דינמיים מראש.
אם אתם משתמשים בחיבור לקוח אחד גם לנקודת הקצה הראשית וגם לנקודת הקצה של הרפליקה, יכול להיות שהלקוח ישלח בטעות פקודות כתיבה לרפליקה לקריאה בלבד. הרפליקה מחזירה שגיאת READONLY כי היא לא יכולה לעבד פקודות כתיבה.
כדי למנוע ניתוב שגוי של פעולות כתיבה, אל תשתמשו בחיבור אחד גם לפעולות קריאה וגם לפעולות כתיבה. במקום זאת, צריך לפצל את הפעולות על ידי יצירת המופעים הנפרדים הבאים של הלקוח:
- לקוח ראשי: התחברות רק לנקודת הקצה הראשית
- לקוח רפליקה: מתחבר רק לנקודת הקצה של הרפליקה
בכרטיסיות הבאות מוסבר איך להגדיר את התבניות הנפרדות ב-Go, ב-Java, ב-Node.js וב-Python.
המשך
package main import ( "context" "fmt" "github.com/redis/go-redis/v9" ) func main() { ctx := context.Background() // Initialize the primary client connecting only to the primary endpoint primaryClient := redis.NewClient(&redis.Options{ Addr: "PRIMARY_HOST:6379", Password: "YOUR_PASSWORD", DB: 0, }) defer primaryClient.Close() // Initialize the replica client connecting only to the replica endpoint replicaClient := redis.NewClient(&redis.Options{ Addr: "REPLICA_HOST:6379", Password: "YOUR_PASSWORD", DB: 0, }) defer replicaClient.Close() // Use the primary client for all mutating commands err := primaryClient.Set(ctx, "example_key", "example_value", 0).Err() if err != nil { fmt.Printf("Failed to write to primary: %v\n", err) } // Use the replica client for all read-only commands val, err := replicaClient.Get(ctx, "example_key").Result() if err != nil { fmt.Printf("Failed to read from replica: %v\n", err) } else { fmt.Printf("Successfully read value: %s\n", val) } }
Java
@Bean public RedisConnectionFactory primaryConnectionFactory() { RedisStandaloneConfiguration config = new RedisStandaloneConfiguration("PRIMARY_HOST", 6379); config.setPassword(RedisPassword.of("YOUR_PASSWORD")); return new LettuceConnectionFactory(config); } @Bean public RedisConnectionFactory replicaConnectionFactory() { RedisStaticMasterReplicaConfiguration config = new RedisStaticMasterReplicaConfiguration("PRIMARY_HOST", 6379); config.addNode("REPLICA_HOST", 6379); config.setPassword(RedisPassword.of("YOUR_PASSWORD")); LettuceClientConfiguration clientConfig = LettuceClientConfiguration.builder() .readFrom(ReadFrom.REPLICA_PREFERRED) .build(); return new LettuceConnectionFactory(config, clientConfig); } @Bean public RedisTemplate<String, Object> primaryRedisTemplate( @Qualifier("primaryConnectionFactory") RedisConnectionFactory factory) { RedisTemplate<String, Object> template = new RedisTemplate<>(); template.setConnectionFactory(factory); return template; } @Bean public RedisTemplate<String, Object> replicaRedisTemplate( @Qualifier("replicaConnectionFactory") RedisConnectionFactory factory) { RedisTemplate<String, Object> template = new RedisTemplate<>(); template.setConnectionFactory(factory); return template; }
Node.js
import { createClient } from 'redis'; async function main() { // Initialize the primary client connecting only to the primary endpoint const primaryClient = createClient({ url: 'redis://PRIMARY_HOST:6379', password: 'YOUR_PASSWORD' }); // Initialize the replica client connecting only to the replica endpoint const replicaClient = createClient({ url: 'redis://REPLICA_HOST:6379', password: 'YOUR_PASSWORD' }); primaryClient.on('error', (err) => console.error('Primary Client Error', err)); replicaClient.on('error', (err) => console.error('Replica Client Error', err)); await primaryClient.connect(); await replicaClient.connect(); // Use the primary client for all mutating commands try { await primaryClient.set('example_key', 'example_value'); console.log('Successfully wrote to primary'); } catch (err) { console.error('Failed to write to primary:', err); } // Use the replica client for all read-only commands try { const val = await replicaClient.get('example_key'); console.log(`Successfully read value: ${val}`); } catch (err) { console.error('Failed to read from replica:', err); } await primaryClient.disconnect(); await replicaClient.disconnect(); } main();
Python
import redis def main(): # Initialize the primary client connecting only to the primary endpoint primary_client = redis.Redis( host='PRIMARY_HOST', port=6379, password='YOUR_PASSWORD', decode_responses=True ) # Initialize the replica client connecting only to the replica endpoint replica_client = redis.Redis( host='REPLICA_HOST', port=6379, password='YOUR_PASSWORD', decode_responses=True ) # Use the primary client for all mutating commands try: primary_client.set('example_key', 'example_value') print('Successfully wrote to primary') except redis.RedisError as e: print(f'Failed to write to primary: {e}') # Use the replica client for all read-only commands try: val = replica_client.get('example_key') print(f'Successfully read value: {val}') except redis.RedisError as e: print(f'Failed to read from replica: {e}') if __name__ == '__main__': main()