sql server 死鎖總結

昨天去面試,考官問了一個問題:如何處理存儲過程中的死鎖?結果沒答上來,工資減500...            

deadlocks(死鎖)

所謂死鎖<DeadLock>: 是指兩個或兩個以上的進程在執行過程中,因爭奪資源而造成的一種互相等待的現象,若無外力作用,它們都將無法推進下去.此時稱系統處於死鎖狀態或系統產生了死鎖,這些永遠在互相等待的進程稱爲死鎖進程.

由於資源佔用是互斥的,當某個進程提出申請資源後,使得有關進程在無外力協助下,永遠分配不到必需的資源而無法繼續運行,這就產生了一種特殊現象死鎖。

一種情形,此時執行程序中兩個或多個線程發生永久堵塞(等待),每個線程都在等待被其他線程佔用並堵塞了的資源。例如,如果線程A鎖住了記錄1並等待記錄2,而線程B鎖住了記錄2並等待記錄1,這樣兩個線程就發生了死鎖現象。

計算機系統中,如果系統的資源分配策略不當,更常見的可能是程序員寫的程序有錯誤等,則會導致進程因競爭資源不當而產生死鎖的現象。

產生死鎖的原因主要是:

(1) 因爲系統資源不足。

(2) 進程運行推進的順序不合適。

(3) 資源分配不當等。

如果系統資源充足,進程的資源請求都能夠得到滿足,死鎖出現的可能性就很低,否則就會因爭奪有限的資源而陷入死鎖。其次,進程運行推進順序與速度不同,也可能產生死鎖

產生死鎖的四個必要條件:

(1) 互斥條件:一個資源每次只能被一個進程使用。

(2) 請求與保持條件:一個進程因請求資源而阻塞時,對已獲得的資源保持不放。

(3) 不剝奪條件:進程已獲得的資源,在末使用完之前,不能強行剝奪。

(4) 循環等待條件:若干進程之間形成一種頭尾相接的循環等待資源關係。這四個條件是死鎖的必要條件,只要系統發生死鎖,這些條件必然成立,而只要上述條件之 一不滿足,就不會發生死鎖。 

死鎖的解決:預防和解除

1。死鎖的預防:1)一次封鎖法:就是要求每個事務必須一次將所有要使用的數據全部加鎖,否則就不能繼續執行。但是出現這樣的問題:因爲一次給全部數據加鎖,從而降低系統的併發度,同時數據庫中數據是不斷變化的,原來不要求封鎖的數據在執行過程中可能會變成封鎖對象。2)順序封鎖法,預先對數據對象規定一個封鎖順序,所有事務都按這個順序實行封鎖,問題:數據庫中數據對象多,且不斷的變化,維護困難,成本高;事務的封鎖請求可以隨着事務的執行而動態的決定。

2。檢測死鎖

企業管理器->可以在[管理]->[當前活動] 裏看到阻塞信息(有時SQL Server企業管理器會因爲鎖太多而沒有響應).

設定跟蹤1204:

USE MASTER
DBCC TRACEON (
1204,-1)

顯示當前啓用的所有跟蹤標記的狀態:

DBCC TRACESTATUS(-1)

取消跟蹤1204:

DBCC TRACEOFF (1204,-1)

在設定跟蹤1204後,會在數據庫的日誌文件裏顯示SQL Server數據庫死鎖時一些信息。但那些信息很難看懂,需要對照SQL Server聯機叢書仔細來看。根據PAG鎖要找到相關數據庫表的方法:

DBCC TRACEON (3604)
DBCC PAGE (db_id,file_id,page_no)
DBCC TRACEOFF (
3604)

可以使用以下存儲過程來檢測,就可以查出引起死鎖的進程和SQL語句。

寫了一個系統存儲過程sp_who_lock.sql。代碼如下:

 

if exists (select * from dbo.sysobjects
where id = object_id(N'[dbo].[sp_who_lock]')
and OBJECTPROPERTY(id, N'IsProcedure'= 1)
drop procedure [dbo].[sp_who_lock]
GO
/********************************************************
//  說明 : 查看數據庫裏阻塞和死鎖情況
*******************************************************
*/

use master
go
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語句.

我們還可以設置鎖的超時時間(單位是毫秒), 來縮短死鎖可能影響的時間範圍:

例如:

use master
seelct @@lock_timeout
set lock_timeout 900000
-- 15分鐘
seelct @@lock_timeout

 
*********************************************************************************************************

常看到死鎖的問題,一般都是KILL進程,但如果不查出引起死鎖的原因,死鎖會時常發生
可以通過查找引起死鎖的的操作,就可以方便的解決死鎖,現將日常解決問題的方法總結,也許對大家有幫助

1/死鎖發生時,通過如下語法,查詢出引起死鎖的操作

use master
go
declare @spid int,@bl int
DECLARE s_cur CURSOR FOR 
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
OPEN s_cur
FETCH NEXT FROM s_cur INTO @spid,@bl
WHILE @@FETCH_STATUS = 0
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 )
FETCH NEXT FROM s_cur INTO @spid,@bl
end
CLOSE s_cur
DEALLOCATE s_cur

exec sp_who2
2/查找程序/數據庫,此t_sql語法在什麼地方使用
3/分析找到的,並解決問題

EG:

/*
------------------------------------------------------- 
引起數據庫死鎖的是: 71進程號,其執行的SQL語法如下

EventType      Parameters EventInfo                                        
-------------- ---------- ------------------------------------------------ 
Language Event 0          
select * from test
insert test values(1,2)


(所影響的行數爲 1 行)

DBCC 執行完畢。如果 DBCC 輸出了錯誤信息,請與系統管理員聯繫。
                                                                               
------------------------------------------------------------------------------ 
進程號SPID:64被進程號SPID:71阻塞,其當前進程執行的SQL語法如下

EventType      Parameters EventInfo                                        
-------------- ---------- ------------------------------------------------ 
Language Event 0          
select * from test
insert test values(1,2)


(所影響的行數爲 1 行)

DBCC 執行完畢。如果 DBCC 輸出了錯誤信息,請與系統管理員聯繫。
                                                                               
------------------------------------------------------------------------------ 
進程號SPID:65被進程號SPID:64阻塞,其當前進程執行的SQL語法如下

EventType      Parameters EventInfo                                                                                          
-------------- ---------- -------------------------------------------------------------------------------------------------- 
Language Event 0          begin tran
select * from test with (holdlock)
waitfor time '12:00'
select * from test
commit

(所影響的行數爲 1 行)

DBCC 執行完畢。如果 DBCC 輸出了錯誤信息,請與系統管理員聯繫。
                                                                               
------------------------------------------------------------------------------ 
進程號SPID:73被進程號SPID:64阻塞,其當前進程執行的SQL語法如下

EventType      Parameters EventInfo                                                                                          
-------------- ---------- -------------------------------------------------------------------------------------------------- 
Language Event 0          begin tran
select * from test with (holdlock)
waitfor time '12:00'
select * from test
commit

(所影響的行數爲 1 行)

DBCC 執行完畢。如果 DBCC 輸出了錯誤信息,請與系統管理員聯繫。
*/

***************************************************************************************************

解決方法:

  讓用戶(即先讀後寫類型的操作),在select 時就是用Update lock

  語法如下:

select * from table1 with(updlock) where ....
發佈了23 篇原創文章 · 獲贊 0 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章