臟讀或幻行
我們的數據倉庫中有臟讀或幻行。我的問題不是如何解決它(這是一個很長的故事,我目前正在研究它),但我想知道究竟發生了什麼以更好地理解行為並使用正確的術語。這些是臟讀還是幻行?
假設我們
SourceTableA
在 ERP 數據庫中有一個CLUSTERED UNIQUE
索引Col1
和Col2
. 執行以下查詢的 DWH 提取過程:SELECT Col1, Col2, Col3 FROM ERPdatabase..SourceTableA WITH(NOLOCK);
不建議這樣做,因為現在我們甚至可以進行臟讀,但我們是被 ERP 供應商強迫這樣做的。供應商知道其中的含義並對其所有事務使用 READ UNCOMMITTED 並強制我們使用 NOLOCK 提示。
假設我們
RowA
有SourceTableA
:('GreatCompany','WonderfullItem','ColorRed')
RowA
現在(我認為……)ERP系統中的使用者會話在上一個查詢執行時執行更新,如下所示:UPDATE SourceTableA SET Col3 = 'ColorBlue' WHERE Col1 = 'GreatCompany' AND Col2 = 'WonderfullItem'
如果我正確與否,我看到的情況是提取過程正在讀取 2 行。自從我們使用以來,我並不感到驚訝
NOLOCK
(儘管我不確定我是否了解確切的原因,因此提出了這個問題),但我對此有兩個問題:
- 既然行在 CLUSTERED 索引中沒有移動,因為主鍵沒有改變,為什麼要第二次讀取呢?我的想法是提取過程已經過了那一行,所以它不應該再遇到它了。或者這不是它的工作原理?我能想到的唯一解釋是提取查詢沒有使用 CLUSTERED 索引來讀取數據,而可能是使用 NONCLUSTERED 索引來移動行。或者我還缺少什麼?還是只是臟讀?還是只是“除非您使用 ORDER BY、DISTINCT 或不使用 NOLOCK,否則 SQL Server 不保證行順序”?然後我很好奇為什麼。:-)
- 關於臟讀,我一直認為我看到的行為是幻像行,但是,據我了解,要發生幻像行,我們實際上需要在同一事務中讀取同一組數據的 2 個語句。這裡不是這種情況,因為它只有 1 條語句,所以我隱含地認為這應該是臟讀?另一方面,為了發生臟讀,我們需要一個更新被另一個事務回滾。這不是我認為正在發生的事情。或者這實際上是唯一的解釋?
- 您在這裡的初始假設是正確的。如果該行不移動(包括由於頁面拆分),則掃描/搜尋不會讀取它兩次。但是,正如您所說,查詢可能是通過移動行索引的 NC 索引完成的,因此是雙重讀取。
- 關於術語,ANSI SQL中定義了一些標準現象,然後結合隔離級別來描述隔離級別。即,隔離級別 A 消除了現象 X 和 Y,但沒有消除 Z。此類現象包括臟讀(讀取已修改但稍後可能回滾的內容)、幻像(讀取一些數據,再次讀取並在其中出現新行)放)。雙重讀取不在 ANSI SQL 中定義的那些現像中,也許是因為該標準過於豪華而無法關心物理實現(如索引),並且在這樣的世界中兩次遇到同一行甚至不在桌面上?即,您在這裡所描述的是我們在 SQL Server 社區中使用的所謂的“雙重讀取”,但不要期望看到針對這種現象的術語正式化。
(我意識到我可能在這裡過度/濫用了現像這個詞,在那種情況下我的錯!)
*由於對更改數據的分配順序掃描*而失去已送出的行或多次遇到它們是特定於使用未送出的讀取隔離
NOLOCK
(由此處的提示啟用)。另請參閱如果Microsoft 的 Lubor Kollar使用 NOLOCK 提示,則可能會失去以前送出的行。正如David Browne建議的那樣,您可以考慮報告數據庫快照 - 如果您不能使用產生正確結果的隔離級別。
人們也可以考慮超低技術的解決方案。我正在考慮備份和恢復,然後報告恢復的數據庫。您可以將完整備份與日誌備份結合使用。當然,當數據庫不太大時,這更可行。
既然行在 CLUSTERED 索引中沒有移動,因為主鍵沒有改變,為什麼要第二次讀取呢?
實際上,即使您沒有更新集群鍵,它也可以移動。更新非鍵列值或更新/插入同一頁面上的其他行可能會導致頁面拆分,並且一半的行將移動到新頁面。