מה זה שכבת Cache מבוזרת ולמה קאש מקומי מפסיק להספיק¶
קאשינג נשמע כמו נושא פשוט - שומרים ערך בזיכרון, שולפים אותו מהר בפעם הבאה. זה נכון, כל עוד יש לכם שרת אחד. ברגע שהמערכת שלכם גדלה ורצה על כמה שרתים במקביל, אפילו הרעיון הבסיסי הזה נשבר בדרך לא צפויה, וזה בדיוק המקום שבו שכבת קאש מבוזרת נכנסת לתמונה.
הבעיה - קאש מקומי לא מסתנכרן בין שרתים¶
תארו לעצמכם שהמערכת שלכם רצה על שלושה שרתים במקביל, מאחורי מאזן עומסים (load balancer), וכל שרת שומר קאש בזיכרון שלו - קאש מקומי (in-process cache). זה מהיר במיוחד, כי אין אפילו קריאה ברשת, רק גישה לזיכרון של אותו תהליך.
הבעיה מתחילה כשמשתמש שולח בקשה שמגיעה לשרת א', והבקשה הבאה שלו מגיעה לשרת ב' - כי מאזן העומסים לא בהכרח שולח את אותו משתמש תמיד לאותו שרת. אם שרת א' עדכן ערך בקאש המקומי שלו, שרת ב' פשוט לא יודע על זה בכלל. יש לכם עכשיו שלושה קאשים שונים, שכל אחד עשוי להחזיק גרסה אחרת של אותו מידע, בלי שום דרך לסנכרן ביניהם.
הפתרון - קאש אחד משותף לכולם¶
שכבת קאש מבוזרת (distributed cache) פותרת את זה בצורה ישירה - במקום שכל שרת יחזיק קאש נפרד משלו, כל השרתים פונים לשכבת קאש חיצונית משותפת, כמו Redis. עכשיו יש מקור אמת אחד לקאש. כשמשרת א' מעדכן ערך, שרת ב' רואה אותו עדכון מיד, כי שניהם פונים לאותה שכבה בדיוק, לא לזיכרון פנימי נפרד.
זה גם פותר בעיה נוספת - קיבולת. קאש מקומי מוגבל לזיכרון של שרת בודד, ואם יש לכם עשרה שרתים, אתם למעשה מבזבזים זיכרון על החזקת אותו מידע פעמים רבות. שכבת קאש מבוזרת מנוהלת במקום אחד, בגודל שאפשר לתכנן ולהתאים לפי הצורך האמיתי של המערכת, לא לפי מספר השרתים שיש לכם באותו רגע.
אז מה המחיר של קאש מבוזר¶
זה לא ארוחת חינם. קאש מקומי הוא בקריאה מזיכרון תהליך, כמעט ללא השהיה בכלל. קאש מבוזר דורש קריאת רשת לשרת נפרד, שגם אם היא מהירה מאוד (Redis בנוי בדיוק בשביל זה), היא עדיין איטית משמעותית מקריאת זיכרון מקומי. יש גם תלות נוספת במערכת - שכבת הקאש המבוזרת עצמה הופכת לרכיב קריטי שצריך לנטר, לתחזק, ולוודא שהוא זמין, כי אם הוא נופל, כל השרתים מאבדים גישה לקאש בבת אחת.
דפוס נפוץ - שילוב בין שתי השכבות¶
הרבה מערכות בקנה מידה גדול לא בוחרות בין קאש מקומי לקאש מבוזר - הן משתמשות בשני השכבות יחד, מה שנקרא לפעמים "קאש רב-שכבתי" (multi-tier caching). הבקשה בודקת קודם את הקאש המקומי המהיר מאוד, ורק אם היא לא מוצאת שם את המידע, פונה לקאש המבוזר האיטי יותר אבל המשותף, ורק אם גם שם אין - פונה למקור האמת, מסד הנתונים.
הגישה הזו נותנת את המהירות של קאש מקומי לרוב הבקשות, בלי לוותר על הסנכרון בין שרתים שקאש מבוזר נותן. המחיר הוא מורכבות נוספת - עכשיו יש שתי שכבות קאש לנהל, ושתיהן צריכות מנגנון ביטול תוקף משלהן.
מתי בכלל צריך את זה¶
אם המערכת שלכם רצה על שרת בודד, אין לכם עדיין את הבעיה הזו, וקאש מקומי פשוט מספיק לגמרי. ברגע שיש לכם יותר משרת אחד שרצים במקביל - וזה קורה מוקדם יותר ממה שרוב המפתחים חושבים, כבר בשביל זמינות גבוהה ולא רק בשביל עומס - קאש מקומי הופך לבעיית עקביות אמיתית, וקאש מבוזר הופך להכרחי.
איפה לומדים לתכנן את זה נכון¶
ההבדל בין קאש מקומי לקאש מבוזר נראה עדין עד שנתקלים בו בפועל, בדרך כלל דרך באג מוזר שקשה להבין למה הוא קורה רק לפעמים. בקורס צד שרת אנחנו בונים מערכת שרצה על כמה שרתים ונוגעים בדיוק בבעיות האלה, כדי שתזהו אותן מראש ולא תגלו אותן בייצור.
מתלבטים אם המערכת שלכם צריכה כבר שכבת קאש מבוזרת? שאלו בדיסקורד, זה תלוי בדיוק כמה שרתים אתם מריצים והאם קאש מקומי כבר גורם לכם לבעיות.
לסיכום¶
קאש מקומי מהיר אבל לא מסתנכרן בין שרתים, וקאש מבוזר פותר את בעיית הסנכרון במחיר של קריאת רשת נוספת. ברגע שהמערכת שלכם גדלה מעבר לשרת בודד, זו לא שאלה של "האם", אלא "מתי" תצטרכו לעבור לשכבת קאש משותפת. תבינו את הפשרה מראש, ותחסכו לעצמכם באג מבלבל שיגיע בדיוק ברגע שתוסיפו שרת שני.