解決SQL2005死鎖問題
讓我們從這樣一個示例開始說起,它在 SQL Server 2000 和 2005 中都能引起死鎖。在本文中,我使用 SQL Server 2005 的最新 CTP(社群技術預覽,Community Technology Preview)版本,SQL Server 2005 Beta 2(7 月釋出)也同樣適用。如果您沒有 Beta 2 或最新的 CTP 版本,請下載 SQL Server 2005 Express 的最新版本,用它來進行試驗。
可能發生的死鎖情況有很多,[參閱http://msdn.microsoft.com/library/default.asp?url=/library/en-us/acdata/ac_8_con_7a_3xrf.asp以及死鎖文章樹中的後續文章。— 編者 ],但最有趣、最微妙的是那些關於閱讀器和編寫器互相阻塞的死鎖。以下程式碼在 pubs 資料庫中就產生了這樣一個死鎖。(您可以在 SQL Server 2000 的兩個 Query Analyzer 視窗中或 SQL Server 2005 的兩個 Management Studio queries 中並列執行這段程式碼。)在其中一個視窗中的程式碼正文前面新增下列語句:
-- Window 1 header DECLARE @au_id varchar(11), @au_lname varchar(40) SELECT @au_id = '111-11-1111', @au_lname = 'test1' |
在第二個視窗中新增下列語句,進行第二次連線:
-- Window 2 header DECLARE @au_id varchar(11), @au_lname varchar(40) SELECT @au_id = '111-11-1112', @au_lname = 'test2' |
在兩個視窗中都使用下列語句作為程式碼正文:
-- Body for both connections: BEGIN TRANSACTION INSERT Authors VALUES (@au_id, @au_lname, '', '', '', '', '', '11111', 0) WAITFOR DELAY '00:00:05' SELECT * FROM authors WHERE au_lname LIKE 'Test%' COMMIT |
在第三個視窗中執行下列語句,確保 authors 表格中沒有任何包含以下 id 的資料:
DELETE FROM authors WHERE au_id = '111-11-1111' DELETE FROM authors WHERE au_id = '111-11-1112' |
在 5 秒鐘內同時執行視窗 1 和 視窗 2。因為每個視窗都要等待至少 5 秒鐘的時間才能發出 SELECT 語句,所有每個連線都將完成 INSERT 操作,這樣就保證了兩個視窗中的 INSERT 操作在各自的 SELECT 語句釋出前就已經完成了。每個視窗中的 SELECT 語句都嘗試讀取 authors 表格中的所有資料,查詢 au_lname 欄位值中類似“Test%”格式的資料。因此,兩個視窗中的 SELECT 語句都將嘗試讀取各自連線中的插入資料 — 也讀取對方連線中的插入資料。
read COMMITTED 隔離級別通過釋出共享鎖確保 SELECT 語句永遠不讀取未提交的資料。對於同一個資源,共享鎖與排它鎖互不相容,請求者在釋出共享鎖之前必須等待排它鎖釋放。每個連線對於插入的資料都設定了排它鎖,因此嘗試讀取對方插入資料的 SELECT 語句將試圖解除插入資料的共享鎖,但它會被阻塞。兩個連線將互相阻塞,從而形成一個死鎖。SQL Server 的鎖定管理器檢測到死鎖時,將中止其中的一個批處理,回滾它的事務,釋放它的阻塞鎖,以便其他事務能夠完成。作為死鎖犧牲品的事務將回滾,其他事務則將成功完成。
如何使用 TRY/CATCH 語句避免死鎖
現在,讓我們來使用 TRY/CATCH 語句修改程式碼正文。(對於本示例,需要以 SQL Server 2005 版本執行程式碼。)使用 TRY/CATCH 時,操作程式碼和錯誤處理程式碼是分開的。您應該將執行一個操作的程式碼放在 TRY 語句塊中,將錯誤處理程式碼放在 CATCH 語句塊中。如果 TRY 語句塊中的程式碼執行失敗,程式碼執行將跳到 CATCH 語句塊。(除了那些防礙整個批處理執行的錯誤(如,丟失物件),該方法幾乎適用於所有的錯誤。)
以下示例使用 TRY/CATCH 語句對前面使用的程式碼進行了改寫。程式碼標題相同,但是程式碼正文不同:
BEGIN TRANSACTION BEGIN TRY INSERT Authors VALUES (@au_id, @au_lname, '', '', '', '', '', '11111', 0) WAITFOR DELAY '00:00:05' SELECT COUNT(*) FROM Authors COMMIT END TRY BEGIN CATCH SELECT ERROR_NUMBER() AS ErrorNumber ROLLBACK END CATCH; SELECT @@TRANCOUNT AS '@@Trancount' |
現在,在連線到 SQL Server 2005 的並列視窗中執行這些程式碼,在此之前您需要確認已經刪除了 authors 表格中任何可能阻止插入操作的資料;或者,您可以使用前置 DELETE 語句。
兩個視窗返回的 @@TRANCOUNT 級別都為 0,這表明仍然發生了死鎖,但 TRY/CATCH 語句捕獲了這次發生的死鎖。死鎖犧牲品的批處理沒有再次中止,可在它的輸出結果中看到錯誤:
ErrorNumber ----------- 1205 @@Trancount ----------- 0 |
您應該已經發現 TRY/CATCH 語句具有的威力了。因為死鎖錯誤能夠為 CATCH 語句塊所捕獲,所以批處理將不再中止,T-SQL 程式碼也能繼續執行。對於死鎖犧牲品而言,死鎖錯誤 1205 將程式碼放入 CATCH 語句塊 — 在這裡您可以使用新的錯誤處理函式瀏覽死鎖錯誤。前置程式碼僅使用 ERROR_NUMBER() 函式取代 @@ERROR 變數,您也可以使用 ERROR_MESSAGE()、ERROR_PROCEDURE()、ERROR_SEVERITY() 和 ERROR_STATE()。這些函式的功能一目瞭然,它們提供的功能比我們以往使用的更多。
請注意,這個前置 CATCH 語句塊包含一個 ROLLBACK。這樣做的原因是,即使捕獲了死鎖錯誤,事務也不會回滾。事務仍然要失敗,但是,現在您有責任在 TRY/CATCH 語句中回滾事務。那麼,區別在哪裡?儘管您不能使事務繼續進行,但是您能夠 重試事務!
在 TRY/CATCH 語句中進行重試
在 SQL Server 2000 的 T-SQL 中,錯誤 1205 令人沮喪之處是它提供的建議:“Rerun the transaction.”問題是,至少在 SQL Server 2000 的 T-SQL 中,您不能做到這一點。但是,由於 SQL Server 2005 的 TRY/CATCH 為我們提供了捕獲死鎖錯誤的方法,現在,重試事務是可能實現的。
以下程式碼正文說明了一種執行重試操作的方法。這段程式碼仍然使用與前面相同的標題:
DECLARE @Tries tinyint SET @Tries = 1 WHILE @Tries <= 3 BEGIN BEGIN TRANSACTION BEGIN TRY INSERT Authors VALUES (@au_id, @au_lname, '', '', '', '', '', '11111', 0) WAITFOR DELAY '00:00:05' SELECT * FROM authors WHERE au_lname LIKE 'Test%' COMMIT BREAK END TRY BEGIN CATCH SELECT ERROR_NUMBER() AS ErrorNumber ROLLBACK SET @Tries = @Tries + 1 CONTINUE END CATCH; END |
這段程式碼的功能是通過一個 WHILE 迴圈新增一個重試操作。我將重試次數設定為 3,重試次數是可以配置的。至少我們現在有了一種在 T-SQL 內重試一個死鎖犧牲品程式碼的方法 — 這是我們過去一直無法做到的。
但是,需要注意整個事務是在 WHILE 迴圈內進行的 — 而不是在迴圈外部。因此執行迴圈時,事務不僅在每個迴圈體內部開始,而且也在其中結束 — 不是 TRY 語句塊執行完畢,返回一個 COMMIT,就是 CATCH 語句塊執行,返回一個 ROLLBACK。如果 TRY 成功,TRY 語句塊將以一個 BREAK 語句結束,退出 WHILE 迴圈。否則,CATCH 語句塊將重試計數器加 1,以一個 CONTINUE 語句結束本次迴圈,重新執行下次 WHILE 迴圈。事實上,您有實現重試事務的程式碼 — 就像錯誤 1205 告訴我們做的那樣。但現在,重試操作完全在 T-SQL 內部完成。
sql Server 2005 也提供幫助解決死鎖問題的其他方法,例如 SNAPSHOT ISOLATION 級別和用於 READ COMMITTED 的新選項(稱為 READ COMMITTED SNAPSHOT)。然而,這一事實 — 現在,通過 SQL Server 2005,您能夠對事務進行編碼並捕獲死鎖錯誤(並重試它們) — 已經意味著您擁有一個可任意支配、功能更加強大的工具。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/16436858/viewspace-616840/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 解決Oracle死鎖問題步驟Oracle
- SQLserver 程式被死鎖問題解決SQLServer
- Mysql使用kill命令解決死鎖問題MySql
- 通過 sysprocesses 解決Sql死鎖問題SQL
- Oracle觸發器死鎖問題解決Oracle觸發器
- Java 程式死鎖問題原理及解決方案Java
- 手把手教你分析解決MySQL死鎖問題MySql
- SQ死鎖及死鎖的解決
- 故障解析丨一次死鎖問題的解決
- 死鎖問題排查過程-間隙鎖的復現以及解決
- MySQL死鎖問題MySql
- MySQL 死鎖解決MySql
- MySQL解決死鎖MySql
- SQL Server 2000 死鎖(dead lock) 問題解決SQLServer
- 記憶體混亂及解決方法和死鎖問題記憶體
- MySQL死鎖系列-線上死鎖問題排查思路MySql
- MySQL 死鎖問題分析MySql
- Sqlserver分析死鎖問題SQLServer
- 線上死鎖問題分析
- 死鎖問題總結
- Python | 多執行緒死鎖問題的巧妙解決方法Python執行緒
- 最近解決了一個比較複雜的“死鎖”問題
- sqlserver大數批次update時死鎖的問題及解決方案SQLServer
- MySQL鎖等待與死鎖問題分析MySql
- 面試:什麼是死鎖,如何避免或解決死鎖;MySQL中的死鎖現象,MySQL死鎖如何解決面試MySql
- sql server死鎖的問題SQLServer
- 死鎖檢測及解決
- Oracle 解決鎖表問題Oracle
- oracle 鎖問題的解決Oracle
- 關於資料庫死鎖問題的解釋資料庫
- MySQL死鎖分析與解決之路MySql
- 解決Oracle死鎖的快捷方法Oracle
- oracle死鎖測試與解決Oracle
- 解決Oracle資料庫死鎖Oracle資料庫
- openldap+NFS 鎖問題解決LDANFS
- 什麼是死鎖?如何解決死鎖?
- 併發技術5:死鎖問題
- SpringBoot Seata 死鎖問題排查Spring Boot