במאמר הזה מוסבר איך להשתמש בטבלאות המובנות של נתוני הנעילה ב-Spanner כדי לזהות את מפתחות השורות ואת עמודות הטבלה שגרמו להתנגשויות של נעילת טרנזקציות במסד הנתונים במהלך פרק זמן מסוים. אפשר לאחזר נתונים סטטיסטיים מהטבלאות האלה באמצעות הצהרות SQL.SPANNER_SYS.LOCK_STATS*
אם הטרנזקציות שלכם משתמשות בבקרת בו-זמניות אופטימית, הנתונים הסטטיסטיים של הנעילות לא זמינים כי הנעילות לא מתבצעות במהלך ההרצה של הטרנזקציה.
סטטיסטיקות של נעילת הגישה
Spanner מספק את נתוני הנעילה בסכימה SPANNER_SYS.
אפשר לגשת לנתוני SPANNER_SYS בדרכים הבאות:
דף Spanner Studio של מסד נתונים במסוף Google Cloud
מרכז הבקרה Lock insights.
השיטה
executeSqlאו השיטהexecuteStreamingSql.ה-methods הבאות לקריאה יחידה ש-Spanner מספק לא תומכות ב-
SPANNER_SYS:- ביצוע קריאה חזקה משורה אחת או מכמה שורות בטבלה.
- ביצוע קריאה לא עדכנית משורה אחת או מכמה שורות בטבלה.
- קריאה משורה אחת או מכמה שורות באינדקס משני.
נעילת נתונים סטטיסטיים לפי מפתח שורה
בטבלאות הבאות אפשר לעקוב אחרי מפתח השורה עם זמן ההמתנה הכי ארוך:
SPANNER_SYS.LOCK_STATS_TOP_MINUTE: מפתחות שורה עם זמני ההמתנה הגבוהים ביותר לנעילה במרווחי זמן של דקה אחת.
SPANNER_SYS.LOCK_STATS_TOP_10MINUTE: מפתחות שורה עם זמני המתנה לנעילה הכי ארוכים במרווחי זמן של 10 דקות.
SPANNER_SYS.LOCK_STATS_TOP_HOUR: מפתחות שורות עם זמני ההמתנה הגבוהים ביותר לנעילה במרווחי זמן של שעה
הטבלאות האלה כוללות את המאפיינים הבאים:
כל טבלה מכילה נתונים של מרווחי זמן לא חופפים באורך שצוין בשם הטבלה.
המרווחים מבוססים על שעות. מרווחי זמן של דקה אחת מסתיימים בתחילת הדקה, מרווחי זמן של 10 דקות מסתיימים כל 10 דקות החל מתחילת השעה, ומרווחי זמן של שעה אחת מסתיימים בתחילת השעה. אחרי כל מרווח זמן, מערכת Spanner אוספת נתונים מכל השרתים, ואז הנתונים האלה זמינים בטבלאות SPANNER_SYS זמן קצר לאחר מכן.
לדוגמה, בשעה 11:59:30, המרווחים האחרונים שזמינים לשאילתות SQL הם:
- דקה אחת: 11:58:00 עד 11:58:59
- 10 דקות: 11:40:00–11:49:59
- שעה אחת: 10:00:00–10:59:59 AM
מערכת Spanner מקבצת את הנתונים הסטטיסטיים לפי טווח מפתחות שורות התחלתי.
כל שורה מכילה נתונים סטטיסטיים לגבי זמן ההמתנה הכולל לנעילה של טווח מסוים של מפתח שורה התחלתי, ש-Spanner אוסף לגביו נתונים סטטיסטיים במהלך המרווח שצוין.
אם Spanner לא מצליח לאחסן מידע על כל טווח של מפתחות שורות להמתנה לנעילה במהלך המרווח, המערכת מתעדפת את טווח מפתחות השורות עם זמן ההמתנה לנעילה הגבוה ביותר במהלך המרווח שצוין.
כל העמודות בטבלאות הן nullable.
סכמת טבלאות
| שם עמודה | סוג | תיאור |
|---|---|---|
INTERVAL_END |
TIMESTAMP |
סוף מרווח הזמן שבו התרחשו ההתנגשויות של הנעילה שנכללו. |
ROW_RANGE_START_KEY |
BYTES(MAX) |
מפתח השורה שבה התרחשה התנגשות הנעילה. אם הקונפליקט כולל טווח של שורות, הערך הזה מייצג את מפתח ההתחלה של הטווח הזה. סימן פלוס, +, מציין טווח.
מידע נוסף זמין במאמר מהו מפתח התחלה של טווח שורות.
|
LOCK_WAIT_SECONDS |
FLOAT64 |
משך ההמתנה המצטבר לנעילה של קונפליקטים של נעילה שנרשמו עבור כל העמודות בטווח מפתחות השורה, בשניות. |
SAMPLE_LOCK_REQUESTS |
ARRAY<STRUCT<
|
כל רשומה במערך הזה תואמת לבקשת נעילה לדוגמה שתרמה להתנגשות הנעילה, על ידי המתנה לנעילה או חסימת טרנזקציות אחרות מלקבל את הנעילה, במפתח השורה (הטווח) הנתון. מספר הדגימות המקסימלי במערך הזה הוא 20.
כל דוגמה מכילה את שלושת השדות הבאים:
|
SAMPLE_LOCK_REQUESTS_JSON_STRING |
STRING |
ייצוג מחרוזת תואמת ל-JSON של העמודה SAMPLE_LOCK_REQUESTS. מחרוזת ה-JSON היא אובייקט JSON עם אותו מבנה כמו STRUCT שמוגדר בעמודה SAMPLE_LOCK_REQUESTS.
העמודה הזו נתמכת במסדי נתונים של GoogleSQL-dialect ו-PostgreSQL-dialect. |
מצבי נעילה
פעולות ב-Spanner מקבלות נעילות כשהן חלק מעסקת קריאה-כתיבה. עסקאות עם הרשאת קריאה בלבד לא מקבלות נעילות. ב-Spanner נעשה שימוש במצבי נעילה שונים כדי למקסם את מספר העסקאות שיש להן גישה לתא נתונים מסוים בזמן נתון. למנעולים שונים יש מאפיינים שונים. לדוגמה, יש נעילות שאפשר לשתף בין כמה עסקאות, ויש כאלה שלא.
התנגשות נעילה יכולה להתרחש כשמנסים להשיג אחד ממצבי הנעילה הבאים בטרנזקציה.
ReaderSharedנעילה – נעילה שמאפשרת לגישות קריאה אחרות לגשת לנתונים עד שהעסקה מוכנה להתחייבות. הנעילה המשותפת הזו מתבצעת כשעסקת קריאה-כתיבה קוראת נתונים.WriterSharedנעילה – הנעילה הזו מתבצעת כשטרנזקציית קריאה-כתיבה מנסה לבצע כתיבה.Exclusiveנעילה – נעילה בלעדית מתבצעת כשטרנזקציה של קריאה וכתיבה, שכבר קיבלה נעילה מסוג ReaderShared, מנסה לכתוב נתונים אחרי השלמת הקריאה. נעילה בלעדית היא שדרוג מנעילתReaderShared. נעילה בלעדית היא מקרה מיוחד של טרנזקציה שבה מוחזקות בו-זמנית גם נעילתReaderSharedוגם נעילתWriterShared. אף טרנזקציה אחרת לא יכולה לקבל נעילה באותו תא.WriterSharedTimestampנעילה – סוג מיוחד שלWriterSharedנעילה שמתקבלת כשמוסיפים שורות חדשות לטבלה שיש בה חותמת זמן של ביצוע פעולת commit כחלק מהמפתח הראשי. סוג הנעילה הזה מונע ממשתתפים בעסקה ליצור בדיוק את אותה שורה, ולכן מונע מהם להתנגש זה עם זה. מערכת Spanner מעדכנת את המפתח של השורה שנוספה כך שיתאים לחותמת הזמן של האישור של הטרנזקציה שביצעה את ההוספה.
מידע נוסף על סוגי עסקאות ועל סוגי הנעילות שזמינים מופיע במאמר עסקאות.
בעיות שקשורות למצב נעילה
בטבלה הבאה מוצגים העימותים האפשריים בין מצבי נעילה שונים.
| מצבי נעילה | ReaderShared |
WriterShared |
Exclusive |
WriterSharedTimestamp |
|---|---|---|---|---|
ReaderShared |
לא | כן | כן | כן |
WriterShared |
כן | לא | כן | לא רלוונטי |
Exclusive |
כן | כן | כן | לא רלוונטי |
WriterSharedTimestamp |
כן | לא רלוונטי | לא רלוונטי | כן |
נעילות WriterSharedTimestamp משמשות רק כשמוסיפים שורות חדשות עם חותמת זמן כחלק מהמפתח הראשי. הנעילות WriterShared ו-Exclusive משמשות לכתיבה לתאים קיימים או להוספת שורות חדשות בלי חותמות זמן. כתוצאה מכך, WriterSharedTimestamp לא יכול להתנגש עם סוגים אחרים של נעילות, והתרחישים האלה מוצגים כלא רלוונטי בטבלה הקודמת.
החריג היחיד הוא ReaderShared, שאפשר להחיל על שורות שלא קיימות, ולכן הוא עלול להתנגש עם WriterSharedTimestamp. לדוגמה, סריקה מלאה של טבלה נועלת את כל הטבלה, גם שורות שלא נוצרו, ולכן יכול להיות ש-ReaderShared יתנגש עם WriterSharedTimestamp.
מהו מפתח התחלה של טווח שורות?
העמודה ROW_RANGE_START_KEY מזהה את המפתח הראשי המורכב, או את המפתח הראשי ההתחלתי של טווח שורות, שיש לו התנגשויות נעילה. הסכימה הבאה משמשת להמחשת דוגמה.
CREATE TABLE Singers (
SingerId INT64 NOT NULL,
FirstName STRING(1024),
LastName STRING(1024),
SingerInfo BYTES(MAX),
) PRIMARY KEY (SingerId);
CREATE TABLE Albums (
SingerId INT64 NOT NULL,
AlbumId INT64 NOT NULL,
AlbumTitle STRING(MAX),
) PRIMARY KEY (SingerId, AlbumId),
INTERLEAVE IN PARENT Singers ON DELETE CASCADE;
CREATE TABLE Songs (
SingerId INT64 NOT NULL,
AlbumId INT64 NOT NULL,
TrackId INT64 NOT NULL,
SongName STRING(MAX),
) PRIMARY KEY (SingerId, AlbumId, TrackId),
INTERLEAVE IN PARENT Albums ON DELETE CASCADE;
CREATE TABLE Users (
UserId INT64 NOT NULL,
LastAccess TIMESTAMP NOT NULL OPTIONS (allow_commit_timestamp=true),
...
) PRIMARY KEY (UserId, LastAccess);
כפי שאפשר לראות בטבלה הבאה של מפתחות שורות וטווחים של מפתחות שורות, טווח מיוצג באמצעות סימן פלוס, '+', במפתח. במקרים כאלה, המפתח מייצג את מפתח ההתחלה של טווח מפתחות שבו התרחש עימות נעילה.
| ROW_RANGE_START_KEY | הסבר |
|---|---|
| זמרים(2) | טבלת Singers במפתח SingerId=2 |
| albums(2,1) | טבלת אלבומים במפתח SingerId=2,AlbumId=1 |
| songs(2,1,5) | טבלת השירים במפתח SingerId=2,AlbumId=1,TrackId=5 |
| שירים(2,1,5+) | טווח המפתחות בטבלת השירים מתחיל ב-SingerId=2, AlbumId=1, TrackId=5 |
| אלבומים(2 ומעלה) | Albums table key range starting at SingerId=2,AlbumId=1 |
| users(3, 2020-11-01 12:34:56.426426+00:00) | טבלת המשתמשים במפתח UserId=3, LastAccess=commit_timestamp |
נתונים סטטיסטיים מצטברים
SPANNER_SYS כולל גם טבלאות לאחסון נתונים מצטברים של נתונים סטטיסטיים לגבי נעילות שנאספו על ידי Spanner בתקופה מסוימת:
SPANNER_SYS.LOCK_STATS_TOTAL_MINUTE: נתונים סטטיסטיים מצטברים של כל ההמתנות לנעילה במרווחי זמן של דקה אחת.
SPANNER_SYS.LOCK_STATS_TOTAL_10MINUTE: נתונים סטטיסטיים מצטברים של כל ההמתנות לנעילה במרווחי זמן של 10 דקות.
SPANNER_SYS.LOCK_STATS_TOTAL_HOUR: נתונים סטטיסטיים מצטברים לכל ההמתנות לנעילה במרווחי זמן של שעה.
לטבלאות של נתונים סטטיסטיים מצטברים יש את המאפיינים הבאים:
כל טבלה מכילה נתונים של מרווחי זמן לא חופפים באורך שצוין בשם הטבלה.
המרווחים מבוססים על שעות. מרווחי זמן של דקה אחת מסתיימים בתחילת הדקה, מרווחי זמן של 10 דקות מסתיימים כל 10 דקות החל מתחילת השעה, ומרווחי זמן של שעה אחת מסתיימים בתחילת השעה.
לדוגמה, בשעה 11:59:30, המרווחים האחרונים שזמינים לשאילתות SQL לגבי נתוני נעילה מצטברים הם:
- דקה אחת: 11:58:00 עד 11:58:59
- 10 דקות: 11:40:00–11:49:59
- שעה אחת: 10:00:00–10:59:59 AM
כל שורה מכילה נתונים סטטיסטיים של כל ההמתנות לנעילה במסד הנתונים במהלך המרווח שצוין, כשהם מצטברים יחד. לכל מרווח זמן יש רק שורה אחת.
הנתונים הסטטיסטיים שנאספים בטבלאות
SPANNER_SYS.LOCK_STATS_TOTAL_*כוללים המתנה לנעילה שלא נאספה בטבלאותSPANNER_SYS.LOCK_STATS_TOP_*ב-Spanner.חלק מהעמודות בטבלאות האלה מוצגות כמדדים ב-Cloud Monitoring. אלה המדדים שמוצגים:
- זמן ההמתנה לנעילה
מידע נוסף מופיע במאמר בנושא מדדים של Spanner.
סכמת טבלאות
| שם עמודה | סוג | תיאור |
|---|---|---|
INTERVAL_END |
TIMESTAMP |
סיום מרווח הזמן שבו התרחש העימות על הנעילה. |
TOTAL_LOCK_WAIT_SECONDS |
FLOAT64 |
הזמן הכולל של ההמתנה לנעילה בגלל התנגשויות נעילה שנרשמו עבור כל מסד הנתונים, בשניות. |
שאילתות לדוגמה
הדוגמה הבאה היא של הצהרת SQL שאפשר להשתמש בה כדי לאחזר נתונים סטטיסטיים של נעילה: אפשר להריץ את הצהרות ה-SQL האלה באמצעות ספריות הלקוח, gcloud spanner או Google Cloud המסוף.
הצגת נתונים סטטיסטיים לגבי הנעילה של המרווח הקודם של דקה אחת
השאילתה הבאה מחזירה את פרטי ההמתנה לנעילה של כל מפתח שורה עם התנגשות נעילה, כולל החלק היחסי מתוך סך התנגשויות הנעילה, במהלך מרווח הזמן האחרון של דקה אחת.
הפונקציה CAST() ממירה את השדה row_range_start_key [BYTES]
למחרוזת.
SELECT CAST(s.row_range_start_key AS STRING) AS row_range_start_key,
t.total_lock_wait_seconds,
s.lock_wait_seconds,
s.lock_wait_seconds/t.total_lock_wait_seconds frac_of_total,
s.sample_lock_requests
FROM spanner_sys.lock_stats_total_minute t, spanner_sys.lock_stats_top_minute s
WHERE t.interval_end =
(SELECT MAX(interval_end)
FROM spanner_sys.lock_stats_total_minute)
AND s.interval_end = t.interval_end
ORDER BY s.lock_wait_seconds DESC;
פלט השאילתה
| row_range_start_key | total_lock_wait_seconds | lock_wait_seconds | frac_of_total | sample_lock_requests |
|---|---|---|---|---|
| שירים(2,1,1) | 2.37 | 1.76 | 0.7426 | LOCK_MODE: ReaderShared עמודה: Singers.SingerInfo LOCK_MODE: WriterShared עמודה: Singers.SingerInfo |
| משתמשים(3, 2020-11-01 12:34:56.426426+00:00) | 2.37 | 0.61 | 0.2573 | LOCK_MODE: ReaderShared עמודה: users._exists1 LOCK_MODE: WriterShared עמודה: users._exists1 |
1 _exists הוא שדה פנימי שמשמש לבדיקה אם שורה מסוימת קיימת או לא.
שמירת נתונים
לפחות, Spanner שומר נתונים לכל טבלה למשך תקופות הזמן הבאות:
SPANNER_SYS.LOCK_STATS_TOP_MINUTEו-SPANNER_SYS.LOCK_STATS_TOTAL_MINUTE: מרווחי זמן שמתייחסים ל-6 השעות האחרונות.
SPANNER_SYS.LOCK_STATS_TOP_10MINUTEו-SPANNER_SYS.LOCK_STATS_TOTAL_10MINUTE: מרווחי זמן שכוללים את 4 הימים הקודמים.
SPANNER_SYS.LOCK_STATS_TOP_HOURו-SPANNER_SYS.LOCK_STATS_TOTAL_HOUR: מרווחי זמן שכוללים את 30 הימים האחרונים.
פתרון בעיות של התנגשויות נעילה במסד הנתונים באמצעות נתונים סטטיסטיים של נעילות
אתם יכולים להשתמש ב-SQL או בלוח הבקרה תובנות לגבי נעילות כדי לראות התנגשויות נעילה במסד הנתונים.
בנושאים הבאים מוסבר איך אפשר לחקור את העימותים האלה של נעילות באמצעות קוד SQL.
בחירת תקופת זמן לבדיקה
אתם בודקים את מדדי זמן האחזור של מסד הנתונים שלכם ב-Spanner ומגלים תקופה שבה האפליקציה חווה זמן אחזור גבוה ושימוש גבוה במעבד. לדוגמה, הבעיה התחילה ב-12 בנובמבר 2020 בסביבות השעה 22:50.
בודקים אם זמן האחזור של אישור העסקה גדל יחד עם זמן ההמתנה לנעילה במהלך התקופה שנבחרה
העסקאות מקבלות נעילות, ולכן אם עימותי נעילות גורמים לזמני המתנה ארוכים, אמורה להיות אפשרות לראות את העלייה בחביון של אישור העסקה יחד עם העלייה בזמן ההמתנה של הנעילה.
אחרי שבחרנו תקופת זמן להתחיל בה את הבדיקה, נצרף את נתוני העסקאות TXN_STATS_TOTAL_10MINUTE עם נתוני הנעילה LOCK_STATS_TOTAL_10MINUTE מאותה תקופה כדי להבין אם העלייה בחביון הממוצע של השלמת הפעולות נובעת מהעלייה בזמן ההמתנה לנעילה.
SELECT t.interval_end, t.avg_commit_latency_seconds, l.total_lock_wait_seconds
FROM spanner_sys.txn_stats_total_10minute t
LEFT JOIN spanner_sys.lock_stats_total_10minute l
ON t.interval_end = l.interval_end
WHERE
t.interval_end >= "2020-11-12T21:50:00Z"
AND t.interval_end <= "2020-11-12T23:50:00Z"
ORDER BY interval_end;
הנתונים הבאים הם דוגמה לתוצאות שמתקבלות מהשאילתה שלנו.
| interval_end | avg_commit_latency_seconds | total_lock_wait_seconds |
|---|---|---|
| 2020-11-12 21:40:00-07:00 | 0.002 | 0.090 |
| 2020-11-12 21:50:00-07:00 | 0.003 | 0.110 |
| 2020-11-12 22:00:00-07:00 | 0.002 | 0.100 |
| 2020-11-12 22:10:00-07:00 | 0.002 | 0.080 |
| 2020-11-12 22:20:00-07:00 | 0.030 | 0.240 |
| 2020-11-12 22:30:00-07:00 | 0.034 | 0.220 |
| 2020-11-12 22:40:00-07:00 | 0.034 | 0.218 |
| 2020-11-12 22:50:00-07:00 | 3.741 | 780.193 |
| 2020-11-12 23:00:00-07:00 | 0.042 | 0.240 |
| 2020-11-12 23:10:00-07:00 | 0.038 | 0.129 |
| 2020-11-12 23:20:00-07:00 | 0.021 | 0.128 |
| 2020-11-12 23:30:00-07:00 | 0.038 | 0.231 |
התוצאות הקודמות מראות עלייה דרמטית בavg_commit_latency_seconds ובtotal_lock_wait_seconds באותו פרק זמן מ2020-11-12 22:40:00 עד 2020-11-12 22:50:00, ולאחר מכן ירידה. חשוב לציין שהערך avg_commit_latency_seconds הוא משך הזמן הממוצע שבוצע רק בשלב השליחה. לעומת זאת, total_lock_wait_seconds הוא זמן הנעילה המצטבר לתקופה, ולכן הזמן נראה ארוך בהרבה מזמן אישור העסקה.
עכשיו, אחרי שאישרנו שזמן ההמתנה לנעילה קשור קשר הדוק לעלייה בחביון הכתיבה, נבדוק בשלב הבא אילו שורות ועמודות גורמות להמתנה הארוכה.
אפשר לראות אילו מפתחות שורות ועמודות היו עם זמני המתנה ארוכים לנעילה במהלך התקופה שנבחרה
כדי לגלות אילו מפתחות שורה ועמודות חוו זמני המתנה ארוכים לנעילה במהלך התקופה שאנחנו בודקים, אנחנו שולחים שאילתה לטבלה LOCK_STAT_TOP_10MINUTE, שמפרטת את מפתחות השורה והעמודות שתרמו הכי הרבה לזמן ההמתנה לנעילה.
הפונקציה CAST() בשאילתה הבאה ממירה את השדה row_range_start_key BYTES למחרוזת.
SELECT CAST(s.row_range_start_key AS STRING) AS row_range_start_key,
t.total_lock_wait_seconds,
s.lock_wait_seconds,
s.lock_wait_seconds/t.total_lock_wait_seconds frac_of_total,
s.sample_lock_requests
FROM spanner_sys.lock_stats_total_10minute t, spanner_sys.lock_stats_top_10minute s
WHERE
t.interval_end = "2020-11-12T22:50:00Z" and s.interval_end = t.interval_end;
| row_range_start_key | total_lock_wait_seconds | lock_wait_seconds | frac_of_total | sample_lock_requests |
|---|---|---|---|---|
| זמרים(32) | 780.193 | 780.193 | 1 | LOCK_MODE: WriterShared עמודה: Singers.SingerInfo LOCK_MODE: ReaderShared עמודה: Singers.SingerInfo |
מטבלת התוצאות הזו אפשר לראות שההתנגשות התרחשה בטבלה Singersבמפתח SingerId=32. Singers.SingerInfo היא העמודה שבה התרחש העימות של הנעילה בין ReaderShared לבין WriterShared.
זה סוג נפוץ של התנגשות שמתרחש כשטרנזקציה אחת מנסה לקרוא תא מסוים וטרנזקציה אחרת מנסה לכתוב לאותו תא. עכשיו אנחנו יודעים מה התא המדויק של הנתונים שעליו מתחרות הטרנזקציות על הנעילה, ולכן בשלב הבא נזהה את הטרנזקציות שמתחרות על הנעילות.
איך אפשר לדעת לאילו עסקאות יש גישה לעמודות שמעורבות בקונפליקט הנעילה
כדי לזהות את העסקאות שבהן יש השהיה משמעותית של ביצוע פעולות (commit) בפרק זמן מסוים בגלל התנגשויות נעילה, צריך להריץ שאילתה על העמודות הבאות מהטבלה SPANNER_SYS.TXN_STATS_TOTAL_10MINUTE:
fprintread_columnswrite_constructive_columnsavg_commit_latency_seconds
צריך לסנן את העמודות הנעולות שמופיעות בטבלה SPANNER_SYS.LOCK_STATS_TOP_10MINUTE:
עסקאות שקוראות עמודה כלשהי שגרמה להתנגשות נעילה כשניסו לקבל את נעילת
ReaderShared.עסקאות שכותבות לכל עמודה שגרמה להתנגשות נעילה כשניסו להשיג נעילה מסוג
WriterShared.
SELECT
fprint,
read_columns,
write_constructive_columns,
avg_commit_latency_seconds
FROM spanner_sys.txn_stats_top_10minute t2
WHERE (
EXISTS (
SELECT * FROM t2.read_columns columns WHERE columns IN (
SELECT DISTINCT(req.COLUMN)
FROM spanner_sys.lock_stats_top_10minute t, t.SAMPLE_LOCK_REQUESTS req
WHERE req.LOCK_MODE = "ReaderShared" AND t.interval_end ="2020-11-12T23:50:00Z"))
OR
EXISTS (
SELECT * FROM t2.write_constructive_columns columns WHERE columns IN (
SELECT DISTINCT(req.COLUMN)
FROM spanner_sys.lock_stats_top_10minute t, t.SAMPLE_LOCK_REQUESTS req
WHERE req.LOCK_MODE = "WriterShared" AND t.interval_end ="2020-11-12T23:50:00Z"))
)
AND t2.interval_end ="2020-11-12T23:50:00Z"
ORDER BY avg_commit_latency_seconds DESC;
תוצאת השאילתה ממוינת לפי העמודה avg_commit_latency_seconds, כך שהעסקה עם זמן האחזור הכי גבוה של השלמת הפעולה מוצגת ראשונה.
| fprint | read_columns | write_constructive_columns | avg_commit_latency_seconds |
|---|---|---|---|
| 1866043996151916800 |
['Singers.SingerInfo', 'Singers.FirstName', 'Singers.LastName', 'Singers._exists'] |
['Singers.SingerInfo'] | 4.89 |
| 4168578515815911936 | [] | ['Singers.SingerInfo'] | 3.65 |
תוצאות השאילתה מראות ששתי טרנזקציות ניסו לגשת לעמודה Singers.SingerInfo, שהיא העמודה שהיו בה התנגשויות נעילה במהלך התקופה. אחרי שמזהים את העסקאות שגורמות להתנגשויות הנעילה, אפשר לנתח את העסקאות באמצעות טביעת האצבע שלהן, fprint, כדי לזהות בעיות פוטנציאליות שתרמו להתנגשות הנעילה.
אחרי שבודקים את העסקה עם fprint=1866043996151916800, אפשר להשתמש בעמודות read_columns ו-write_constructive_columns כדי לזהות איזה חלק מקוד האפליקציה הפעיל את העסקה. אחר כך תוכלו לראות את ה-DML הבסיסי שלא מסנן לפי המפתח הראשי, SingerId. הפעולה הזו גרמה לסריקה מלאה של הטבלה ולנעילת הטבלה עד שהטרנזקציה אושרה.
כדי לפתור את הבעיה של נעילת הקובץ, אפשר:
- כדי לזהות את הערכים הנדרשים של
SingerId, אפשר להשתמש בעסקה לקריאה בלבד. - משתמשים בעסקת קריאה-כתיבה נפרדת כדי לעדכן את השורות של הערכים הנדרשים
SingerId.
שימוש בשיטות מומלצות לצמצום התחרות על נעילה
בתרחיש לדוגמה שלנו, השתמשנו בנתונים סטטיסטיים של נעילות ושל עסקאות כדי לצמצם את הבעיה לעסקה שלא השתמשה במפתח הראשי של הטבלה שלנו כשביצעה עדכונים. הצענו רעיונות לשיפור העסקה, בהתאם לכך שידענו מראש את המפתחות של השורות שרצינו לעדכן או שלא ידענו.
כשבודקים בעיות פוטנציאליות בפתרון, או אפילו כשמתכננים את הפתרון, כדאי לפעול לפי השיטות המומלצות האלה כדי להפחית את מספר ההתנגשויות של נעילות במסד הנתונים.
מומלץ להשתמש בעסקאות לקריאה בלבד כשאפשר, כי הן לא רוכשות נעילות.
מומלץ להימנע מסריקות מלאות של טבלאות בעסקאות קריאה-כתיבה. זה כולל כתיבת DML על תנאי במפתח הראשי או הקצאת טווח מפתחות ספציפי כשמשתמשים ב-Read API.
כדי שהתקופה שבה הנתונים נעולים תהיה קצרה, צריך לבצע את השינוי בעסקה לקריאה ולכתיבה כמה שיותר מהר אחרי שקוראים את הנתונים. טרנזקציה מסוג Read-write מבטיחה שהנתונים יישארו ללא שינוי אחרי שקוראים את הנתונים, עד שהשינוי יבוצע בהצלחה. כדי לעשות את זה, צריך לנעול את תאי הנתונים במהלך הקריאה ובמהלך השמירה של הנתונים. לכן, אם אפשר לקצר את תקופת הנעילה, הסיכוי שיהיו התנגשויות נעילה בעסקאות קטן יותר.
עדיף להשתמש בעסקאות קטנות במקום בעסקאות גדולות, או לשקול להשתמש ב-Partitioned DML לעסקאות DML ארוכות. טרנזקציה שפועלת במשך זמן רב מקבלת נעילה למשך זמן רב, ולכן מומלץ לפצל טרנזקציה שמשפיעה על אלפי שורות לכמה טרנזקציות קטנות יותר שמעדכנות מאות שורות, בכל הזדמנות שאפשר.
אם לא צריך את ההבטחה שניתנת על ידי טרנזקציית קריאה-כתיבה, כדאי להימנע מקריאת נתונים בטרנזקציית קריאה-כתיבה לפני ביצוע השינוי, למשל על ידי קריאת הנתונים בטרנזקציית קריאה בלבד נפרדת. רוב העימותים של נעילות מתרחשים בגלל ההבטחה החזקה, כדי להבטיח שהנתונים יישארו ללא שינוי בין הקריאה לבין השמירה. לכן, אם העסקה לקריאה ולכתיבה לא קוראת נתונים, היא לא צריכה לנעול את התאים למשך זמן רב.
צריך לציין רק את קבוצת העמודות המינימלית שנדרשת בעסקת קריאה-כתיבה. הנעילות ב-Spanner הן ברמת תא הנתונים. לכן, כשטרנזקציית קריאה-כתיבה קוראת יותר מדי עמודות, היא מקבלת
ReaderSharedנעילה על התאים האלה. זה עלול לגרום להתנגשויות נעילה כשטרנזקציות אחרות מקבלות נעילתWriterSharedעל פעולות כתיבה בעמודות העודפות. לדוגמה, במקום*בקריאה, אפשר לציין קבוצה של עמודות.צמצום הקריאות ל-API בעסקת קריאה-כתיבה. ההשהיה של קריאות ל-API עלולה לגרום למאבק על נעילה ב-Spanner, כי קריאות ל-API כפופות לעיכובים ברשת וגם לעיכובים בצד השרת. מומלץ לבצע קריאות ל-API מחוץ לעסקאות קריאה-כתיבה, כשהדבר אפשרי. אם אתם חייבים להפעיל קריאות ל-API בתוך טרנזקציה של קריאה וכתיבה, הקפידו לעקוב אחרי זמן האחזור של הקריאות ל-API כדי לצמצם את ההשפעה על תקופת ההמתנה לקבלת נעילה.
עובדים בהתאם לשיטות המומלצות לעיצוב סכימה.
אם האפשרות של הטיה בכתיבה לא רלוונטית לאפליקציה שלכם, כדאי להשתמש בבידוד של קריאה חוזרת עם ברירת המחדל האופטימית של מקביליות כדי למנוע התנגשויות של קריאה-כתיבה. מידע נוסף זמין במאמר בנושא בידוד של קריאה חוזרת.
עבור עומסי עבודה עם מחלוקת נמוכה על קריאה וכתיבה, כדאי להשתמש בבידוד ניתן לסדר עם בקרת בו-זמניות אופטימית כדי להימנע מנעילת טרנזקציות עד לזמן השמירה, תוך הקפדה על כך שכל הקריאות של הטרנזקציה יהיו תקפות בזמן השמירה. מידע נוסף על האפשרות הזו מופיע במאמר שליטה בו-זמנית אופטימית.
המאמרים הבאים
- מידע נוסף על כלים אחרים לבדיקת נתונים
- מידע נוסף על נתונים אחרים ש-Spanner שומר לגבי כל מסד נתונים זמין בטבלה information_schema של מסדי הנתונים.
- מידע נוסף על שיטות מומלצות ל-SQL ב-Spanner