減少SQL Server死鎖的方法

kitesky發表於2009-12-14
鎖是指在某組資源中,兩個或兩個以上的執行緒在執行過程中,在爭奪某一資源時而造成互相等待的現象,若無外力的作用下,它們都將無法推進下去,死時就可能會產生死鎖,這些永遠在互相等待的程式稱為死鎖執行緒。簡單的說,程式A等待程式B釋放他的資源,B又等待A釋放他的資源,這樣互相等待就形成死鎖。 [@more@]

鎖是指在某組資源中,兩個或兩個以上的執行緒在執行過程中,在爭奪某一資源時而造成互相等待的現象,若無外力的作用下,它們都將無法推進下去,死時就可能會產生死鎖,這些永遠在互相等待的程式稱為死鎖執行緒。簡單的說,程式A等待程式B釋放他的資源,B又等待A釋放他的資源,這樣互相等待就形成死鎖。

如在資料庫中,如果需要對一條資料進行修改,首先資料庫管理系統會在上面加鎖,以保證在同一時間只有一個事務能進行修改操作。如事務1的執行緒 T1具有表A上的排它鎖,事務2的執行緒T2 具有表B上的排它鎖,並且之後需要表A上的鎖。事務2無法獲得這一鎖,因為事務1已擁有它。事務2被阻塞,等待事務1。然後,事務1需要表B的鎖,但無法獲得鎖,因為事務2將它鎖定了。事務在提交或回滾之前不能釋放持有的鎖。因為事務需要對方控制的鎖才能繼續操作,所以它們不能提交或回滾,這樣資料庫就會發生死鎖了。

如在編寫儲存過程的時候,由於有些儲存過程事務性的操作比較頻繁,如果先鎖住表A,再鎖住表B,那麼在所有的儲存過程中都要按照這個順序來鎖定它們。如果無意中某個儲存過程中先鎖定表B,再鎖定表A,這可能就會導致一個死鎖。而且死鎖一般是不太容易被發現的。

如果伺服器上經常出現這種死鎖情況,就會降低伺服器的效能,所以應用程式在使用的時候,我們就需要對其進行跟蹤,使用sp_who和sp_who2來確定可能是哪些使用者阻塞了其他使用者,我們還可以用下面的儲存過程來跟蹤具體的死鎖執行的影響:

create procedure sp_who_lock

as

begin

declare @spid int,@bl int,

@intTransactionCountOnEntry int,

@intRowcount int,

@intCountProperties int,

@intCounter int

create table #tmp_lock_who (id int identity(1,1),spid smallint,bl smallint)

IF @@ERROR<>0 RETURN @@ERROR

insert into #tmp_lock_who(spid,bl) select 0 ,blocked

from (select * from sysprocesses where blocked>0 ) a

where not exists(select * from (select * from sysprocesses where blocked>0 ) b
where a.blocked=spid)

union select spid,blocked from sysprocesses where blocked>0

IF @@ERROR<>0 RETURN @@ERROR

-- 找到臨時表的記錄數

select @intCountProperties = Count(*),@intCounter = 1

from #tmp_lock_who

IF @@ERROR<>0 RETURN @@ERROR

if @intCountProperties=0

select ’現在沒有阻塞和死鎖資訊’ as message

-- 迴圈開始

while @intCounter <= @intCountProperties

begin

-- 取第一條記錄

select @spid = spid,@bl = bl

from #tmp_lock_who where id = @intCounter
begin

if @spid =0

select ’引起資料庫死鎖的是: ’+ CAST(@bl AS VARCHAR(10)) + ’程式號,其執行的SQL語法如下’

else

select ’程式號SPID:’+ CAST(@spid AS VARCHAR(10))+ ’被’ + ’程式號SPID:’+ CAST(@bl AS VARCHAR(10)) +’阻塞,其當前程式執行的SQL語法如下’

DBCC INPUTBUFFER (@bl )

end

-- 迴圈指標下移

set @intCounter = @intCounter + 1

end

drop table #tmp_lock_who

return 0

end

我們只需要透過在查詢分析器裡面執行sp_who_lock,就可以具體捕捉到執行的堵塞程式,這時我們就可以對對應的SQL語句或者儲存過程進行效能上面的改進及設計。 [Page]

所以我們在資料庫設計的時候,雖然不能完全避免死鎖,但可以使死鎖的數量儘量減少。增加事務的吞吐量並減少系統開銷,因為只有很少的事務,所以就得遵循下面的原則:

按同一順序訪問物件

如果所有併發事務按同一順序訪問物件,則發生死鎖的可能性會降低。在寫SQL語句或儲存過程的時候,就需要按照順序在兩個併發事務中先獲得表A上的鎖,然後獲得表B上的鎖,當第一個事務完成之前,另一個事務被阻塞在表A上。第一個事務提交或回滾後,第二個事務繼續進行,而不能在語句裡面寫先獲得表B上的鎖,然後再獲得表A的鎖。

避免事務中的使用者互動

避免編寫包含使用者互動的事務,因為執行沒有使用者互動的批處理的速度要遠遠快於使用者手動響應查詢的速度,例如答覆應用程式請求引數的提示。例如,如果事務正在等待使用者輸入,而使用者就去做別的事了,則使用者將此事務掛起使之不能完成。這樣將降低系統的吞吐量,因為事務持有的任何鎖只有在事務提交或回滾時才會釋放。即使不出現死鎖的情況,訪問同一資源的其它事務也會被阻塞,等待該事務完成。

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

在同一資料庫中併發執行多個需要長時間執行的事務時通常發生死鎖。事務執行時間越長,其持有排它鎖或更新鎖的時間也就越長,從而堵塞了其它活動並可能導致死鎖。保持事務在一個批處理中,可以最小化事務的網路通訊往返量,減少完成事務可能的延遲並釋放鎖。

使用低隔離級別

確定事務是否能在更低的隔離級別上執行。執行提交讀允許事務讀取另一個事務已讀取(未修改)的資料,而不必等待第一個事務完成。使用較低的隔離級別(例如提交讀)而不使用較高的隔離級別(例如可序列讀)可以縮短持有共享鎖的時間,從而降低了鎖定爭奪。

使用繫結連線

使用繫結連線使同一應用程式所開啟的兩個或多個連線可以相互合作。次級連線所獲得的任何鎖可以象由主連線獲得的鎖那樣持有,反之亦然,因此不會相互阻塞。

下面有一些對死鎖發生的一些建議:

1)對於頻繁使用的表使用集簇化的索引;

2)設法避免一次性影響大量記錄的T-SQL語句,特別是INSERT和UPDATE語句;

3)設法讓UPDATE和DELETE語句使用索引;

4)使用巢狀事務時,避擴音交和回退衝突;

5)對一些資料不需要及時讀取更新值的表在寫SQL的時候在表後臺加上(nolock),如:Select * from tableA(nolock) 。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/66009/viewspace-1029648/,如需轉載,請註明出處,否則將追究法律責任。

相關文章