明輝手游網(wǎng)中心:是一個免費(fèi)提供流行視頻軟件教程、在線學(xué)習(xí)分享的學(xué)習(xí)平臺!

講解更新鎖(U)與排它鎖(X)的相關(guān)知識

[摘要]一直沒有認(rèn)真了解UPDATE操作的鎖,最近在MSDN論壇上看到一個問題,詢問堆表更新的死鎖問題,問題很簡單,有類似這樣的表及數(shù)據(jù):CREATE TABLE dbo.tb(c1 int,c2 char...

一直沒有認(rèn)真了解UPDATE操作的鎖,最近在MSDN論壇上看到一個問題,詢問堆表更新的死鎖問題,問題很簡單,有類似這樣的表及數(shù)據(jù):

CREATE TABLE dbo.tb(
     c1 int,
     c2 char(10),
     c3 varchar(10)
);
GO
DECLARE @id int;
SET @id = 0;
WHILE @id <5
BEGIN;
     SET @id = @id + 1;
     INSERT dbo.tb VALUES( @id, 'b' + RIGHT(10000 + @id, 4), 'c' + RIGHT(100000 + @id, 4) );
END;

在查詢一中執(zhí)行更新操作:

BEGIN TRAN
UPDATE dbo.tb SET c2 = 'xx' WHERE c1 = 2;
WAITFOR DELAY '00:00:30';
UPDATE dbo.tb SET c2 = 'xx' WHERE c1 = 5;
ROLLBACK;

在查詢一執(zhí)行開始后,馬上在查詢二中執(zhí)行下面的操作

BEGIN TRAN
UPDATE dbo.tb SET c2 = 'xx' WHERE c1 = 1;
ROLLBACK;

為什么會出現(xiàn)死鎖,如果條件改為 c1 = 4 則不會死鎖。

開始的時(shí)候想得比較簡單,死鎖的表現(xiàn)是形成循環(huán)等待(對于兩個查詢而言,可以簡單地認(rèn)為就是在相互等待對方鎖定資源的釋放)。

對于這個例子而言,第一個查詢更新兩次,會先更新并鎖定一條記錄,然后等待第二個更新;但第二個查詢只會更新一條記錄,它要么與第一個查詢沖突,無法獲得鎖,需要等待查詢一完成,這個時(shí)候它并沒有鎖定什么;要么能夠獲得鎖,完成更新。似乎不應(yīng)該會出現(xiàn)死鎖,死鎖會不會是其他原因?qū)е隆?/span>

在自己的電腦上簡單測試了一下,似乎也確實(shí)沒有死鎖。

但后面通過Profile跟蹤更新操作的下鎖情況才發(fā)現(xiàn),自己的分析大錯特錯了。主要原因在于沒有正確理解更新操作是如何用鎖的。

在聯(lián)機(jī)幫助上鎖模式中有關(guān)于更新的U(更新鎖)和X(排它鎖)的說明

http://msdn.microsoft.com/zh-cn/library/ms175519(v=sql.105).aspx

不過說得確實(shí)挺模糊的,里面還提到了S鎖,我一直以為是查詢數(shù)據(jù)過程中用的S鎖(也 SELECT 一樣),找到滿足條件的記錄后用U鎖,再轉(zhuǎn)換為X鎖做更新。

Profile(事件探查器)跟蹤的結(jié)果讓我知道了這是一個錯誤的理解,在Profile中新建一個跟蹤,選擇Locks中的Lock:Acquired(加鎖),Lock:Acquired(釋放鎖)解兩個事件,在篩選中設(shè)置只跟蹤測試用的查詢窗口對應(yīng)的spid(可以執(zhí)行 PRINT @@SPID獲得),然后執(zhí)行一個更新語句,比如 UPDATE dbo.tb SET c2 = 'xx' WHERE c1 = 3

在Profile中可以看到,對于每條記錄都有加 U 鎖的操作,對于不滿足條件的記錄,會馬上釋放U鎖;對于滿足條件的記錄,最終轉(zhuǎn)換為X鎖。如下圖所示。

講解更新鎖(U)與排它鎖(X)的相關(guān)知識




注意一下,在這個跟蹤結(jié)果里面,并沒有出現(xiàn)S鎖。

另外學(xué)做了一些測試:


  1. 通過加大記錄量做更新測試,會發(fā)現(xiàn)數(shù)據(jù)掃描涉及的記錄都有U鎖,并不限于更新記錄所在的頁。這從另一個角度說明了大表中Scan 可怕。

  2. 當(dāng)使用索引Scan的時(shí)候,也會通過跟蹤發(fā)現(xiàn)所Scan的索引資源有U鎖,如果更新不涉及索引變化,那以只會對應(yīng)的記錄有U轉(zhuǎn)X鎖,索引的U鎖會釋放;如果影響索引,那么索引的U鎖會轉(zhuǎn)X鎖。

  3. 刪除操作與更新操作類似

  4. 使用 UPDATE aSET c2 = 'xx' FROM dbo.tb AS a WITH(NOLOCK) WHERE c1 = 3 的加鎖情況是一樣的, 并不會因?yàn)镹OLOCK的提示而不加 U 或者 X 鎖

最后回頭研究一下示例中的死鎖問題:

  • 對于查詢一,第一個更新依次掃描表中所有記錄,對于每條記錄,加 U 鎖,判斷是否符合更新條件,如果符合,轉(zhuǎn)換為 X 鎖;如果不符合條件,釋放 U 鎖。第一個更新完成的時(shí)候,查詢一鎖定了一條記錄(由于事務(wù)未完成,所以鎖一直保持),然后等待第二個更新

  • 對于查詢二,依次掃描表中的每條記錄(與前面的更新一樣),如果它更新的記錄在查詢一更新的記錄前被掃描到,那么這條記錄也會變成 X 鎖;當(dāng)繼續(xù)并進(jìn)行到查詢一的X鎖記錄的零點(diǎn),U 與 X 沖突,無法繼續(xù),這時(shí)候查詢二等待查詢一釋放鎖

  • 查詢一的第二個更新開始執(zhí)行,依次掃描每條記錄,同一個事務(wù)內(nèi)不會有沖突,所以它不會與自己之前鎖定的記錄有沖突,但進(jìn)行到查詢二鎖定的記錄的時(shí)候,它也無法獲得 U 鎖,它需要等待查詢二釋放資源。這個時(shí)候就形成了相互等待,符合死鎖條件

  • 如果查詢二需要更新的記錄在查詢一的第一個更新記錄之后,則不會有死鎖,因?yàn)椴樵兌趻呙璧讲樵円坏谝粋更新的記錄時(shí)就會因?yàn)殒i沖突等待了,這個時(shí)候它沒有對任何記錄設(shè)置與查詢一的操作有沖突的鎖。我自己測試的時(shí)候沒有死鎖,就是這種情況。

    注意這里面提到的順序,是數(shù)據(jù)讀取的順序,不一定與存儲順序一樣,磁盤上記錄的順序也不一定與INSERT的記錄順序一樣,這也是我用同樣條件沒有測試出死鎖的原因(我的環(huán)境中,恰好讀出的順序與INSERT的順序不一樣)

更新時(shí),記錄讀取的順序,可以通過Profile跟蹤的Lock:Acquired (加鎖)事件來看,涉及大量數(shù)據(jù)時(shí),如果服務(wù)器支持,還會有并發(fā)讀取。這也是分析死鎖時(shí)要考慮的因素

本文講解了講解更新鎖(U)與排它鎖(X)的相關(guān)知識,更多相關(guān)內(nèi)容請關(guān)注php中文網(wǎng)。

相關(guān)推薦:

SQL Server 2008 處理隱式數(shù)據(jù)類型轉(zhuǎn)換在執(zhí)行計(jì)劃中的增強(qiáng)

如何讓MySQL中單句實(shí)現(xiàn)無限層次父子關(guān)系查詢

帶進(jìn)度的SQL Server FileStream如何存取

??

以上就是講解更新鎖(U)與排它鎖(X)的相關(guān)知識的詳細(xì)內(nèi)容,更多請關(guān)注php中文網(wǎng)其它相關(guān)文章!


學(xué)習(xí)教程快速掌握從入門到精通的SQL知識。