SQL deadlock ו-timeout: איך מאתרים תחרות בין פעולות בלי להאריך סתם את ההמתנה
חזרה לבלוג
full stack·5 בספטמבר 2026·4 דק' קריאה·מאת יהונתן סעדיה

SQL deadlock ו-timeout: איך מאתרים תחרות בין פעולות בלי להאריך סתם את ההמתנה

1205, 1222 ו-‎-2 נראים דומה בלוג ומשמעותם שונה לגמרי. איך מבדילים בין deadlock, פקיעת נעילה ו-timeout של הלקוח, ואיפה מוצאים את התחרות האמיתית בין הפעולות.

עיקרי הדברים

  • 1205 מגיעה עם rollback אוטומטי. השרת כבר ביטל את הטרנזקציה שלכם.
  • 1222 היא `Lock request time out period exceeded` - פעולה אחרת החזיקה נעילה זמן רב מהסבלנות שהוגדרה.
  • ‎-2 היא פקיעת `CommandTimeout` בצד הלקוח. השאילתה עלולה להמשיך לרוץ בשרת אחרי שהאפליקציה ויתרה.
  • הארכת ה-timeout מאריכה את החסימה, לא מסירה אותה. היא הופכת שגיאה מהירה לתקיעה ארוכה.
  • גרף ה-deadlock כבר קיים אצלכם. סשן `system_health` אוסף אותו כברירת מחדל.

שגיאה 1205 היא deadlock: השרת זיהה מעגל נעילות ובחר את הפעולה שלכם כקורבן. שגיאה 1222 היא פקיעת המתנה לנעילה. שגיאה ‎-2 מגיעה מהלקוח, לא מהשרת, ופירושה שהאפליקציה ויתרה. שלושתן נראות בלוג כ"תקלת בסיס נתונים", ורק אחת מהן היא באמת מעגל.

שלוש תקלות שנראות זהות בלוג של האפליקציה

ההבדל בין השלוש הוא מי ויתר ראשון: השרת, מנגנון הנעילות, או הקוד שלכם.

מספרמקורמה קרההאם יש rollback
1205SQL Server, שם סמלי LK_VICTIMמעגל נעילות; השרת בחר קורבןכן, אוטומטי
1222SQL Server, שם סמלי LK_TIMEOUTההמתנה לנעילה חרגה מ-LOCK_TIMEOUTלא בהכרח
‎-2SqlClient בצד הלקוחCommandTimeout פגלא, ולעיתים השאילתה ממשיכה
איןחסימה רגילהפעולה ממתינה ומצליחה בסוףלא

הטקסט המדויק של 1205 הוא: Transaction (Process ID %d) was deadlocked on %.*ls resources with another process and has been chosen as the deadlock victim. Rerun the transaction. הטקסט של 1222 קצר בהרבה: Lock request time out period exceeded.

למה הארכת ה-timeout מחמירה את המצב?

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

  • חיבור שממתין תופס חיבור ב-connection pool. כשהם נגמרים, גם בקשות בריאות נכשלות.
  • טרנזקציה שממתינה מחזיקה את הנעילות שכבר לקחה, ולכן חסימה ארוכה יותר מייצרת תור ארוך יותר מאחוריה.
  • SET LOCK_TIMEOUT בברירת מחדל הוא ‎-1, כלומר המתנה ללא הגבלה. הערך 0 אומר לא להמתין כלל.
  • ב-‎.NET ברירת המחדל של CommandTimeout היא 30 שניות, והערך 0 מבטל את ההגבלה לגמרי. אל תגדירו 0 בקוד שרץ מול משתמש.

הכלל המעשי: timeout קצר הוא מכשיר מדידה. הוא זה שאומר לכם שיש תחרות. מי שמאריך אותו מכבה את מכשיר המדידה.

איפה נמצא גרף ה-deadlock

ב-SQL Server קיים סשן Extended Events בשם system_health שפועל כברירת מחדל ואוסף אירועי xml_deadlock_report. כלומר, אם היה deadlock בימים האחרונים, הגרף שלו ככל הנראה כבר שמור אצלכם ואין צורך להפעיל מעקב חדש ולחכות שזה יקרה שוב.

הגרף מכיל שלושה חלקים: victim-list עם מזהה הקורבן, process-list עם כל התהליכים שהשתתפו, ו-resource-list עם המשאבים שעליהם נאבקו. שמירת ה-XML בסיומת .xdl ופתיחתו ב-SSMS מציגה אותו כתרשים.

שימו לב שהיעד מסוג event file מוגבל בגודל ובמספר קבצים, ולכן היסטוריה עמוקה לא נשמרת שם לנצח. אם ה-deadlock קורה פעם בשבועיים, ייצאו את הגרף ברגע שהוא נתפס.

איך מאתרים חסימה בזמן שהיא קורית?

תקלה חוזרת נפתרת הרבה יותר מהר כשתופסים אותה בשידור חי:

  1. הריצו שאילתה על sys.dm_exec_requests וחפשו שורות שבהן blocking_session_id שונה מאפס. זה מזהה מי חוסם את מי.
  2. הצליבו מול sys.dm_os_waiting_tasks כדי לראות על מה בדיוק ההמתנה.
  3. פנו ל-sys.dm_tran_locks וסננו לפי request_status בערך WAIT. שם יושבות בקשות הנעילה שלא אושרו.
  4. הסתכלו על resource_type ועל request_mode באותה שורה: הם אומרים אם מדובר בשורה בודדת, בדף או בטבלה שלמה.
  5. אתרו את הסשן החוסם ובדקו מה הוא מריץ ומתי הטרנזקציה שלו נפתחה.
  6. תעדו את מזהי הסשן ואת השאילתות לפני שאתם משחררים משהו. אחרי KILL המידע נעלם.

מה באמת מתקן תחרות

לפי התיעוד של Microsoft, deadlock הוא ברוב המקרים בעיה ברמת האפליקציה ודורש שינוי בקוד. הכיוונים לפי סדר התועלת:

  • גישה לטבלאות באותו סדר בכל הטרנזקציות. אם כולם נוגעים ב-Table1 ואז ב-Table2, ייתכן שתהיה חסימה אבל לא ייווצר מעגל. זו ההמלצה הראשונה של Microsoft, והיא הזולה ביותר ליישום.
  • טרנזקציות קצרות יותר. אל תפתחו טרנזקציה לפני קריאה לשירות חיצוני ואל תשאירו אותה פתוחה בזמן שהמשתמש חושב.
  • אינדקס מתאים. סריקה של טבלה שלמה נועלת הרבה יותר ממה שהיא צריכה. אינדקס שמצמצם את הסריקה מצמצם ישירות את שטח הנעילה.
  • קריאה בלי לחסום כתיבה. ALTER DATABASE ... SET READ_COMMITTED_SNAPSHOT ON מעביר טרנזקציות ב-READ COMMITTED לעבודה מול גרסאות שורה במקום נעילות. שימו לב שההפעלה דורשת שלא יהיו חיבורים פעילים אחרים לבסיס הנתונים מלבד זה שמריץ את הפקודה, ולכן היא נעשית בחלון תחזוקה.
  • ניסיון חוזר מבוקר על 1205 בלבד. קורבן deadlock מקבל rollback ולכן הרצה חוזרת לרוב תצליח. הוסיפו השהיה קצרה לפני הניסיון, והגבילו למספר ניסיונות סופי.

מתי SET DEADLOCK_PRIORITY הוא הכלי הנכון

SET DEADLOCK_PRIORITY לא מונע deadlock. הוא רק קובע מי יופסק כשהוא קורה. הערכים הם LOW, NORMAL ו-HIGH, שממופים למספרים ‎-5, 0 ו-5 בטווח שנע בין ‎-10 ל-10. ברירת המחדל היא NORMAL.

כששני הסשנים באותה עדיפות, SQL Server בוחר את הקורבן שזול יותר לבצע לו rollback, לפי כמות בייטי הלוג שנכתבו עד אותה נקודה. הערך הזה מופיע בגרף ה-deadlock כ-Log Used.

השימוש הלגיטימי היחיד: תהליך רקע שאפשר להריץ שוב בשקט, שמקבל LOW כדי שלא יפיל פעולה של משתמש אמיתי. שימוש ב-HIGH כדי ש"הקוד שלי לא ייפול" רק מעביר את הכאב למישהו אחר.

מה לרשום בלוג כדי שהפעם הבאה תהיה ניתנת לאבחון

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

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

קשור: שגיאות API נפוצות ב-Priority, תוכנית התאוששות לקובץ MDB פגום, מה זה בסיס נתונים.

מקורות

#SQL Server#deadlock#performance#timeout#database#שגיאות

שאלות נפוצות

האם נכון לנסות שוב אוטומטית אחרי 1205?

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

מה ההבדל בין חסימה לבין deadlock?

חסימה היא המתנה שמסתיימת מעצמה כשהצד השני משחרר. deadlock הוא מעגל שלא ייפתר לעולם בלי התערבות, ולכן SQL Server מזהה אותו ומפסיק אחד הצדדים. חסימה ארוכה מספיק תיראה כמו timeout, אבל היא לא deadlock.

למה השאילתה ממשיכה לרוץ אחרי שגיאת ‎-2?

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

האם READ_COMMITTED_SNAPSHOT פותר את הכול?

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

להמשך קריאה

שירות רלוונטי

הצלת מערכות אקסס ואקסל

לייצב את הקובץ שהעסק רץ עליו, ואז לתקן אותו או להעביר אותו לווב.

מידע נוסף →←

על הכותב

יהונתן סעדיה

מפתח פרילנסר לאוטומציה, אתרים ו-MVP

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

בוא נעבוד יחד

יש לך פרויקט דומה?

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