溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

mysql中更新鎖與排它鎖的示例分析

發(fā)布時(shí)間:2021-09-16 18:08:23 來源:億速云 閱讀:209 作者:小新 欄目:MySQL數(shù)據(jù)庫(kù)

這篇文章將為大家詳細(xì)講解有關(guān)mysql中更新鎖與排它鎖的示例分析,小編覺得挺實(shí)用的,因此分享給大家做個(gè)參考,希望大家閱讀完這篇文章后可以有所收獲。

一直沒有認(rèn)真了解UPDATE操作的鎖,最近在MSDN論壇上看到一個(gè)問題,詢問堆表更新的死鎖問題,問題很簡(jiǎn)單,有類似這樣的表及數(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;

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

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

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

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

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

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

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

mysql中更新鎖與排它鎖的示例分析

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

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

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

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

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

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

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

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

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

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

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

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

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

關(guān)于“mysql中更新鎖與排它鎖的示例分析”這篇文章就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,使各位可以學(xué)到更多知識(shí),如果覺得文章不錯(cuò),請(qǐng)把它分享出去讓更多的人看到。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI