בדף הזה מוסבר איך להשתמש בסעיף FOR UPDATE ב-Spanner.
כשמשתמשים בשאילתת SELECT לסריקת טבלה, מוסיפים פסקה FOR UPDATE כדי להפעיל נעילות בלעדיות ברמת הגרנולריות של השורה והעמודה, שנקראת גם רמת התא. הנעילה נשארת במקום למשך חיי העסקה של קריאה וכתיבה. במהלך הזמן הזה, הסעיף FOR UPDATE מונע מטרנזקציות אחרות לשנות את התאים הנעולים עד שהטרנזקציה הנוכחית מסתיימת. מידע נוסף זמין במדריכי העזר בנושא GoogleSQL ו-PostgreSQL
FOR UPDATE.
למה כדאי להשתמש בסעיף FOR UPDATE
במסדי נתונים עם רמות בידוד פחות מחמירות, יכול להיות שיהיה צורך בסעיף FOR UPDATE כדי לוודא שעסקה מקבילה לא מעדכנת נתונים בין קריאת הנתונים לבין ביצוע העסקה. מכיוון ש-Spanner תמיד אוכף את האפשרות להסדרה, מובטח שהעסקה תתבצע בהצלחה רק אם הנתונים שאליהם ניגשים במסגרת העסקה לא יהיו ישנים בזמן ביצוע העסקה. לכן, הסעיף FOR UPDATE לא נחוץ כדי להבטיח את נכונות העסקה ב-Spanner.
עם זאת, בתרחישי שימוש עם מחלוקת גבוהה על גישת כתיבה, למשל כשכמה עסקאות קוראות וכותבות בו-זמנית לאותם נתונים, העסקאות הסימולטניות עלולות לגרום לעלייה בביטולים. הסיבה לכך היא שכמה עסקאות בו-זמניות מקבלות נעילות משותפות, ואז מנסות לשדרג לנעילות בלעדיות, והעסקאות גורמות לקיפאון. לאחר מכן, Spanner מבטל את כל העסקאות חוץ מאחת. מידע נוסף זמין במאמר בנושא נעילה.
עסקה שמשתמשת בסעיף FOR UPDATE מקבלת את הנעילה הבלעדית וממשיכה לפעול, בזמן שעסקאות אחרות מחכות לתור שלהן לנעילה.
יכול להיות ש-Spanner עדיין יגביל את קצב העברת הנתונים כי אפשר לבצע את העסקאות המתנגשות רק אחת בכל פעם, אבל מכיוון ש-Spanner מתקדם רק בעסקה אחת, הוא חוסך זמן שיוקדש אחרת לביטול עסקאות ולניסיון חוזר לבצע אותן.
לכן, אם חשוב לכם להפחית את מספר הטרנזקציות שבוטלו בתרחיש של בקשת כתיבה בו-זמנית, אתם יכולים להשתמש בסעיף FOR UPDATE כדי להפחית את המספר הכולל של הביטולים ולשפר את יעילות הביצוע של עומס העבודה.
השוואה לרמז LOCK_SCANNED_RANGES
הסעיף FOR UPDATE ממלא תפקיד דומה לזה של הרמז LOCK_SCANNED_RANGES=exclusive.
יש שני הבדלים עיקריים:
אם משתמשים ברמז
LOCK_SCANNED_RANGES, העסקה מקבלת נעילות בלעדיות בטווחים שנסרקו למשך כל ההצהרה. אי אפשר לקבל נעילות בלעדיות בשאילתת משנה. שימוש ברמז הנעילה עלול לגרום לרכישת נעילות רבות יותר מהנדרש ולתרום למאבק על נעילות בעומס העבודה. בדוגמה הבאה אפשר לראות איך משתמשים ברמז לנעילה:@{lock_scanned_ranges=exclusive} SELECT s.SingerId, s.FullName FROM Singers AS s JOIN (SELECT SingerId FROM Albums WHERE MarketingBudget > 100000) AS a ON a.SingerId = s.SingerId;לעומת זאת, אפשר להשתמש בסעיף
FOR UPDATEבשאילתת משנה, כמו בדוגמה הבאה:SELECT s.SingerId, s.FullName FROM Singers AS s JOIN (SELECT SingerId FROM Albums WHERE MarketingBudget > 100000) FOR UPDATE AS a ON a.SingerId = s.SingerId;אפשר להשתמש ברמז
LOCK_SCANNED_RANGESבהצהרות DML, אבל אפשר להשתמש רק בסעיףFOR UPDATEבהצהרותSELECT.
סמנטיקה של נעילה
כדי לצמצם את מספר בקשות הכתיבה בו-זמנית ואת העלות של טרנזקציות שבוטלו כתוצאה מקיפאון, Spanner נועל נתונים ברמת התא אם הדבר אפשרי. כשמשתמשים בסעיף FOR UPDATE, Spanner נועל תאים ספציפיים שנסרקים על ידי השאילתה SELECT.
בדוגמה הבאה, התא MarketingBudget בשורה SingerId = 1 ובשורה AlbumId = 1 נעול באופן בלעדי בטבלה Albums, כך שעסקאות מקבילות לא יכולות לשנות את התא הזה עד שהעסקה הזו תאושר או תבוטל. עם זאת, טרנזקציות מקבילות עדיין יכולות לעדכן את התא AlbumTitle בשורה הזו.
SELECT MarketingBudget
FROM Albums
WHERE SingerId = 1 and AlbumId = 1
FOR UPDATE;
יכול להיות שטרנזקציות מקבילות ייחסמו בקריאת נתונים נעולים
אם עסקה אחת מקבלת נעילות בלעדיות בטווח שנסרק, יכול להיות שעסקאות מקבילות יחסמו את קריאת הנתונים האלה. ב-Spanner נאכפת סריאליזציה, כך שאפשר לקרוא נתונים רק אם מובטח שהם לא ישתנו על ידי טרנזקציה אחרת במהלך משך החיים של הטרנזקציה. יכול להיות שעסקאות מקבילות שמנסות לקרוא נתונים שכבר נעולים יצטרכו להמתין עד שהעסקה שנועלת את הנתונים תאושר או תבוטל.
בדוגמה הבאה, Transaction 1 נועל את התאים MarketingBudget עבור 1 <= AlbumId < 5.
-- Transaction 1
SELECT MarketingBudget
FROM Albums
WHERE SingerId = 1 and AlbumId >= 1 and AlbumId < 5
FOR UPDATE;
הפעולה Transaction 2, שמנסה לקרוא את MarketingBudget עבור AlbumId = 1, חסומה עד ש-Transaction 1 יבצע commit או יבוטל.
-- Transaction 2
SELECT MarketingBudget
FROM Albums
WHERE SingerId = 1 and AlbumId = 1;
-- Blocked by Transaction 1
באופן דומה, עסקה שמנסה לנעול טווח שנסרק עם FOR UPDATE נחסמת על ידי עסקה מקבילה שנועלת טווח סריקה חופף.
גם Transaction 3 בדוגמה הבאה חסום, כי Transaction 1 נעל את התאים MarketingBudget עבור 3 <= AlbumId < 5, שהוא טווח הסריקה החופף ל-Transaction 3.
-- Transaction 3
SELECT MarketingBudget
FROM Albums
WHERE SingerId = 1 and AlbumId >= 3 and AlbumId < 10
FOR UPDATE;
-- Blocked by Transaction 1
קריאת אינדקס
יכול להיות שקריאה מקבילה לא תיחסם אם השאילתה שנעלה את טווח הסריקה נועלת את השורות בטבלת הבסיס, אבל העסקה המקבילה קוראת מאינדקס.
הפקודה הבאה Transaction 1 נועלת את התאים SingerId ו-SingerInfo למשך SingerId = 1.
-- Transaction 1
SELECT SingerId, SingerInfo
FROM Singers
WHERE SingerId = 1
FOR UPDATE;
הגישה לקריאה בלבד Transaction 2 לא נחסמת על ידי הנעילות שהושגו ב-Transaction 1, כי היא שולחת שאילתה לטבלת אינדקס.
-- Transaction 2
SELECT SingerId FROM Singers;
עסקאות מקבילות לא חוסמות פעולות DML על נתונים שכבר נעולים
כשטרנזקציה אחת מקבלת נעילות בטווח של תאים עם רמז נעילה בלעדי, טרנזקציות מקבילות שמנסות לבצע כתיבה בלי לקרוא קודם את הנתונים בתאים הנעולים יכולות להמשיך. העסקה נחסמת בביצוע עד שהעסקה שמחזיקה את הנעילות מתבצעת או מבוטלת.
הפקודה הבאה Transaction 1 נועלת את התאים MarketingBudget של 1 <= AlbumId < 5.
-- Transaction 1
SELECT MarketingBudget
FROM Albums
WHERE SingerId = 1 and AlbumId >= 1 and AlbumId < 5
FOR UPDATE;
אם Transaction 2 מנסה לעדכן את הטבלה Albums, הפעולה נחסמת עד ש-Transaction 1 מבצעת commit או rollback.
-- Transaction 2
UPDATE Albums
SET MarketingBudget = 200000
WHERE SingerId = 1 and AlbumId = 1;
> Query OK, 1 rows affected
COMMIT;
-- Blocked by Transaction 1
כשנועלים טווח סרוק, השורות והפערים הקיימים ננעלים
אם עסקה אחת קיבלה נעילות בלעדיות על טווח שנסרק, עסקאות מקבילות לא יכולות להוסיף נתונים בפערים בתוך הטווח הזה.
הפקודה הבאה Transaction 1 נועלת את התאים MarketingBudget של 1 <= AlbumId < 10.
-- Transaction 1
SELECT MarketingBudget
FROM Albums
WHERE SingerId = 1 and AlbumId >= 1 and AlbumId < 10
FOR UPDATE;
אם Transaction 2 מנסה להוסיף שורה בשביל AlbumId = 9 שלא קיימת עדיין, הפעולה נחסמת עד ש-Transaction 1 מבצעת commit או rollback.
-- Transaction 2
INSERT INTO Albums (SingerId, AlbumId, AlbumTitle, MarketingBudget)
VALUES (1, 9, "Hello hello!", 10000);
> Query OK, 1 rows affected
COMMIT;
-- Blocked by Transaction 1
הערות לגבי נעילת רכישה
הסמנטיקה של הנעילה שמתוארת כאן מספקת הנחיות כלליות, אבל לא מבטיחה איך בדיוק יתבצעו הנעילות כש-Spanner יבצע טרנזקציה שמשתמשת בסעיף FOR UPDATE. יכול להיות שמנגנוני האופטימיזציה של השאילתות ב-Spanner ישפיעו גם על הנעילות שמתבצעות. הסעיף מונע מעסקאות אחרות לשנות את התאים הנעולים עד שהעסקה הנוכחית תסתיים.
סמנטיקה של שאילתות
בקטע הזה מוסבר על הסמנטיקה של שאילתות כשמשתמשים ב-FOR UPDATE
clause.
שימוש בהצהרות WITH
הסעיף FOR UPDATE לא מקבל נעילות עבור ההצהרה WITH כשמציינים FOR UPDATE בשאילתה ברמה החיצונית של ההצהרה WITH.
בשאילתה הבאה, לא נרכשו נעילות על הטבלה Singers, כי הכוונה לנעול לא מועברת לשאילתת הביטויים הנפוצים של הטבלה (CTE).
WITH s AS (SELECT SingerId, SingerInfo FROM Singers WHERE SingerID > 5)
SELECT * FROM s
FOR UPDATE;
אם סעיף FOR UPDATE מצוין בשאילתת ה-CTE, הטווח שנסרק של שאילתת ה-CTE מקבל את הנעילות.
בדוגמה הבאה, התאים SingerId ו-SingerInfo בשורות שבהן SingerId > 5 נעולים.
WITH s AS
(SELECT SingerId, SingerInfo FROM Singers WHERE SingerId > 5 FOR UPDATE)
SELECT * FROM s;
שימוש בשאילתות משנה
אפשר להשתמש בסעיף FOR UPDATE בשאילתה ברמה החיצונית שיש לה שאילתה אחת או יותר. הנעילות מתבצעות על ידי השאילתה ברמה העליונה ובתוך שאילתות משנה, למעט שאילתות משנה של ביטויים.
השאילתה הבאה נועלת את התאים SingerId ו-SingerInfo בשורות שבהן
SingerId > 5.
(SELECT SingerId, SingerInfo FROM Singers WHERE SingerId > 5) AS t
FOR UPDATE;
השאילתה הבאה לא נועלת תאים בטבלה Albums כי היא נמצאת בתוך שאילתת משנה של ביטוי. התאים SingerId ו-SingerInfo בשורות שמוחזרות על ידי שאילתת המשנה של הביטוי נעולים.
SELECT SingerId, SingerInfo
FROM Singers
WHERE SingerId = (SELECT SingerId FROM Albums WHERE MarketingBudget > 100000)
FOR UPDATE;
שימוש בשאילתות לתצוגות
אפשר להשתמש בסעיף FOR UPDATE כדי לשלוח שאילתה לתצוגה, כמו בדוגמה הבאה:
CREATE VIEW SingerBio AS SELECT SingerId, FullName, SingerInfo FROM Singers;
SELECT * FROM SingerBio WHERE SingerId = 5 FOR UPDATE;
אי אפשר להשתמש בפסקה FOR UPDATE כשמגדירים תצוגה.
תרחישי שימוש שלא נתמכים
אין תמיכה בתרחישי השימוש הבאים של FOR UPDATE:
- כמנגנון הדדי לביצוע קוד מחוץ ל-Spanner: אל תשתמשו בנעילה ב-Spanner כדי להבטיח גישה בלעדית למשאב מחוץ ל-Spanner. יכול להיות ש-Spanner יבטל עסקאות. לדוגמה, אם מתבצע ניסיון חוזר לעסקה, באופן מפורש על ידי קוד האפליקציה או באופן מרומז על ידי קוד הלקוח, כמו מנהל ההתקנים של Spanner JDBC, מובטח שהנעילות יוחזקו רק במהלך הניסיון שאושר.
- בשילוב עם הרמז
LOCK_SCANNED_RANGES: אי אפשר להשתמש גם בסעיףFOR UPDATEוגם ברמזLOCK_SCANNED_RANGESבאותה שאילתה, אחרת Spanner מחזירה שגיאה. - בשאילתות של חיפוש טקסט מלא: אי אפשר להשתמש בפסקה
FOR UPDATEבשאילתות שמשתמשות באינדקסים של חיפוש טקסט מלא. - בעסקאות לקריאה בלבד: סעיף
FOR UPDATEתקף רק בשאילתות שמופעלות בעסקאות לקריאה ולכתיבה. - בתוך הצהרות DDL: אי אפשר להשתמש בסעיף
FOR UPDATEבשאילתות בתוך הצהרות DDL, שמאוחסנות לביצוע מאוחר יותר. לדוגמה, אי אפשר להשתמש בסעיףFOR UPDATEכשמגדירים תצוגה. אם נדרשת נעילה, אפשר לציין את סעיףFOR UPDATEכשמבצעים שאילתה לתצוגה.
המאמרים הבאים
- GoogleSQL
PostgreSQL
FOR UPDATE - מידע נוסף על ההצעה
LOCK_SCANNED_RANGES - מידע על נעילה ב-Spanner
- מידע על סריאליזציה ב-Spanner