שיעורי מודול (2/4)
תנאי המירוץ
ההבנה כיצד מסד נתונים עובד בבידוד על מסוף בודד היא פשוטה, אך בתרחיש אינטרנטי אמיתי יהיו מאות תהליכי "Node/PHP/Go" במקביל לשלוח בקשות. כאן נולדים תנאי הגזע המפורסמים.
בעיית "עדכון אבוד".
עדכון אבוד מתרחש כאשר שני אשכולות קוראים את אותה שורה ישנה, עושים עליה קצת מתמטיקה בשרת JavaScript ואז שולחים בחזרה את העדכון כדי לשמור את התוצאה.
תַרחִישׁ:
- מוצר #5 מכיל 10 יחידות.
- משתמש A קונה אחד (שרת A קורא "10", מחשב "9").
- באותה אלפית שנייה משתמש B קונה אחר לפני שהרכישה של A מסיימת את שלב ה-COMMIT.
- שרת B קורא גם "10", מחשב "9".
- שרת A שולח
UPDATE: set quantity = 9. - שרת B שולח
UPDATE: set quantity = 9. - שניהם חושבים שהם הורידו את המספר בהצלחה. אבל הערך הסופי היה צריך להיות 8 יחידות! רכישת פנטום אבדה.
פתרון ללא עסקאות כבדות (Atomic Updates)
במקום לקרוא ולעשות את החישוב בחוץ, תן ל-DB לעשות זאת במהלך העדכון עצמו, שתמיד מסונכרן.
-- Wrong
UPDATE products SET stock = 9 WHERE id = 5;
-- Correct and natively thread-safe
UPDATE products SET stock = stock - 1 WHERE id = 5;חריגות קיצוניות יותר (Dirty Reads & Phantoms)
- קריאה מלוכלכת: קראת נתונים זמניים של מישהו אחר שעדיין לא אושרו על ידי COMMIT (ויכולים לעבור לפתע ל-ROLLBACK ולבטל אותך).
- Phantom Reads: אתה עושה סריקה של כל השורות, בינתיים שרשור שני מכניס רשומה חדשה, ואז אתה עושה סריקה נוספת במהלך אותה עסקה ואתה מגלה באורח קסם שהרשומה החדשה הופיעה משום מקום, ופוגעת בדוחות פיננסיים מחושבים.
שנה אטומית את רשומת הלקוחות לצורך מניפולציה קטנה של מחרוזת.\nנניח שאנחנו רוצים לשנות את שם ה'עיר' של לקוח כדי להוסיף כוכבית. \nעשה זאת על ידי מינוף הערך של השורה עצמה עבור זו עם id=10, שרשור ' - Old' לערך הנוכחי (השתמש בצינור השרשור של postgres ||).
הצג רמז
עדכון לקוחות SET city = city || ' - Old' WHERE id = 10;
הפתרון זמין לאחר 3 ניסיונות
עדכון קלאסי אבוד מתרחש בתשלומים ובמחירים.\nנניח שיש עליית מחירים לשעת חירום: הגדל את 'מחיר_יחידה' של טבלת 'פריטים_הזמנה' (עבור מוצר 1 של הזמנה 1) ב-20% באופן אטומי, כלומר הכפל אותו *1.2 במקום להכניס את המחיר החדש שחושב מראש ב-JS מראש.
הצג רמז
עדכן order_items SET unit_price = unit_price * 1.2 WHERE order_id = 1 ומזהה_מוצר = 1;
הפתרון זמין לאחר 3 ניסיונות