1. 程式人生 > >關於兩個update語句互相死鎖的顯現,加深我們對鎖的瞭解

關於兩個update語句互相死鎖的顯現,加深我們對鎖的瞭解

前段時間在msdn的論壇上看到鄒老大對一個問題的回覆,覺得對鎖更瞭解了,先二話不說“拿來”記錄學習下。

原帖地址:http://social.msdn.microsoft.com/Forums/zh-CN/6559504d-c546-45a6-89e2-eeb75041b3e7/-?forum=sqlserverzhchs

首先是環境指令碼

CREATE TABLE [dbo].[table1](

 [A] [nvarchar](10) NULL,

 [B] [nvarchar](10) NOT NULL,

 [C] [nvarchar](10) NULL

) ON [PRIMARY]

GO

INSERT [dbo].[table1] ([A], [B], [C]) VALUES (N'aa1', N'b1', N'11')

INSERT [dbo].[table1] ([A], [B], [C]) VALUES (N'aa2', N'b3', N'11')

INSERT [dbo].[table1] ([A], [B], [C]) VALUES (N'aa3', N'b4', N'11')

INSERT [dbo].[table1] ([A], [B], [C]) VALUES (N'aa3', N'b5', N'11')

INSERT [dbo].[table1] ([A], [B], [C]) VALUES (N'aa3', N'b2', N'11')

INSERT [dbo].[table1] ([A], [B], [C]) VALUES (N'aa3', N'b6', N'11')

INSERT [dbo].[table1] ([A], [B], [C]) VALUES (N'aa3', N'b7', N'11')

INSERT [dbo].[table1] ([A], [B], [C]) VALUES (N'aa3', N'b8', N'11')

INSERT [dbo].[table1] ([A], [B], [C]) VALUES (N'aa1', N'b9', N'11')


然後是三個指令碼

--查詢1

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED

 begin tran

    print convert(nvarchar(30),convert(datetime,getdate(),121),121)

       update table1

     set A='aa1'

     where B='b3'

    print convert(nvarchar(30),convert(datetime,getdate(),121),121)

    EXEC sp_lock @@spid

   waitfor  delay '00:00:10'

   update table1

     set A='aa2'

     where B='b8'

     EXEC sp_lock @@spid

    print convert(nvarchar(30),convert(datetime,getdate(),121),121)

 commit tran

--查詢二

SET TRANSACTION ISOLATION LEVEL Read UNCOMMITTED

    begin tran

  update table1

    set A='aa3'

    where B='b7'

    EXEC sp_lock @@spid

   commit tran

--查詢三:

SET TRANSACTION ISOLATION LEVEL Read UNCOMMITTED

    begin tran

  update table1

    set A='aa3'

    where B='b1'

    EXEC sp_lock @@spid

   commit tran

執行的情況是在兩個執行緒中

情況1:

先執行查詢1,然後立刻在另外一個執行緒中執行查詢2。

結果是兩邊順利的完成更新

情況2:

先執行查詢1,然後立刻在另外一個執行緒中執行查詢3.

結果是查詢3出現死鎖情況。

以上是現象

---------------------------------------------------------------------------------------------

下面我們來分析為什麼會有這個現象,首先我們先來看看update動作到底會做哪些事.

我們先開啟profiler,監控lock:acquired以及lock:released兩個專案,並且限制suid為我們需要監控的程序ID,然後我們得到下面的監控結果

我們可以得出這樣一個結論:

Update過程中對錶進行掃描依此對每行記錄下U鎖,若滿足條件則轉換為X鎖更新,若不滿足條件則釋放U鎖。由此結論,我們就可以推斷出上面分別導致死鎖,和不會導致死鎖的情況是怎樣的原因了。

情況1:

查詢1:對錶進行掃描依此對每行記錄下U鎖,若滿足條件則轉換為X鎖更新,若不滿足條件則釋放U鎖。更新完成後,若未提交則X鎖繼續保留。

注意:這裡被保留的X鎖的行數為第2行(b3),因為這個是一個堆表,排列順序一般是按照插入順序,update掃描該表時候從從第一開始掃描的


查詢2:對錶進行掃描一次對每行記錄下U鎖,因為需要查詢的目標在查詢1的X鎖之後,未能查詢到需要更新得條件既被髮生無法新增U鎖等待,這樣只有當查詢1完成查詢後,查詢2才能繼續,所以不會導致死鎖。

注意:正因為查詢2要滿足X鎖的條件在第7行以(b7),所以當查詢2的U鎖獲取再釋放動作,到了第二行的時候,遇到查詢1保留的X鎖,整個事務進入等待狀態,並且未留下任何可以干擾查詢1的第二個語句的鎖,所以,當查詢1直接完畢後,查詢2得以繼續正常執行完成

情況2:

查詢1:對錶進行掃描依此對每行記錄下U鎖,若滿足條件則轉換為X鎖更新,若不滿足條件則釋放U鎖。更新完成後,若未提交則X鎖繼續保留。

查詢3:對錶進行掃描依此對每行記錄下U鎖,因為需要update的條件在查詢1前就被發現並加上X鎖,但是掃描到查詢1鎖新增的X鎖後無法新增U鎖導致需要等待查詢1完成後才能繼續提交。這時查詢1的第二個update語句開始執行,更新U鎖時,發現查詢2的X,要等待查詢3的X鎖釋放同時達成死鎖條件。

注意:正因為查詢3的新增X鎖動作,在第一行就發生了(b1),然後達到第二行是,又被查詢1保留的X鎖阻止了繼續操作第二行(b3)。這樣他就需要等待查詢1釋放在第二行的X鎖才能繼續update,但是查詢1的第二個語句,又需要查詢3先釋放在第一行(b1)的X鎖,就這樣形成了死鎖條件。

如此這個在 同一頁面,不同的行的互相死鎖,和不死鎖的原因就相當清楚了。

這個案例重點就是,update是一個兩個動作的事務,分為查詢和修改,並且修改這個動作是通過對各個滿足條件的行做X鎖來保證一致性的

而這個動作當遇到併發的時候,即使是不同的行也可能導致互相死鎖。

通過這個案例我們應該得出一個結論,就是應該避免對一個表的多執行緒操作。因為他們很可能造成死鎖。

--------------------------------------------------------------------------------------------

以下是延伸研究:

        那麼現在又一個問題,若update動作會對整個表進行掃描,並一一進行U鎖獲取釋放動作,這必然會造成大量效能消耗,

是否大資料的的情況會不同呢?增加索引是否會改善呢?增加主鍵是否會改善呢?

我們進行以下實驗

1、不增加主鍵或索引,僅對錶擴大資料量,然後update其中第1000行記錄

--環境建立語句
Create Table dbo.a2
(
id int identity(1,1),
value1 char(10),
value2 varchar(20)
)

declare @n int
set @n=1
--測試資料填充
while (@n<10000)
begin
INSERT INTO dbo.a2
           (value1       ,value2
           )
     VALUES
           (REPLICATE('c',10)
           ,CONVERT(varchar(20),REPLICATE('F',20))
           )
set @[email protected]+1
end

產生的鎖和釋放數量和行數*2差距不大,這讓我們直接確認了表掃描的可怕

2、增加主鍵,並擴大資料量

    建立一個表,int列為自增主鍵,插入1W筆資料,使用主鍵update其中第1000行記錄

  

這次我們可以看到整個事務中根本不使用U鎖了,而是先通過S鎖,定位到具體頁面,再直接鎖定對應行,實現更新。

3、不新增主鍵,而是查詢表新增索引,資料量10000


Create Table dbo.a3
(
id int identity(1,1),
value1 char(10),
value2 varchar(20)
)
create index ix_a3 on a3 (id)

declare @n int
set @n=1

while (@n<10000)
begin
INSERT INTO dbo.a3
           (value1       ,value2
           )
     VALUES
           (REPLICATE('c',10)
           ,CONVERT(varchar(20),REPLICATE('F',20))
           )
set @[email protected]+1
end

其中1:18051是索引頁,

這個查詢結果行數大約80行,這個是輸出結果的末尾,上面反覆出現S鎖的釋放。

我們可以判斷這個是通過查詢索引,定位到 索引對應行以後, 再對對應資料行進行U鎖新增釋放動作。注意你可以看到這裡有一個對索引頁的IU動作,這裡是在確定是否該更改會影響到索引頁,因為沒有使索引頁發生更變,從而釋放了這個IU鎖。

相關推薦

關於update語句互相顯現加深我們瞭解

前段時間在msdn的論壇上看到鄒老大對一個問題的回覆,覺得對鎖更瞭解了,先二話不說“拿來”記錄學習下。 原帖地址:http://social.msdn.microsoft.com/Forums/zh-CN/6559504d-c546-45a6-89e2-eeb75041b3

mysql的備份語句

故障 inno trigge mysqld 日誌 trigger tran 兩個 triggers 適合多引擎混合(例如:myisam與innodb混合)的備份命令如下: mysqldump -A -R --triggers --master-data=2 --single

ReactNative連續顯示modal,IOS卡問題

筆者在進行開發的過程發現一個bug,就是點選一個modal後,進行網路請求之後根據業務邏輯需要再顯示一個modal, 但是這個modal死活顯示不出來,但是Android上就沒有問題,一開始以為是邏輯問題,但是檢查了好幾遍都沒有發現邏輯問題。後來經過嘗試,在一個blog中找到了解決方法:

如果類希望互相呼叫成員變數或成員函式

如果希望在類A中使用類B的成員變數或成員函式。那麼有兩種方法: 1.類A和類B相互引用 典型例子是MVP,在View中建立Presenter,建立時View將自己傳入 class Activity{ Presenter mPresenter; public Activ

hadoop叢集出現datanode節點互相排斥的情況解決

我明明配置了3個節點的datanode,但是在 http://mini2:50070/dfshealth.html#tab-overview 的管理介面了只看到兩天存活 Live Nodes 為 2, Dead Nodes 為 0 我想就算有一臺掛掉也

linux伺服器互相拷貝檔案或者資料夾

例子: 互相拷貝檔案: scp /etc/mysql/my.cnf [email protected]:/etc/mysql 將mysql配置檔案上傳到136伺服器相應資料夾內 scp [email protected]:/etc/mysql/my.cnf /et

C#共享記憶體程序軟體互相讀寫實現類

我在網上找了很多原始碼,沒有一個可以用2個程式實現互相讀寫的共享記憶體功能,一般只能單向傳遞,沒有任何意義,於是我自己封裝了一個類,但是看起來沒任何問題,就是不能共享,現在我把程式碼貼出來,請大神幫忙看看 using System; using System.IO; using System

C++ 包含類互相呼叫彼此的類成員變數和方法

      在編寫C++程式時,有時候我們想在一個類中呼叫另一個類中的成員變數或方法,比如:兩個類 A和B ,A包含B,一般A中呼叫B中的方法比較簡單,重點是子類B如何呼叫父類A中的成員變數或方法呢?

java種經典例子Lock發生死案列

第一種synchronized方式死鎖:執行緒thread1先獲取鎖locka,然後在同步塊裡巢狀競爭鎖lockb。而執行緒thread2先獲取鎖lockb,然後在同步塊裡巢狀競爭鎖locka(此時已經被執行緒thread1擁有,而thread1在等待lockb,而loc

linux下用scp命令在服務器之間傳輸文件利用php_scp函數進行文件傳輸

evc 在操作 path send 返回值 遠程 false cal 上傳 在linux下利用scp進行文件傳輸, 從服務器下載文件 scp [email protected]/* */:/path/filename /path/filename 上傳

leetcode算法題2: 合並二叉樹。遞歸如何切入並保持清醒?

leetcode算法題2: 合並兩個二叉樹。遞歸 如何切入並保持清醒? /* Given two binary trees and imagine that when you put one of them to cover the other, some nodes of the two trees

2.5給定用鏈表表示的整數每個結點包含一個數位。這些數位是反向存放的也就是個位排在鏈表首部。編寫函數整數求和並用鏈表形式返回結果。

直接 logs next 末尾 做的 nbsp before != 結果 其實仔細想想是挺簡單的,我們要做的只是記得進位。 LinkedListNode addLists(LinkedListNode l1, LinkedListNode l2, int carry) /

在O(n)時間復雜度內求無序數組中任意元素的最大差值以及存在的組數

== result scan span pub ger oid 最小值 lose 題目描述: 求無序數組中任意兩個元素的最大差值,以及存在最大差值的組別數. 輸入: 輸入包含兩行,第一行輸入一個整數n;第二行n個正整數,用空格隔開. 輸出: 輸出為一行,包含最大差值,以及存

Integer變量a和b值相等a==b等於多少?

結果 多少 變量 原因 body 對象 valueof 整數 常用 Integer a = Integer.valueOf(127); Integer b = Integer.valueOf(127); Integer c = Integer.valueOf(128);

同張表中同時查詢字段顯示一個字段字段進行按時間排序

principal mount sel con AC code rom inter nbsp select b.bid_name as bidName,bd.repayment_way as depict,r.exact_repayment_time as time, r

9. 3 行 3 列的矩陣實現其對應位置的數據相加並返回一個新矩陣

int nco utf print odin enc odi nbsp bsp X = [[12,7,3], [4 ,5,6], [7 ,8,9]] Y = [[5,8,1], [6,7,3], [4,5,9]] #encoding=

算法:用棧來實現一個隊列完成隊列的Push和Pop操作。 隊列中的元素為int類型。《劍指offer》

pack 代碼 exception 隊列 imp scrip 入棧 return tro 算法:用兩個棧來實現一個隊列,完成隊列的Push和Pop操作。 隊列中的元素為int類型。《劍指offer》 利用棧來進行操作,代碼註釋寫的比較清楚:首先判斷兩個棧是否是空的:

面試題9-用棧來實現一個隊列完成隊列的Push和Pop操作

ati import str highlight print row pty 用兩個棧 div 題目 用兩個棧來實現一個隊列,完成隊列的Push和Pop操作。 隊列中的元素為int類型。 思路: 一個棧壓入元素,而另一個棧作為緩沖,將棧1的元素出棧後壓入棧2中

(java)leetcode415 字串相加(整數儲存成字串的形式它們求和)(Add String)

題目描述: 給定兩個字串形式的非負整數 num1 和num2 ,計算它們的和。 注意: num1 和num2 的長度都小於 5100. num1 和num2 都只包含數字 0-9. nu

iSO獲取日期之間的所有日期陣列精確到天

- (void)viewDidLoad {     [superviewDidLoad];     NSArray *datearr = [selfgetDayArrayLeftDate:@"2017年01月01日