2

我知道快照隔離可以解決這個問題,但我想知道在這種特殊情況下NOLOCK是否安全,這樣我可以避免開銷。在這種情況下讀取未提及/解鎖安全嗎?

我有一個表,看起來是這樣的:

drop table Data 

create table Data 
(
    Id BIGINT NOT NULL, 
    Date BIGINT NOT NULL, 
    Value BIGINT, 
    constraint Cx primary key (Date, Id) 
) 

create nonclustered index Ix on Data (Id, Date) 

有表中沒有更新,永遠。刪除可能發生,但他們不應該與SELECT競爭,因爲它們會影響表的另一個較舊的末端。插入是常規的,頁面拆分到(Id,Date)索引是非常普遍的。

我有一個標準的INSERT和SELECT看起來像這樣之間的死鎖情況:

select top 1 Date, Value from Data where Id = @p0 order by Date desc 

因爲INSERT獲取關於CX鎖(日期,身份證;值),然後IX(ID,日期),但SELECT會獲取Ix(Id,Date)和Cx(Date,Id; Value)上的鎖定。這是因爲SELECT首先在Ix上尋找,然後加入到Cx上尋找。

交換聚集索引和非聚集索引會打破這個循環,但它不是一個可接受的解決方案,因爲它會引入具有其他(更復雜)SELECT的循環。

如果我將NOLOCK添加到SELECT,在這種情況下會出錯嗎?它可以返回:

  1. 不止一行,即使我問TOP 1?
  2. 沒有行,即使存在並已提交?
  3. 最糟糕的是,一行不符合WHERE子句?

我已經做了很多的閱讀有關本次網上,但過高或過低計數異常的唯一複製品我見過(onetwo)涉及掃描。這隻涉及尋求。 Jeff Atwood has a post關於使用NOLOCK產生了一個很好的討論。我是由Rick湯森評論特別感興趣:

其次,如果你讀髒數據,運行 風險是讀 完全錯誤的行。例如,如果 您選擇讀取索引來查找 你行,則更新改變 位置的行(例如:由於 頁拆分或更新到 聚集索引),當你選擇 去讀取實際的數據行,它的 或者不再存在,或者完全不同的 行!

這是可能的只插入和沒有更新?如果是這樣,那麼我想即使我在一張只有插入的桌子上尋找也是危險的。


更新:

我試圖找出how snapshot isolation works。它似乎是基於行的,事務讀取表(不帶共享鎖!),找到他們感興趣的行,然後查看他們是否需要從tempdb中的版本存儲中獲取該行的舊版本。

但在我的情況下,沒有行將有多個版本,所以版本存儲似乎是沒有意義的。如果找到沒有共享鎖的行,那麼和使用NOLOCK有什麼不同呢?

回答

7

使用NOLOCK或READ UNCOMMITTED意味着您放棄任何一致性的保證。期。

如果你需要一致性,不要做髒讀。您的整個解釋依賴於未記錄的行爲,未來版本中可能會發生更改,更糟糕的是,您希望查詢的的特定訪問計劃爲。查詢優化器可以自由選擇任何它認爲合適的計劃,並且任何假設都可能在生產中被破壞。所以回到原點:如果你不準備面對後果,不要做髒讀。

不知道這是否適用,不清楚你試圖用你的查詢/表達到什麼,但也許這篇文章可能有所幫助:Using tables as Queues

更新
凡NOLOCK讀會讀不一致的狀態(例如,讀一個陳舊的非聚集索引鍵,它追缺失的行聚集索引)快照讀會找到「失蹤」在版本商店中排。對於穩定的數據,快照讀取與nolock讀取相同。每當數據發生變化(未提交更新)時,版本存儲的魔力就會發揮作用,因爲快照讀取進入版本存儲並發現nolock讀取會失控並追逐指針的'舊'值(穩定且一致)啦啦土地。

+0

啊,OUTPUT子句非常酷,謝謝。我不需要它在這個特定的桌子上,但我可以考慮一些未來的代碼,我可以使用它。 我明白你的觀點。我已經體驗過不同查詢計劃的影響 - 直到有足夠的行才能使這兩個查找比全面掃描更好,纔會發生死鎖。說實話,我已經傾向於快照隔離。但記錄無證行爲對我來說很有意思,即使我沒有在生產中利用它。 :) – 2010-06-08 22:27:28

+0

用另一個問題更新原來的帖子,謝謝。 – 2010-06-08 23:39:46

+0

好吧,所以我同意我們不想「讀取過時的非聚簇索引鍵並將其追加到聚簇索引中缺失的行」。問題是,這是否會在沒有任何行的UPDATE的情況下實際發生?我所做的只是插入,因此只有元數據將被更新,而不是任何行中的數據。我以爲SQL Server會有一些低級別的一致性機制來防止元數據損壞,即使使用NOLOCK(這是一個相當高層次的概念)..? – 2010-06-09 00:39:46

1

在這種情況下,您應該使用NOLOCK進行安全操作。另外一個想法是:在Ix索引中添加Value作爲包含的列應該消除Cx上的查找。

create nonclustered index Ix on Data (Id, Date) include (Value) 
+0

鑑於上述擔憂,您可否詳細說明爲什麼您認爲它是安全的?另外,我已經考慮在索引中包含非關鍵字列,但實際上它們相當廣泛(其中不止一個),並且會佔用大量空間。重複可能會將更多有用的東西從緩存中取出。 – 2010-06-08 22:08:59