שיעורי מודול (3/4)
נעילה ובחר לעדכון
להבין איך מסד נתונים עובד בבידוד על מסוף בודד זה פשוט... לא רגע, כבר אמרתי את זה!
לפעמים "עדכונים אטומיים" (למשל stock = stock - 1) אינם מספיקים.
בוא נדמיין שאתה צריך לבדוק אם למשתמש יש מספיק יתרה כדי לקנות דברים בשווי 50€:
SELECT balance FROM users WHERE id = 1;- JS: אם היתרה היא < 50, חסום אותם!
- אחרת המשך בקופה...
- הפעל את הניכוי! (והנה אתה מוצא משתמש שבזמן אימות הצ'ק בשלב 2, ירה עוד אחד מקביל שנצרב לו בחשבון ועכשיו אתה יורד מתחת לאפס!).
לעדכון נעילה מפורשת
הוספת FOR UPDATE בסוף SELECT הופכת בצורה קסומה את הקריאה התמימה שלך למנעול אטום מול לקוחות אחרים עד ל-COMMIT הבא. אף אחד אחר לא יוכל לבצע עדכון (או קריאה-לעדכון במקביל) בשורות הספציפיות שנבחרו עד שתחליט מה לעשות איתן!
BEGIN;
-- If id=3 had already been locked by T1, T2 will sit in perpetual loading on this "read"
-- frozen in place, before receiving the raw data, until T1 runs COMMIT freeing everyone!
SELECT balance
FROM users
WHERE id = 3
FOR UPDATE;
-- at this point our Node JS instance lives with the guarantee, knowing for certain that NO ONE ELSE in the world has
-- manipulated (nor been able to read with intent to alter) that balance while we complete the application logic
UPDATE users SET balance = balance - 50 WHERE id = 3;
COMMIT;יש אפילו FOR SHARE שמאפשר לקוראים אחרים להמשיך לקרוא (אם על נעילת בסיס) מבלי לחסום קריאות מצב, אלא לחסום כל עדכון אפשרי שלאחר מכן עד שתתחייב. עם זאת, עבור שינויים, השתמש תמיד בכוח הגס של סעיף הנעילה הבלעדי:
נסה זאת בעצמך
פתח עסקה רגילה בטווח זמן ונעל באופן בלעדי את הקריאה של מזהה המוצר=5.\nהרץ שאילתה דו-שלבית:\n1. פתח את העסקה (BEGIN)\n2. שאילתה 'מוצרים' מביאה את השורה המלאה עבור מזהה 5 עם ערבות שאף אחד לא ישנה אותה, על ידי צירוף האפשרות 'לעדכון'.
הצג רמז
BEGIN;, newline, SELECT * FROM products WHERE id = 5 לעדכון;
הפתרון זמין לאחר 3 ניסיונות
לפעמים העסקה נתקעת לתקופות הרות אסון (מבוי סתום או המתנה אינסופית).\nכדי למנוע זאת, אתה יכול לפקח על מסד הנתונים לוותר ברגע שהשורה לא תהיה זמינה באופן מיידי: שימוש ב-NOWAIT בסוף, מה שיגרום לחריג שרת SQL לחזור ל-JS כדי להיתפס בלי להיתקע לנצח.\nהפעל SELECT FORc FORc UPDATE=1 ו-enWAIT.
הצג רמז
בחר * מקטגוריות WHERE id = 1 לעדכון NOWAIT;
הפתרון זמין לאחר 3 ניסיונות