鋒利的SQL2014 處理死鎖

2021-06-29 06:54:07 字數 4651 閱讀 7141

在兩個或多個任務中,如果每個任務鎖定了其他任務試圖鎖定的資源,此時會造成這些任務永久阻塞,從而出現死鎖。例如:事務a

獲取了行

1的共享鎖。事務b

獲取了行

2的共享鎖。

現在,事務

a請求行

2的排他鎖,但在事務

b完成並釋放其對行

2持有的共享鎖之前被阻塞。

現在,事務

b請求行

1的排他鎖,但在事務

a完成並釋放其對行

1持有的共享鎖之前被阻塞。

事務a必須在事務b完成之後才能完成,但事務b被事務a阻塞。這種情況也稱為迴圈依賴關係:事務a依賴於事務b,而事務b又依賴於事務a,從而形成了乙個迴圈。

除非某個外部程序斷開死鎖,否則死鎖中的兩個事務都將無限期等待下去。資料庫引擎死鎖監視器定期檢查陷入死鎖的任務。如果檢測死鎖,將選擇其中乙個任務作為犧牲品,然後終止其事務並提示錯誤。這樣,其他任務就可以完成其事務,從而解除死鎖。對於事務以錯誤終止的應用程式,它還可以重試該事務,但通常要等到與它一起陷入死鎖的其他事務完成後執行。

1.按同一順序訪問物件

如果所有併發事務按同一順序訪問物件,則發生死鎖的可能性會降低。例如,如果兩個併發事務先獲取supplier表上的鎖,然後獲取part表上的鎖。在其中乙個事務完成之前,另乙個事務將在supplier表上被阻塞。當第1個事務提交或回滾之後,第2個事務將繼續執行,這樣就不會發生死鎖。如果使用儲存過程進行資料修改,則可以使物件的訪問順序標準化。

2.避免事務中的使用者互動

避免編寫包含使用者互動的事務,因為沒有使用者干預的批處理的執行速度遠快於使用者必須手動響應查詢時的速度。例如,如果事務正在等待使用者輸入,而使用者去吃午餐了,那麼使用者就耽誤了事務的完成。這將降低系統的吞吐量,因為事務持有的任何鎖只有在事務提交或回滾後才會釋放。即使不出現死鎖的情況,在占用資源的事務完成之前,訪問同一資源的其他事務也會被阻塞。

3.保持事務簡短並處於乙個批處理中

在同一資料庫中,併發執行多個需要長時間執行的事務時通常會發生死鎖。事務的執行時間越長,它持有排他鎖或更新鎖的時間也就越長,從而會阻塞其他活動並可能導致死鎖。

4.使用較低的隔離級別

確定事務是否能在較低的隔離級別上執行。實現已提交讀允許事務讀取另乙個事務已讀取(未修改)的資料,而不必等待第1個事務完成。使用較低的隔離級別(例如已提交讀)比使用較高的隔離級別(例如可序列化)持有共享鎖的時間更短。這樣就減少了鎖爭用。

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

如果將read_committed_snapshot資料庫選項設定為on,則在已提交讀隔離級別下執行的事務在讀操作期間將使用行版本控制而不是共享鎖。

在try...catch構造的catch塊可以捕獲1205死鎖錯誤,發生錯誤後,可以通過回滾事務來解除鎖定。下面的語句建立了用於說明死鎖狀態的表和用於列印錯誤資訊的儲存過程。

use adventureworks;

go -- 驗證表是否已經存在

if object_id(n'my_sales',n'u') is not null

drop table my_sales;

go -- 建立表並插入資料

create table my_sales

( itemid      int primary key,

sales       int not null

); go

insert my_sales (itemid,sales) values (1, 1);

insert my_sales (itemid,sales) values (2, 1);

go -- 驗證儲存過程是否已經存在

if object_id(n'usp_myerrorlog',n'p') is not null

drop procedure usp_myerrorlog;

go -- 建立儲存過程,用於輸出錯誤訊息

create procedure usp_myerrorlog

as print

n'錯誤 ' +convert(varchar(50), error_number()) +

n', 嚴重級別 ' +convert(varchar(5), error_severity()) +

n', 狀態 ' +convert(varchar(5), error_state()) +

n', 行 ' +convert(varchar(5), error_line());

print

error_message();

下面的會話1和會話2**指令碼在兩個單獨的sql server management studio連線下同時執行。兩個會話都嘗試更新表中的相同行。在第一次嘗試過程中,其中乙個會話將成功完成更新操作,而另乙個會話將被選擇為死鎖犧牲品。死鎖犧牲品錯誤將使執行跳至catch塊,事務將進入無法提交狀態。在catch塊中,死鎖犧牲品會回滾事務並重試更新此表,直到更新成功或達到了重試限制。

會話1

會話2

use adventureworks;

go-- 定義並設定變數,指定嘗試提交更新的次數

declare @retry int;

set @retry = 5;

-- 如果被作為了死鎖犧牲品,保持嘗試更新

while (@retry > 0)

begin

begin try

begin transaction;

update my_sales

set sales = sales + 1

where itemid = 1;

-- 延時等待,此時itemid為1和2的行

-- 在沒有提交前,都無法釋放鎖

waitfor delay '00:00:13';

update my_sales

set sales = sales + 1

where itemid = 2;

set @retry = 0;

commit transaction;

end try

begin catch

-- 檢測錯誤編號,如果是死鎖犧牲品,

-- 則減少重新嘗試計數。如果是其他

-- 錯誤,則退出while迴圈

if (error_number() = 1205)

set @retry = @retry - 1;

else

set @retry = -1;

-- 輸出錯誤訊息

execute usp_myerrorlog;

-- 會話中包含無法提交的事務

-- xact_state將返回 -1

if xact_state() <> 0

rollback transaction;

end catch;

end; -- 結束while迴圈

use adventureworks;

go-- 定義並設定變數,指定嘗試提交更新的次數

declare @retry int;

set @retry = 5;

-- 如果被作為了死鎖犧牲品,保持嘗試更新

while (@retry > 0)

begin

begin try

begin transaction;

update my_sales

set sales = sales + 1

where itemid = 2;

-- 延時等待,此時itemid為1和2的行

-- 在沒有提交前,都無法釋放鎖

waitfor delay '00:00:07';

update my_sales

set sales = sales + 1

where itemid = 1;

set @retry = 0;

commit transaction;

end try

begin catch

-- 檢測錯誤編號,如果是死鎖犧牲品,

-- 則減少重新嘗試計數。如果是其他

-- 錯誤,則退出while迴圈

if (error_number() = 1205)

set @retry = @retry - 1;

else

set @retry = -1;

-- 輸出錯誤訊息

execute usp_myerrorlog;

-- 會話中包含無法提交的事務

-- xact_state將返回 -1

if xact_state() <> 0

rollback transaction;

end catch;

end; -- 結束while迴圈

下面是會話1中返回的訊息,表示兩行都已經被更新。

(1 行受影響) 

(1 行受影響)

下面是會話2中返回的訊息,會話2被作為了死鎖犧牲品。

(1 行受影響)                    -- 由於死鎖,會話2事務中只成功更新1行,這時會發生回滾操作

錯誤 1205, 嚴重級別 13, 狀態 51, 行18   -- 由儲存過程usp_myerrorlog輸出的錯誤訊息

事務(程序 id52)與另乙個程序被死鎖在鎖資源上,並且已被選作死鎖犧牲品。請重新執行該事務。 -- sql server的提示

(1 行受影響)  -- 本行和下行訊息是重新嘗試更新後得到的提示訊息

(1 行受影響)

SQL2014學習筆記3 記憶體優化表的事務

本想寫記憶體表的備份和還原,結果寫到一半發現涉及到事務的部分太多,所以單開一篇寫事務,篇幅會比較短,但這是理解記憶體表備份還原機制的基礎。記憶體表分兩種,一種是只存在於記憶體中,和磁碟 日誌都沒半毛錢關係的記憶體表,這種表在備份還原時只會處理表結構而不處理資料。類似於tempdb中的表,不同之處就是...

SQL 資料庫 質疑後 情況1 處理

資料庫附加時出現錯誤 錯誤 3624 recovery.c 2440 seenckptend spid 54.processid 792 1.新建乙個同名的資料庫 2.再停掉sql server 注意不要分離資料庫 3.用原資料庫的資料檔案覆蓋掉這個新建的資料庫 4.再重啟sql server 5....

Sql2000 處理排名中存在並列排名的問題

其實這個問題不難,不過今天碰上了,就記下來,權當做筆記,呵呵。案例,做乙個奧運排行榜,根據金,銀,銅牌的獎牌數來排名,3種獎牌相同的時候排名並列。只有乙個表,表結構如下 create table t medal country varchar 50 primary key,國家名 glod int,...