關於兩個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日