初步了解更新锁(U)与排它锁(X)
??
一直沒(méi)有認(rèn)真了解UPDATE操作的鎖。近期在MSDN論壇上看到一個(gè)問(wèn)題,詢問(wèn)堆表更新的死鎖問(wèn)題,問(wèn)題非常easy,有相似這種表及數(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;
在查詢一中運(yùn)行更新操作:
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;
在查詢一運(yùn)行開(kāi)始后,立即在查詢二中運(yùn)行以下的操作
BEGIN TRAN
UPDATE dbo.tb SET c2 = 'xx' WHERE c1 = 1;
ROLLBACK;
為什么會(huì)出現(xiàn)死鎖,假設(shè)條件改為 c1 = 4 則不會(huì)死鎖。
開(kāi)始的時(shí)候想得比較簡(jiǎn)單,死鎖的表現(xiàn)是形成循環(huán)等待(對(duì)于兩個(gè)查詢而言,能夠簡(jiǎn)單地覺(jué)得就是在相互等待對(duì)方鎖定資源的釋放)。
對(duì)于這個(gè)樣例而言。第一個(gè)查詢更新兩次,會(huì)先更新并鎖定一條記錄,然后等待第二個(gè)更新。但第二個(gè)查詢僅僅會(huì)更新一條記錄。它要么與第一個(gè)查詢沖突,無(wú)法獲得鎖。須要等待查詢一完畢,這個(gè)時(shí)候它并沒(méi)有鎖定什么;要么能夠獲得鎖,完畢更新。
似乎不應(yīng)該會(huì)出現(xiàn)死鎖,死鎖會(huì)不會(huì)是其它原因?qū)е隆?/p>
在自己的電腦上簡(jiǎn)單測(cè)試了一下。似乎也確實(shí)沒(méi)有死鎖。
但后面通過(guò)Profile跟蹤更新操作的下鎖情況才發(fā)現(xiàn)。自己的分析大錯(cuò)特錯(cuò)了。
主要原因在于沒(méi)有正確理解更新操作是怎樣用鎖的。
在聯(lián)機(jī)幫助上“鎖模式”中有關(guān)于更新的U(更新鎖)和X(排它鎖)的說(shuō)明
http://msdn.microsoft.com/zh-cn/library/ms175519(v=sql.105).aspx
只是說(shuō)得確實(shí)挺模糊的。里面還提到了S鎖。我一直以為是查詢數(shù)據(jù)過(guò)程中用的S鎖(也 SELECT 一樣)。找到滿足條件的記錄后用U鎖,再轉(zhuǎn)換為X鎖做更新。
Profile(事件探查器)跟蹤的結(jié)果讓我知道了這是一個(gè)錯(cuò)誤的理解,在Profile中新建一個(gè)跟蹤,選擇Locks中的Lock:Acquired (加鎖),Lock:Acquired(釋放鎖)解兩個(gè)事件,在篩選中設(shè)置僅僅跟蹤測(cè)試用的查詢窗體相應(yīng)的spid(能夠運(yùn)行 PRINT @@SPID 獲得),然后運(yùn)行一個(gè)更新語(yǔ)句。比方 UPDATE dbo.tb SET c2 = 'xx' WHERE c1 = 3
在Profile中能夠看到。對(duì)于每條記錄都有加 U 鎖的操作,對(duì)于不滿足條件的記錄,會(huì)立即釋放U鎖;對(duì)于滿足條件的記錄,終于轉(zhuǎn)換為X鎖。例如以下圖所看到的。
注意一下,在這個(gè)跟蹤結(jié)果里面。并沒(méi)有出現(xiàn)S鎖。
另外學(xué)做了一些測(cè)試:
通過(guò)加大記錄量做更新測(cè)試,會(huì)發(fā)現(xiàn)數(shù)據(jù)掃描涉及的記錄都有U鎖,并不限于更新記錄所在的頁(yè)。這從還有一個(gè)角度說(shuō)明了大表中Scan 可怕。
當(dāng)使用索引Scan的時(shí)候,也會(huì)通過(guò)跟蹤發(fā)現(xiàn)所Scan的索引資源有U鎖,假設(shè)更新不涉及索引變化。那以僅僅會(huì)相應(yīng)的記錄有U轉(zhuǎn)X鎖。索引的U鎖會(huì)釋放;假設(shè)影響索引,那么索引的U鎖會(huì)轉(zhuǎn)X鎖。
刪除操作與更新操作相似
使用 UPDATE aSET c2 = 'xx' FROM dbo.tb AS a WITH(NOLOCK) WHERE c1 = 3 的加鎖情況是一樣的。 并不會(huì)由于NOLOCK的提示而不加 U 或者 X 鎖
最后回頭研究一下演示樣例中的死鎖問(wèn)題:
對(duì)于查詢一,第一個(gè)更新依次掃描表中全部記錄,對(duì)于每條記錄,加 U 鎖,推斷是否符合更新條件。假設(shè)符合,轉(zhuǎn)換為 X 鎖;假設(shè)不符合條件。釋放 U 鎖。第一個(gè)更新完畢的時(shí)候,查詢一鎖定了一條記錄(由于事務(wù)未完畢,所以鎖一直保持),然后等待第二個(gè)更新
對(duì)于查詢二,依次掃描表中的每條記錄(與前面的更新一樣),假設(shè)它更新的記錄在查詢一更新的記錄前被掃描到,那么這條記錄也會(huì)變成 X 鎖;當(dāng)繼續(xù)并進(jìn)行到查詢一的X鎖記錄的零點(diǎn),U 與 X 沖突,無(wú)法繼續(xù),這時(shí)候查詢二等待查詢一釋放鎖
查詢一的第二個(gè)更新開(kāi)始運(yùn)行。依次掃描每條記錄。同一個(gè)事務(wù)內(nèi)不會(huì)有沖突。所以它不會(huì)與自己之前鎖定的記錄有沖突,但進(jìn)行到查詢二鎖定的記錄的時(shí)候,它也無(wú)法獲得 U 鎖。它須要等待查詢二釋放資源。
這個(gè)時(shí)候就形成了相互等待,符合死鎖條件
假設(shè)查詢二須要更新的記錄在查詢一的第一個(gè)更新記錄之后。則不會(huì)有死鎖。由于查詢二在掃描到查詢一第一個(gè)更新的記錄時(shí)就會(huì)由于鎖沖突等待了,這個(gè)時(shí)候它沒(méi)有對(duì)不論什么記錄設(shè)置與查詢一的操作有沖突的鎖。我自己測(cè)試的時(shí)候沒(méi)有死鎖,就是這種情況。
注意這里面提到的順序。是數(shù)據(jù)讀取的順序,不一定與存儲(chǔ)順序一樣,磁盤上記錄的順序也不一定與INSERT的記錄順序一樣,這也是我用相同條件沒(méi)有測(cè)試出死鎖的原因(我的環(huán)境中,恰好讀出的順序與INSERT的順序不一樣)
更新時(shí),記錄讀取的順序,能夠通過(guò)Profile跟蹤的Lock:Acquired (加鎖)事件來(lái)看。涉及大量數(shù)據(jù)時(shí),假設(shè)server支持。還會(huì)有并發(fā)讀取。這也是分析死鎖時(shí)要考慮的因素
??
總結(jié)
以上是生活随笔為你收集整理的初步了解更新锁(U)与排它锁(X)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: cad无法复制粘贴解决方法
- 下一篇: 关于 Orbeon form PE 版本