SQL分析死锁并处理

SQL分析死锁并处理 From CSDN.net
原帖地址: http://topic.csdn.net/u/20080721/20/15a60db6-34b5-4ea1-b392-11c42270aaab.html

-- 死鎖
/*
*****************************************************************************************************************************************************
死鎖指兩個以上事務相互阻塞相互等待對方釋放它們的鎖,SQL Server會通過回滾其中一個事務並返回一個錯誤來自已解決阻塞問題,讓其他事務完成它們的工作。

整理人:中国风(Roy)

日期:2008.07.20
*****************************************************************************************************************************************************
*/

set  nocount  on  ;
if   object_id ( ' T1 ' is   not   null
    
drop   table  T1
go
create   table  T1(ID  int   primary   key ,Col1  int ,Col2  nvarchar ( 20 ))
insert  T1  select   1 , 101 , ' A '
insert  T1  select   2 , 102 , ' B '
insert  T1  select   3 , 103 , ' C '
go

if   object_id ( ' T2 ' is   not   null
    
drop   table  T2
go
create   table  T2(ID  int   primary   key ,Col1  int ,Col2  nvarchar ( 20 ))
insert  T2  select   1 , 201 , ' X '
insert  T2  select   2 , 202 , ' Y '
insert  T2  select   3 , 203 , ' Z '


go
生成表數據:
/*
T1:
ID          Col1        Col2
----------- ----------- --------------------
1           101         A
2           101         B
3           101         C

T2:
ID          Col1        Col2
----------- ----------- --------------------
1           201         X
2           201         Y
3           201         Z
*/

防止死鎖:
1 、    最少化阻塞。阻塞越少,發生死鎖機會越少
2 、    在事務中按順序訪問表(以上例子:死鎖2)
3 、    在錯誤處理程式中檢查錯誤1205並在錯誤發生時重新提交事務
4 、    在錯誤處理程式中加一個過程將錯誤的詳細寫入日誌
5 、    索引的合理使用(以上例子:死鎖1、死鎖3)
當發生死鎖時,事務自動提交,可通過日誌來監視死鎖


死鎖1(索引):
-- 連接窗口1
--
1步:
begin   tran
    
update  t1  set  col2 = col2 + ' A '   where  col1 = 101

-- 3步:
     select   *   from  t2  where  col1 = 201
commit   tran


-- 連接窗口2

-- 2步:
begin   tran
    
update  t2  set  col2 = col2 + ' B '   where  col1 = 203

-- 4步:
     select   *   from  t1  where  col1 = 103
commit   tran



-- 連接窗口1:收到死鎖錯誤,連接窗口2得到結果:

/*
訊息 1205,層級 13,狀態 51,行 3
交易 (處理序識別碼 53) 在 鎖定 資源上被另一個處理序鎖死並已被選擇作為死結的犧牲者。請重新執行該交易。
*/

-- 連接窗口2:得到結果

/*
----------- ----------- --------------------
3           103         C
*/

處理方法:
-- 在t1、t2表的col1條件列建索引
create   index  IX_t1_col1  on  t1(col1)
create   index  IX_t2_col1  on  t2(col1)
go

-- 連接窗口1
--
1步:
begin   tran
    
update  t1  set  col2 = col2 + ' A '   where  col1 = 101

-- 3步:
select   *   from  t2  with ( index = IX_t2_col1) where  col1 = 201      -- 因表數據少,只能指定索引提示才能確保SQL Server使用索引
commit   tran



-- 連接窗口2

-- 2步:
begin   tran
    
update  t2  set  col2 = col2 + ' B '   where  col1 = 203


-- 4步:
select   *   from  t1  with ( index = IX_t1_col1)  where  col1 = 103      -- 因表數據少,只能指定索引提示才能確保SQL Server使用索引
commit   tran



-- 連接窗口1:
/*

ID          Col1        Col2
----------- ----------- --------------------
1           201         X

(1 個資料列受到影響)

*/
-- 連接窗口2
/*

ID          Col1        Col2
----------- ----------- --------------------
3           103         C

(1 個資料列受到影響)
*/


死鎖2(訪問表順序):

-- 連接窗口1:
--
1步:
begin   tran
    
update  t1  set  col1 = col1 + 1   where  ID = 1

-- 3步:
select  col1  from  t2  where  ID = 1
commit   tran



-- 連接窗口2:
--
2步:
begin   tran
    
update  t2  set  col1 = col1 + 1   where  ID = 1

-- 4步
select  col1  from  t1  where  ID = 1
commit   tran


-- 連接窗口1:

/*
col1
-----------
201

(1 個資料列受到影響)
*/

-- 連接窗口2:

/*
col1
-----------
訊息 1205,層級 13,狀態 51,行 1
交易 (處理序識別碼 54) 在 鎖定 資源上被另一個處理序鎖死並已被選擇作為死結的犧牲者。請重新執行該交易。
*/

處理方法:

-- 改變訪問表的順序

-- 連接窗口1:
--
1步:
begin   tran
    
update  t1  set  col1 = col1 + 1   where  ID = 1

-- 3步:
     select  col1  from  t2  where  ID = 1
commit   tran

-- 連接窗口2:
--
2步:
begin   tran
    
select  col1  from  t1  where  ID = 1 -- 會等待連接窗口1提交
--
4步
     update  t2  set  col1 = col1 + 1   where  ID = 1
commit   tran

死鎖3(單表):

-- 連接窗口1:

while   1 = 1
    
update  T1  set  col1 = 203 - col1  where  ID = 2

-- 連接窗口2:
declare   @i    nvarchar ( 20 )
while   1 = 1
    
set   @i = ( select  col2  from  T1  with ( index = IX_t1_col1) where  Col1 = 102 ); -- 因表數據少,只能指定索引提示才能確保SQL Server使用索引

-- 連接窗口1
/*

訊息 1205,層級 13,狀態 51,行 4
交易 (處理序識別碼 53) 在 鎖定 資源上被另一個處理序鎖死並已被選擇作為死結的犧牲者。請重新執行該交易。
*/


處理方法:
1 、刪除col1上的非聚集索引,這樣影響SELECT速度,不可取.
    
drop   index  IX_t1_col1  on  t1
2 、建一個覆蓋索引
    A、
drop   index  IX_t1_col1  on  t1
    B、
create   index  IX_t1_col1_col2  on  t1(col1,col2)


通過SQL Server Profiler查死鎖信息:

啟動SQL Server Profiler——連接實例——事件選取範圍——顯示所有事件
選擇項:
TSQL——SQL:StmtStarting
Locks——Deadlock graph(這是SQL2005新增事件,生成包含死鎖信息的xml值)
     ——Lock:DeadlockChain 死鎖鏈中的進程產生該事件,可標識死鎖進程的ID並跟蹤操作
     ——Lock:Deadlock 該事件發生了死鎖

你可能感兴趣的:(sql)