PostgreSQL Antipatterns: נלחמים בהמוני "מתים"

המוזרויות של המנגנונים הפנימיים של PostgreSQL מאפשרים לו להיות מהיר מאוד במצבים מסוימים ו"לא מאוד מהיר" באחרים. היום נתמקד בדוגמה קלאסית של התנגשות בין איך עובד DBMS לבין מה שהמפתח עושה איתו - עקרונות עדכון לעומת MVCC.

סיפור קצר מ מאמר נהדר:

כאשר שורה משתנה על ידי פקודת UPDATE, מתבצעות למעשה שתי פעולות: DELETE ו-INSERT. IN הגרסה הנוכחית של המחרוזת xmax מוגדר שווה למספר העסקה שביצעה את העדכון. ואז זה נוצר גרסה חדשה אותה שורה; ערך ה-xmin שלו עולה בקנה אחד עם ערך ה-xmax של הגרסה הקודמת.

זמן מה לאחר השלמת העסקה הזו, הגרסה הישנה או החדשה, בהתאם COMMIT/ROOLBACK, יוכר "מתים" (טופלים מתים) כאשר עוברים VACUUM לפי הטבלה ומנוקה.

PostgreSQL Antipatterns: נלחמים בהמוני "מתים"

אבל זה לא יקרה מיד, אבל בעיות עם "מתים" ניתן לרכוש מהר מאוד - עם או חוזרים עדכון המוני של רשומות בשולחן גדול, וקצת אחר כך תתקלו באותו מצב ואקום לא יוכל לעזור.

מס' 1: אני אוהב להזיז את זה

נניח שהשיטה שלך עובדת על לוגיקה עסקית, ופתאום היא מבינה שיהיה צורך לעדכן את שדה X ברשומה כלשהי:

UPDATE tbl SET X = <newX> WHERE pk = $1;

לאחר מכן, ככל שהביצוע מתקדם, מסתבר שיש לעדכן גם את שדה Y:

UPDATE tbl SET Y = <newY> WHERE pk = $1;

... ואז גם Z - למה לבזבז זמן על זוטות?

UPDATE tbl SET Z = <newZ> WHERE pk = $1;

כמה גרסאות של רשומה זו יש לנו כעת במסד הנתונים? כן, 4 חלקים! מתוכם, אחד רלוונטי, ו-3 יצטרכו לנקות אחריך על ידי [אוטו] ואקום.

אל תעשה את זה ככה! להשתמש עדכון כל השדות בבקשה אחת - כמעט תמיד ניתן לשנות את ההיגיון של השיטה כך:

UPDATE tbl SET X = <newX>, Y = <newY>, Z = <newZ> WHERE pk = $1;

מס' 2: השימוש נבדל מלוק!

אז, עדיין רצית עדכן הרבה מאוד רשומות בטבלה (במהלך השימוש בסקריפט או ממיר, למשל). ומשהו כזה נכנס לתסריט:

UPDATE tbl SET X = <newX> WHERE pk BETWEEN $1 AND $2;

בקשה בערך זה מתרחשת לעתים קרובות למדי וכמעט תמיד לא למלא שדה חדש ריק, אלא לתקן כמה שגיאות בנתונים. במקביל, היא עצמה נכונות הנתונים הקיימים אינה נלקחת בחשבון כלל - אך לשווא! כלומר, הרשומה נכתבת מחדש, גם אם היא מכילה בדיוק את מה שרצוי – אבל למה? בוא נתקן את זה:

UPDATE tbl SET X = <newX> WHERE pk BETWEEN $1 AND $2 AND X IS DISTINCT FROM <newX>;

אנשים רבים אינם מודעים לקיומו של מפעיל כל כך נפלא, אז הנה דף הונאה IS DISTINCT FROM ואופרטורים לוגיים אחרים שיעזרו:
PostgreSQL Antipatterns: נלחמים בהמוני "מתים"
... וקצת על פעולות במתחם ROW()-ביטויים:
PostgreSQL Antipatterns: נלחמים בהמוני "מתים"

#3: אני מזהה את אהובתי על ידי... חסימה

מושקים שני תהליכים מקבילים זהים, שכל אחד מהם מנסה לסמן את הערך שהוא "בביצוע":

UPDATE tbl SET processing = TRUE WHERE pk = $1;

גם אם תהליכים אלו למעשה עושים דברים בלתי תלויים זה בזה, אך בתוך אותו מזהה, הלקוח השני "יינעל" על בקשה זו עד להשלמת העסקה הראשונה.

פתרון מס '1: המשימה מצטמצמת לקודמתה

בוא נוסיף את זה שוב IS DISTINCT FROM:

UPDATE tbl SET processing = TRUE WHERE pk = $1 AND processing IS DISTINCT FROM TRUE;

בטופס הזה, הבקשה השנייה פשוט לא תשנה שום דבר במסד הנתונים, הכל כבר כמו שצריך - לכן, חסימה לא תתרחש. לאחר מכן, אנו מעבדים את העובדה של "לא למצוא" את הרשומה באלגוריתם המיושם.

פתרון מס '2: מנעולים מייעצים

נושא גדול למאמר נפרד, בו תוכלו לקרוא עליו שיטות יישום ו"גרפה" של חסימה ממליצה.

פתרון מס '3: שיחות מטופשות

אבל זה בדיוק מה שצריך לקרות לך עבודה בו זמנית עם אותו רקורד? או שהתבלבלת עם האלגוריתמים לקריאה בלוגיקה עסקית בצד הלקוח, למשל? ואם חושבים על זה?..

מקור: www.habr.com

הוספת תגובה