說我有一個簡單的查詢如使用WITH(NOLOCK)和交易
Select * From MyTable WITH (NOLOCK)
而且,在此執行查詢時的同時,另一個用戶在插入100行到該表中的一個的上下文中交易。
在提交或回滾事務之前,Select語句是否理論上有可能因爲它使用NOLOCK而讀取插入表中的100行的子集?如果我正確理解NOLOCK,這似乎是可能的,但想要驗證。
SQL Server 2012的
說我有一個簡單的查詢如使用WITH(NOLOCK)和交易
Select * From MyTable WITH (NOLOCK)
而且,在此執行查詢時的同時,另一個用戶在插入100行到該表中的一個的上下文中交易。
在提交或回滾事務之前,Select語句是否理論上有可能因爲它使用NOLOCK而讀取插入表中的100行的子集?如果我正確理解NOLOCK,這似乎是可能的,但想要驗證。
SQL Server 2012的
當然,你可以閱讀的子集或全部是事務的開始和提交或回滾之間影響了未提交的數據。這就是NOLOCK
這一點 - 允許您讀取未提交的數據,以便您不必等待作者,並避免放置大多數鎖,以便作者不必等待。
證據#1
這是很容易證明的。在一個窗口中,創建該表:
CREATE TABLE dbo.what(id INT);
在第二個窗口,運行此查詢:
DECLARE @id INT;
WHILE 1 = 1
BEGIN
SELECT @id = id FROM dbo.what WITH (NOLOCK) WHERE id = 2;
IF @id = 2
BEGIN
PRINT @id;
BREAK;
END
END
現在回到第一個窗口,並開始有意長時間運行的事務,但捲回:
BEGIN TRANSACTION;
GO
INSERT dbo.what SELECT 2;
GO 10000
ROLLBACK TRANSACTION;
只要你在第一個窗口開始這一聲,在第二個窗口中的查詢將暫停並會吐出被讀取未提交的值。
證據#2
這主要是較量@布拉姆的評論上面,這點我不同意:
其實我覺得你可以閱讀所有100不僅僅是一個前集到提交或回滾。
您當然可以讀取受事務影響的行的子集。嘗試以下類似示例,這次將100組數據插入表中1000次,並使用(NOLOCK)
檢索查詢中的計數。窗口#1(上面,如果你還沒有測試證明#1):
CREATE TABLE dbo.what(id INT);
窗口#2:
DECLARE @c INT;
WHILE 1 = 1
BEGIN
SELECT @c = COUNT(*) FROM dbo.what WITH (NOLOCK) WHERE id = 2;
IF @c > 0
PRINT @c;
IF @c > 10000
BREAK;
END
早在窗口#1:
BEGIN TRANSACTION;
GO
INSERT dbo.what SELECT TOP (100) 2 FROM sys.all_objects;
GO 1000
ROLLBACK TRANSACTION;
窗口# 2將旋轉,直到你開始交易。只要你這樣做,你就會開始看到數字流入。但它們不會達到100的倍數(不要介意100,000,@Blam似乎正在做的全部或沒有任何要求)。這裏是我的刪節結果:
1
10
12
14
17
19
23
25
29
...
85
87
91
95
98
100
100
...
9700
9700
9763
9800
9838
9900
9936
10000
10000
10000
10080
的NOLOCK
查詢顯然不等待任何單個語句來讀取數據之前完成,更不用說整個事務。因此,無論每個語句所影響的行數有多少,並且無論整個事務中有多少個語句,都可以使數據處於任何流動狀態。
其他副作用
也有情況下NOLOCK
可以skip rows, or read the same row twice,取決於掃描,並且當另一個事務正在生成頁拆分的類型。基本上,當(NOLOCK)
查詢正在讀取數據時,其他寫入操作實際上可以將數據移動到其他位置 - 因爲它們可以 - 將已讀取的行移動到掃描中更遠的位置,或者將行移動在您的掃描之前還沒有讀過。
諮詢
在一般情況下,這是一個壞消息,你應該考慮READ_COMMITTED_SNAPSHOT
代替 - 它讓讀者不會阻止作家和反之亦然相同的利益,但給你的一個一致的看法數據在某個時間點忽略所有後續的數據修改(儘管這對tempdb有影響,所以一定要測試它)。 Very thorough information here。
正如亞倫已經很好地解釋,是的,你會閱讀骯髒的未提交的數據。但是,如果您想避免讀取髒數據,並且尚未準備好開始使用樂觀鎖定,則可以嘗試使用如下所示的READPAST表提示,儘管這樣做的效果是它將跳過任何被鎖定的行,所以您將看不到尚未提交的插入和更新的行。
SELECT *
FROM MyTable WITH (READPAST)
請注意,此表提示要求數據庫中所承諾無論是讀(默認),或是可重複讀隔離級別下運行。
其實我認爲你可以在提交或回滾之前讀取全部100個不只是一個子集。 – Paparazzi
@Blam不同意。請看我更新的答案,否則證明。 –
@AaronBertrand - 我想你可能是交叉目的。我將Blam的評論解釋爲*和讀取受影響行的(適當的)子集一樣,您還可以在提交/回滾之前讀取整行行。 –