死鎖產生的條件:
出現循環等待資源。
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進行死鎖的監控。