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

התחילו מהלקוח, לא ממדדי השרת

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

מדדו את הנתיב עצמו לפני שאתם נוגעים בקוד. redis-cli -h redis.example.internal --latency שולח PING חוזרים ומדפיס את זמן הבקשה והתשובה הממוצע, שהוא העלות שלכם לכל חילוף. הכפילו אותו במספר הפקודות העוקבות בפעולת משתמש אחת ותקבלו רצפה לנקודת הקצה.

ספרו את הסבבים שפעולת משתמש אחת באמת דורשת

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

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

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

אגדו רק את הפקודות שאינן תלויות זו בזו

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

import redis

r = redis.Redis(host="redis.example.internal", port=6379, socket_timeout=2)
pipe = r.pipeline(transaction=False)
for key in ("user:1001", "user:1002", "user:1003"):
    pipe.get(key)
print(pipe.execute())

העבירו transaction=False כשאתם רוצים רק אגירה. כברירת מחדל הלקוח עוטף את המנה ב-MULTI ו-EXEC, מה שהופך אותה לאטומית ומשנה מה לקוחות אחרים יכולים לראות באמצע הדרך. אטומיות היא החלטת נכונות ולא כפתור ביצועים, וצינור אינו עסקה רק מפני ששניהם מפחיתים סבבים.

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

שמרו על החיבור חי בין פעולות

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

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

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

קבעו את גודל המנה לפני שהוא הופך לבעיית זיכרון

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

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

מדדו מקצה לקצה לאחר השינוי: redis-cli -h redis.example.internal --latency-history מראה אם הנתיב עצמו יציב, והטיימר שלכם לבקשות מראה אם נקודת הקצה השתפרה. אם נקודת הקצה עדיין איטית בעוד היסטוריית ההשהיה שטוחה, העלות שנותרה היא עבודה בצד השרת או הסריאליזציה שלכם, והצעד הבא הוא פרופיל של המטפל ולא שינוי אגירה נוסף.

מאמרים קשורים