1. 程式人生 > >SqlServer 併發事務:死鎖跟蹤(二)確定死鎖鎖定的資源

SqlServer 併發事務:死鎖跟蹤(二)確定死鎖鎖定的資源

--測試示例:
CREATE TABLE mytest  
(  
    id  INT,  
    name    VARCHAR(20),  
    info    VARCHAR(20),  
)  

INSERT INTO mytest VALUES(1,'kk',null),(2,'mm',null)  



--【現在測試只有非聚集索引的】
CREATE NONCLUSTERED INDEX IX_mytest_id ON DBO.mytest(id)  


--開啟跟蹤標誌
DBCC TRACEON(1222,-1)


--分別打開個視窗,先執行事務視窗【1】,再執行事務視窗【2】
--事務視窗【1】 
BEGIN TRAN  
	PRINT @@SPID
    update dbo.mytest set info='A' where id =1  
    waitfor delay '00:00:10'  
    update dbo.mytest set info='B' where id =2  
ROLLBACK TRAN  


--事務視窗【2】 
BEGIN TRAN  
	PRINT @@SPID
    update dbo.mytest set info='C' where id =2  
    select * from dbo.mytest where id =1
ROLLBACK TRAN  

--session=61 成為死鎖犧牲品


--開啟SqlServer日誌,幾個地方可以看到鎖定的資源資訊。


這個RID具體是哪行資料在爭用導致死鎖??


--找到主要資訊
waitresource=RID: 7:1:786:0
這是一個堆表,db_id=7,fileId=1,pageId=786,Slot = 0


--【方法一】檢視資料頁的資訊
DBCC TRACEON(3604)
DBCC PAGE(TEST,1,786,3)

PAGE: (1:786)     

Slot 0 Offset 0x1008Length 19

Record Type = PRIMARY_RECORD         Record Attributes =  NULL_BITMAP VARIABLE_COLUMNS

Record Size = 19                    

Memory Dump @0x62DAD008

00000000:   30000800 01000000 04000802 00110013 †0...............        

00000010:   006b6b†††††††††††††††††††††††††††††††.kk                     

Slot 0 Column 67108865 Offset 0x0 Length 0 Length (physical) 0

DROPPED = NULL                      

Slot 0 Column 1 Offset 0x4 Length 4 Length (physical) 4

id = 1                              

Slot 0 Column 2 Offset 0x11 Length 2 Length (physical) 2

name = kk                           

Slot 0 Column 3 Offset 0x0 Length 0 Length (physical) 0

info =[NULL]                       

Slot 1 Offset 0x101eLength 19

Record Type =PRIMARY_RECORD         Record Attributes=  NULL_BITMAP VARIABLE_COLUMNS

Record Size = 19                    

Memory Dump @0x62DAD01E

00000000:   30000800 02000000 04000802 00110013 †0...............        

00000010:   006d6d†††††††††††††††††††††††††††††††.mm                     

Slot 1 Column 67108865Offset 0x0 Length 0 Length (physical) 0

DROPPED = NULL                      

Slot 1 Column 1 Offset 0x4Length 4 Length (physical) 4

id = 2                              

Slot 1 Column 2 Offset0x11 Length 2 Length (physical) 2

name = mm                           

Slot 1 Column 3 Offset 0x0Length 0 Length (physical) 0

info = [NULL]                       

DBCC 執行完畢。如果DBCC 輸出了錯誤資訊,請與系統管理員聯絡。


可以看到Slot = 0的資料內容為mytest(1,'kk',null)

可以確定,是這一行資料導致死鎖。從t-sql指令碼看是id=1導致的死鎖。


--【方法二】更簡單!直接select查詢鎖資源
SELECT * FROM [test].[dbo].mytest where %%lockres%% = '1:786:0'


--【現在建立聚集索引的情況】
CREATE CLUSTERED INDEX IX_mytest ON DBO.mytest(name)  
--CREATE NONCLUSTERED INDEX IX_mytest_id ON DBO.mytest(id)  



waitresource=KEY: 7:72057594050445312 (a791659675d9)
上面可以得出的資訊:db_id=7;hobt_id=72057594050445312;keyhashvalue=(a791659675d9)

這下dbcc page()就就很難找了,雖然可以確定是哪個索引,但是確定不了具體鎖資源
SELECT * FROM SYS.partitions WHERE hobt_id=72057594050445312  
SELECT * FROM SYS.indexes WHERE OBJECT_ID= 251147940 AND index_id=1  


--檢視方法和上面rid查詢的一樣
SELECT * FROM [test].[dbo].mytest where %%lockres%% = '(a791659675d9)'

id	name	info
2	mm		NULL

--犧牲品是在等他id=2的行失敗了。
--如果還想看看是哪個資料頁,還是有辦法檢視的!
SELECT %%physloc%%,* FROM [test].[dbo].mytest where %%lockres%% = '(a791659675d9)'
SELECT sys.fn_physlocformatter(0xEE02000001000100)

--或者
select * from dbo.mytest cross apply sys.fn_PhysLocCracker(%%physloc%%)

%%physloc%%能找到資料行的實體地址,函式fn_physlocformatter再將地址解析為(file:page:slot)的格式。
在使用DBCC PAGE()檢視

(注意:%%lockres%%這裡取出的雜湊值是鎖管理的鍵值,與主鍵表的hashkey有出入。待了解!!)

其實確定死鎖的具體資源也沒什麼用,更重要還是確定產生死鎖的物件及指令碼。

通常減少事務死亡的一些辦法去解決:

按同一順序訪問物件

避免事務中的使用者互動

保持事務簡短並處於一個批處理中

使用較低的隔離級別

使用基於行版本控制的隔離級別

使用繫結連線

如果以上沒法更改,試著其他的方法:

1 檢查指令碼是否有優化的空間進行優化

2 確定表中是否有聚集索引,建立聚集索引

3 是否有其他是索引,強制使用有利的索引

4 事務中的語句儘量短、處理少,不要執行太多語句以至時間太長(類似waitfor delay

5 儘量不要在一個事務中重複的讀取和更改相同的資料,能一次讀寫完最好