在說這個之前首先了解一下講一下update語句sql中的情況。
死鎖產生的條件:
出現循環等待資源。
update對鎖的流程:
當sql發出一個update請求之后,數據庫會對表中的每條記錄加上U鎖。然后數據庫會根據where條件,將符合條件的記錄轉換為X鎖。對不滿足條件的記錄釋放U鎖。
環境模擬
1. 創建數據庫環境
--創建數據庫 create database DeadLockTest; --創建數據表 (沒有主鍵) use DealLocktest; create table t_table( A varchar(10), B varchar(10), C varchar(10) ) insert into t_table values('a1','b1','c1'); insert into t_table values('a2','b2','c2'); insert into t_table values('a3','b3','c3'); insert into t_table values('a4','b4','c4'); insert into t_table values('a5','b5','c5'); insert into t_table values('a6','b6','c6'); insert into t_table values('a7','b7','c7'); insert into t_table values('a8','b8','c8'); insert into t_table values('a9','b9','c9');
創建完后,應該是這個樣子:
2.准備高並發的查詢環境
因為一個人測試,很不好模擬現場的環境。所以使用sql 的waitfor 阻塞某一個查詢:
A要執行的sql語句:
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED begin tran print convert(nvarchar(30),convert(datetime,getdate(),121),121) update t_table set A='a2' where B='b2' print convert(nvarchar(30),convert(datetime,getdate(),121),121) EXEC sp_lock @@spid waitfor delay '00:00:05' update t_table set A='b4' where B='b4' EXEC sp_lock @@spid print convert(nvarchar(30),convert(datetime,getdate(),121),121) commit tran
B要進行的sql語句
SET TRANSACTION ISOLATION LEVEL Read UNCOMMITTED begin tran update t_table set A='a1' where B='b1' EXEC sp_lock @@spid commit tran
因為A中第一條查詢和第二天查詢有5s間隔,所以整個的執行過程為:
1、執行A的第一條語句 阻塞5s
2、執行B的查詢
3、A的阻塞釋放,要執行A的第二條語句
要執行的圖解:
分析:
1、執行A的查詢的時候,數據庫將所有的記錄加上U鎖,然后將將不是c2的記錄全部釋放U鎖。對滿足條件的數據添加X鎖。此時,A事務還沒有結束,A不會釋放此時的X鎖。
2、B查詢的時候,B會對表中的所有記錄添加U鎖,因為B查詢要用到t_table這張表,B查詢c1的時候,滿足條件給c1添加上X鎖。執行c=2的時候,要給c2加U鎖,此時該記錄被A添加了X鎖。所以B查詢需要等A釋放的X鎖,才可以執行。此時B等待。B要等待A釋放c2的X鎖
3、A的阻塞釋放之后,A要進行第二條查詢。這個時候A要對符合第二個查詢條件的記錄添加X鎖。當執行c1的時候,c1剛才被B添加了X鎖。所以此時A等待。A在等待B的U鎖釋放。
結論
A在等待B釋放X鎖,B在等待A釋放X鎖。所以才會發生死鎖。
解決方案:
給查詢條件加索引,原因參考下一篇詳解索引。
總結
死鎖的發生,肯定是因為資源的循環等待。分析死鎖,就是分析這些進程分別需要等待什么資源。在sql server中 一般可以使用 sql profiler進行死鎖的監控。