偵測與結束死結
當二或多個工作各自具有某個資源的鎖定,但其他工作嘗試要鎖定此資源,而造成工作永久封鎖彼此時,會發生死結。下圖顯示死結狀態的高階檢視,其中:
工作 T1 有資源 R1 的鎖定 (由 R1 到 T1 的箭頭所表示),並且已要求資源 R2 的鎖定 (由 T1 到 R2 的箭頭所表示)。
工作 T2 有資源 R2 的鎖定 (由 R2 到 T2 的箭頭所表示),並且已要求資源 R1 的鎖定 (由 T2 到 R1 的箭頭所表示)。
因為在有資源可用之前,沒有一項工作可以繼續,而在有工作繼續之前,沒有一項資源可以釋放,所以會有死結狀態。
SQL Server Database Engine 自動偵測 SQL Server 中的死結循環。Database Engine 會選擇其中一個工作階段作為死結犧牲者,讓目前交易終止並產生錯誤,以破除死結。
可能發生死結的資源
每個使用者工作階段可能有一或多個工作代表工作階段執行,其中每個工作可能會取得或等待取得各種資源。下列類型的資源會導致封鎖,而造成死結。
鎖定。等待取得像是物件、分頁、資料列、中繼資料和應用程式等資源的鎖定,可能會導致死結。例如,交易 T1 有資料列 r1 的共用 (S) 鎖定,並且正在等待取得 r2 的獨佔 (X) 鎖定。交易 T2 有 r2 的共用 (S) 鎖定,並且正在等待取得資料列 r1 的獨佔 (X) 鎖定。這樣會產生鎖定循環,因為 T1 和 T2 都在等待對方釋放已鎖定的資源。
工作者執行緒。等待可用工作者執行緒的佇列工作,可能會導致死結。如果佇列工作擁有正在封鎖所有工作者執行緒的資源,便會產生死結。例如,工作階段 S1 啟動交易,並且取得資料列 r1 的共用 (S) 鎖定,然後進入睡眠。在所有可用工作者執行緒上執行的使用中工作階段,正在嘗試取得資料列 r1 的獨佔 (X) 鎖定。因為工作階段 S1 無法取得工作者執行緒,所以它無法認可交易並釋放資料列 r1 的鎖定。這樣會產生死結。
記憶體。當並行要求正在等待記憶體授權,但可用記憶體不足而無法滿足授權時,便會發生死結。例如,兩個並行查詢 Q1 和 Q2 以使用者自訂函數執行,分別取得 10MB 和 20MB 記憶體。如果每個查詢需要 30MB 而可用的總記憶體是 20MB,則 Q1 和 Q2 必須等待對方釋放記憶體。這樣會產生死結。
與執行平行查詢相關的資源。與交換通訊埠相關聯的協調器、產生者或取用者,通常在包含至少一個不屬於平行查詢的其他處理序時,可能會彼此封鎖,而導致死結。而且,當平行查詢開始執行時,SQL Server 會根據目前工作負載來判斷平行程度或工作者執行緒數目。如果系統工作負載意外變更,例如有新查詢開始在伺服器上執行或系統的工作者執行緒用盡,此時會發生死結。
Multiple Active Result Set (MARS) 資源。這些資源在 MARS 下是用來控制多個使用中要求的交錯情形 (請參閱<批次執行環境和 MARS>)。
使用者資源。當執行緒正在等待的資源可能受使用者應用程式控制時,此資源會視為外部或使用者資源,並且如同鎖定處理。
工作階段 Mutex。工作階段中執行的工作為交錯的,這表示同時只能有一個工作在此工作階段執行。工作必須具有對工作階段 Mutex 的獨佔存取權才能執行。
交易 Mutex。交易中執行的所有工作為交錯的,這表示同時只能有一個工作在此交易執行。工作必須具有對交易 Mutex 的獨佔存取權才能執行。
工作必須取得工作階段 Mutex,才能在 MARS 下執行。如果工作在交易下執行,則它必須接著取得交易 Mutex。這可保證在給定工作階段和給定交易中,同時只有一個工作使用中。一旦取得所需的 Mutex,工作即可執行。當工作完成或在要求中途退出時,會以取得 Mutex 的相反順序,先釋放交易 Mutex,接著再釋放工作階段 Mutex。不過,這些資源可能會發生死結。在下列程式碼範例中,同一個工作階段中執行了兩個工作 (使用者要求 U1 和使用者要求 U2)。
U1: Rs1=Command1.Execute("insert sometable EXEC usp_someproc"); U2: Rs2=Command2.Execute("select colA from sometable");
從使用者要求 U1 執行的預存程序已取得工作階段 Mutex。如果預存程序花很長的時間執行,則 Database Engine 會假設此預存程序正在等待使用者輸入。當使用者正在等待 U2 的結果集時,使用者要求 U2 正在等待工作階段 Mutex,而 U1 正在等待使用者資源。這就是死結狀態,邏輯上可用下圖說明:
死結偵測
上節列出的所有資源都參與 Database Engine 死結偵測配置。死結偵測由鎖定監視器執行緒所執行,它會定期在 Database Engine 執行個體的所有工作中啟動搜尋。下列幾點描述搜尋程序:
預設間隔是 5 秒。
如果鎖定監視器執行緒發現死結,死結偵測間隔會從 5 秒降低,最低降至 100 毫秒,視死結頻率而定。
如果鎖定監視器執行緒停止尋找死結,Database Engine 會增加搜尋間隔為 5 秒。
如果剛偵測到死結,則會假設後續還有必須等待鎖定的執行緒進入死結循環。在偵測到死結之後,前面幾個鎖定等待會立即觸發死結搜尋,而不需等到下個死結偵測間隔。例如,如果目前間隔是 5 秒,並且剛偵測到死結,則下個鎖定等待會立即啟動死結偵測設定。如果這個鎖定等待是死結的一部分,則會立即偵測到它,而不需等到下個死結搜尋期間。
Database Engine 通常僅執行定期的死結偵測。由於系統會遇到的死結數量通常很少,週期的死結偵測即可協助將系統在死結偵測上的負擔降低。
當鎖定監視執行緒為特定的執行緒啟動死結搜尋時,便對執行緒正在等候的資源進行識別。而後再由鎖定監視找出該特定資源的擁有者執行緒,並繼續為這些執行緒進行遞迴的死結搜尋直到找出循環為止。以此方式識別到的循環即構成死結。
在偵測到死結之後,Database Engine 會選擇其中一個執行緒作為死結犧牲者,結束死結。Database Engine 會結束目前為此執行緒所執行的批次、回復死結犧牲者的交易,並且傳回 1205 錯誤至應用程式。回復死結犧牲者的交易,將會釋放交易所持有的所有鎖定。這可讓其他執行緒的交易變成解除封鎖的狀態並繼續進行。1205 死結犧牲者錯誤會將與死結相關的執行緒和資源資訊記錄在錯誤記錄檔中。
依預設,Database Engine 會選擇執行回復成本最低之交易的工作階段作為死結犧牲者。或者,使用者也可以使用 SET DEADLOCK_PRIORITY 陳述式,指定死結情況下工作階段的優先權。DEADLOCK_PRIORITY 可以設為 LOW、NORMAL 或 HIGH,或設為 -10 到 10 範圍內的任何整數值。死結優先權預設為 NORMAL。如果兩個工作階段有不同的死結優先權,優先權較低的工作階段會被選為死結犧牲者。如果兩個工作階段有相同的死結優先權,則會選擇回復成本最低之交易的工作階段。如果死結循環中相關的工作階段具有相同的死結優先權和相同成本,則會隨機選擇犧牲者。
使用 CLR 時,死結監視器會為 Managed 程序內所存取的同步處理資源 (監視器、讀取器/寫入器鎖定和執行緒聯結) 自動偵測是否有死結。不過,死結是透過在選為死結犧牲者的程序中擲回例外狀況來解決。例外狀況並不會自動釋放犧牲者目前所擁有的資源;您必須明確釋放資源,瞭解這點很重要。與例外狀況行為一致,用來識別死結犧牲者的例外狀況可以在發生後解除。
死結資訊工具
為了檢視死結資訊,Database Engine 以兩個追蹤旗標的形式以及 SQL Server Profiler 中的死結圖形事件來提供監督工具。
追蹤旗標 1204 和追蹤旗標 1222
發生死結時,追蹤旗標 1204 和追蹤旗標 1222 會傳回在 SQL Server 2005 錯誤記錄檔中擷取到的資訊。追蹤旗標 1204 報告死結所涉及的每一個節點格式化的死結資訊。追蹤旗標 1222 先按處理序再按資源來格式化死結資訊。可同時啟用兩種追蹤旗標來取得相同死結事件的兩種表示法。
除了定義追蹤旗標 1204 和 1222 的屬性之外,下表還顯示其相似性和差異。
屬性 |
追蹤旗標 1204 和追蹤旗標 1222 |
僅追蹤旗標 1204 |
僅追蹤旗標 1222 |
---|---|---|---|
輸出格式 |
輸出是擷取到 SQL Server 2005 錯誤記錄檔中。 |
聚焦於死結所涉及的節點。每一個節點有一個專用區段,最後區段描述死結犧牲者。 |
以類似 XML 格式傳回不符合 XML 結構描述定義 (XSD) 結構描述的資訊。此格式有三大區段。第一個區段宣告死結犧牲者。第二個區段描述死結所涉及的每一個處理序。第三個區段描述與追蹤旗標 1204 中的節點同義的資源。 |
識別屬性 |
SPID:<x> ECID:<x>。識別平行處理序中的系統處理序識別碼執行緒。SPID:<x> ECID:0 項目中的 <x> 被 SPID 值取代,此項目代表主執行緒。SPID:<x> ECID:<y> 項目中的 <x> 被 SPID 值取代,且 <y> 大於 0,該項目代表相同 SPID 子執行緒。 BatchID (追蹤旗標 1222 的 sbid)。識別程式碼執行從中要求或保留鎖定的批次。當 Multiple Active Result Set (MARS) 停用時,BatchID 值為 0。啟用 MARS 之後,作用中批次的值為 1 到 n。如果工作階段中沒有作用中批次,BatchID 為 0。 模式。指定執行緒所要求、授與或等待之特定資源的鎖定類型。模式可為 IS (意圖共用)、S (共用)、U (更新)、IX (意圖獨佔)、SIX (共用意圖獨佔) 和 X (獨佔)。如需詳細資訊,請參閱<鎖定模式>。 Line # (追蹤旗標 1222 的 line)。列出發生死結時正在執行之目前陳述式批次中的行號。 Input Buf (追蹤旗標 1222 的 inputbuf)。列出目前批次中的所有陳述式。 |
節點。代表死結鏈結中的項目號碼。 清單。鎖定擁有者可以是這些清單的一部分:
陳述式類型。描述執行緒有權限的 DML 陳述式的類型 (SELECT、INSERT、UPDATE 或 DELETE)。 犧牲者資源擁有者。指定 SQL Server 選擇作為犧牲者來中斷死結循環的參與執行緒。選擇的執行緒和所有現存的子執行緒會終止。 下一分支。代表相同 SPID 中兩個以上涉及死結循環的子執行緒。 |
deadlock victim。代表選為死結犧牲者之工作的實體記憶位址 (請參閱 sys.dm_os_tasks (Transact-SQL))。在未解決的死結案例中,它可能會是 0 (零)。回復中的工作不可選為死結犧牲者。 executionstack。代表發生死結時正在執行的 Transact-SQL 程式碼。 priority。代表死結優先權。在特定案例中,Database Engine 可能會選擇在短時間內變更死結優先權,以達到更佳的並行效果。 logused。工作所使用的記錄檔空間。 owner id。具有要求控制權之交易的識別碼。 status。工作的狀態。它是下列其中一值:
waitresource。工作所需的資源。 waittime。等待資源的時間 (以毫秒為單位)。 schedulerid。與這個工作相關聯的排程器。請參閱<sys.dm_os_schedulers (Transact-SQL)>。 hostname。工作站的名稱。 isolationlevel。目前交易隔離等級。 Xactid。具有要求控制權之交易的識別碼。 currentdb。資料庫的識別碼。 lastbatchstarted。用戶端處理序上次啟動批次執行的時間。 lastbatchcompleted。用戶端處理序上次完成批次執行的時間。 clientoption1 和 clientoption2。此用戶端連接上的設定選項。這是位元遮罩,其中包含通常由 SET 陳述式 (例如 SET NOCOUNT 和 SET XACTABORT) 所控制之選項的資訊。 associatedObjectId。代表 HoBT (堆積或 B 型樹狀目錄) 識別碼。 |
資源屬性 |
RID。識別在資料表內保留或要求鎖定的單一資料列。RID 是以 RID: db_id:file_id:page_no:row_no 表示。例如,RID: 6:1:20789:0。 OBJECT。識別保留或要求鎖定的資料表。OBJECT 是以 OBJECT: db_id:object_id 表示。例如,TAB: 6:2009058193。 KEY。識別在索引內保留或要求鎖定的索引鍵範圍。KEY 是以 KEY: db_id:hobt_id (index key hash value) 表示。例如,KEY: 6:72057594057457664 (350007a4d329)。 PAG。識別保留或要求鎖定的頁面資源。PAG 是以 PAG: db_id:file_id:page_no 表示。例如,PAG: 6:1:20789。 EXT。識別範圍結構。EXT 是以 EXT: db_id:file_id:extent_no 表示。例如,EXT: 6:1:9。 DB。識別資料庫鎖定。DB 是以下列其中一種方式表示:
APP。識別應用程式資源使用的鎖定。APP 是以 APP: lock_resource 表示。例如,APP: Formf370f478。 METADATA。代表死結所涉及的中繼資料資源。因為 METADATA 有許多子資源,所以傳回的值視含有死結的子資源而定。例如,METADATA.USER_TYPE 傳回 user_type_id = <integer_value>。如需有關 METADATA 資源和子資源的詳細資訊,請參閱<sys.dm_tran_locks (Transact-SQL)>。 HOBT。代表死結所涉及的堆積或 B 型樹狀目錄。 |
None 與此追蹤旗標互斥。 |
None 與此追蹤旗標互斥。 |
追蹤旗標 1204 範例
下列範例顯示追蹤旗標 1204 開啟時的輸出。在此案例中,節點 1 中的資料表是一個不含索引的堆積,節點 2 中的資料表是一個含非叢集索引的堆積。當發生死結時,正在更新節點 2 中的索引鍵。
Deadlock encountered .... Printing deadlock information
Wait-for graph
Node:1
RID: 6:1:20789:0 CleanCnt:3 Mode:X Flags: 0x2
Grant List 0:
Owner:0x0315D6A0 Mode: X
Flg:0x0 Ref:0 Life:02000000 SPID:55 ECID:0 XactLockInfo: 0x04D9E27C
SPID: 55 ECID: 0 Statement Type: UPDATE Line #: 6
Input Buf: Language Event:
BEGIN TRANSACTION
EXEC usp_p2
Requested By:
ResType:LockOwner Stype:'OR'Xdes:0x03A3DAD0
Mode: U SPID:54 BatchID:0 ECID:0 TaskProxy:(0x04976374) Value:0x315d200 Cost:(0/868)
Node:2
KEY: 6:72057594057457664 (350007a4d329) CleanCnt:2 Mode:X Flags: 0x0
Grant List 0:
Owner:0x0315D140 Mode: X
Flg:0x0 Ref:0 Life:02000000 SPID:54 ECID:0 XactLockInfo: 0x03A3DAF4
SPID: 54 ECID: 0 Statement Type: UPDATE Line #: 6
Input Buf: Language Event:
BEGIN TRANSACTION
EXEC usp_p1
Requested By:
ResType:LockOwner Stype:'OR'Xdes:0x04D9E258
Mode: U SPID:55 BatchID:0 ECID:0 TaskProxy:(0x0475E374) Value:0x315d4a0 Cost:(0/380)
Victim Resource Owner:
ResType:LockOwner Stype:'OR'Xdes:0x04D9E258
Mode: U SPID:55 BatchID:0 ECID:0 TaskProxy:(0x0475E374) Value:0x315d4a0 Cost:(0/380)
追蹤旗標 1222 範例
下列範例顯示追蹤旗標 1222 開啟時的輸出。在此案例中,有一個資料表是不含索引的堆積,另一個資料表是含非叢集索引的堆積。在第二份資料表中,當發生死結時,正在更新索引鍵。
deadlock-list
deadlock victim=process689978
process-list
process id=process6891f8 taskpriority=0 logused=868
waitresource=RID: 6:1:20789:0 waittime=1359 ownerId=310444
transactionname=user_transaction
lasttranstarted=2005-09-05T11:22:42.733 XDES=0x3a3dad0
lockMode=U schedulerid=1 kpid=1952 status=suspended spid=54
sbid=0 ecid=0 priority=0 transcount=2
lastbatchstarted=2005-09-05T11:22:42.733
lastbatchcompleted=2005-09-05T11:22:42.733
clientapp=Microsoft SQL Server Management Studio - Query
hostname=TEST_SERVER hostpid=2216 loginname=DOMAIN\user
isolationlevel=read committed (2) xactid=310444 currentdb=6
lockTimeout=4294967295 clientoption1=671090784 clientoption2=390200
executionStack
frame procname=AdventureWorks.dbo.usp_p1 line=6 stmtstart=202
sqlhandle=0x0300060013e6446b027cbb00c69600000100000000000000
UPDATE T2 SET COL1 = 3 WHERE COL1 = 1;
frame procname=adhoc line=3 stmtstart=44
sqlhandle=0x01000600856aa70f503b8104000000000000000000000000
EXEC usp_p1
inputbuf
BEGIN TRANSACTION
EXEC usp_p1
process id=process689978 taskpriority=0 logused=380
waitresource=KEY: 6:72057594057457664 (350007a4d329)
waittime=5015 ownerId=310462 transactionname=user_transaction
lasttranstarted=2005-09-05T11:22:44.077 XDES=0x4d9e258 lockMode=U
schedulerid=1 kpid=3024 status=suspended spid=55 sbid=0 ecid=0
priority=0 transcount=2 lastbatchstarted=2005-09-05T11:22:44.077
lastbatchcompleted=2005-09-05T11:22:44.077
clientapp=Microsoft SQL Server Management Studio - Query
hostname=TEST_SERVER hostpid=2216 loginname=DOMAIN\user
isolationlevel=read committed (2) xactid=310462 currentdb=6
lockTimeout=4294967295 clientoption1=671090784 clientoption2=390200
executionStack
frame procname=AdventureWorks.dbo.usp_p2 line=6 stmtstart=200
sqlhandle=0x030006004c0a396c027cbb00c69600000100000000000000
UPDATE T1 SET COL1 = 4 WHERE COL1 = 1;
frame procname=adhoc line=3 stmtstart=44
sqlhandle=0x01000600d688e709b85f8904000000000000000000000000
EXEC usp_p2
inputbuf
BEGIN TRANSACTION
EXEC usp_p2
resource-list
ridlock fileid=1 pageid=20789 dbid=6 objectname=AdventureWorks.dbo.T2
id=lock3136940 mode=X associatedObjectId=72057594057392128
owner-list
owner id=process689978 mode=X
waiter-list
waiter id=process6891f8 mode=U requestType=wait
keylock hobtid=72057594057457664 dbid=6 objectname=AdventureWorks.dbo.T1
indexname=nci_T1_COL1 id=lock3136fc0 mode=X
associatedObjectId=72057594057457664
owner-list
owner id=process6891f8 mode=X
waiter-list
waiter id=process689978 mode=U requestType=wait
Profiler 死結圖形事件
這是 SQL Server Profiler 中的一個事件,它提供與死結相關之工作和資源的圖形描述。下列範例顯示死結圖形事件開啟時 SQL Server Profiler 的輸出。
如需有關執行 SQL Server Profiler 死結圖形的詳細資訊,請參閱<使用 SQL Server Profiler 分析死結>。