我的 SQL 查詢會使用過時的數據嗎?我該如何預防?
我有兩個表(
SJob
&SJobDependent
),我需要加入儲存過程中的某些邏輯。它們都有一個列 ( )以一對多的關係job
連接它們- 一條記錄對應零個或多個記錄。SJob``SJobDependent
這是我的 SQL 查詢:
-- Return any records that are active and have no unsatisfied dependencies. SELECT * FROM SJob LEFT JOIN SJobDependent ON SJob.job = SJobDependent.job AND SJobDependent.satisfied = 0 WHERE SJobDependent.jobDependentID IS NULL AND SJob.state = 'active'
以下是 SQL Server Studio 的實際執行計劃:
由於程式碼的編寫方式:
// Pseudo-code: // SJob record is added with SJob.state = 'ready'. // Related SJobDependent record(s) are added. // SJob record is updated to SJob.state = 'active'.
我擔心 SQL 查詢執行時可能會發生這種情況:
- 掃描 SJobDependent。
- 已插入 SJobDependent 記錄。
- 開始掃描 SJob。SJob.state 已“準備就緒”。
- SJob 已更新。這會阻止讀取 SJob?
- 結束 SJob 掃描。SJob.state 是“活動的”。
我擔心的問題是我的 SQL 查詢返回
SJob
在“活動”狀態 (SJob.state = 'active'
) 中找到的記錄,但看不到相關SJobDependent
記錄。這個問題會發生嗎,還是我過度分析了 SQL 查詢?
如果這是一個值得擔心的合法問題,我能做些什麼來解決它?我對解決方案持開放態度。
我的一個想法是強制
SJobDependent
掃描SJob
. 這甚至可能嗎?這樣做有什麼影響/後果?實際執行計劃中顯示的掃描是按特定順序發生的,還是總是隨機呼叫的?
注意:如 AMtwo 的回答中所述,Repeatable Read 隔離級別可能無法解決我的問題,因為它僅在讀取開始時生效。
如果您使用 SQL Server 中的預設隔離級別(已送出讀),那麼您肯定會遇到與讀取不一致有關的各種問題。Paul White 在這裡描述了這些問題。
如果您希望您的讀取查詢讀取與給定時間點的外觀完全一致的數據,我建議您考慮讀取送出快照隔離(RCSI)。使用 RCSI,您的查詢將返回與單個時間點(查詢開始)一致的數據。如果使用者 A 在使用者 B 並發執行更新時啟動
SELECT
查詢,使用者 A 將讀取“舊”值,因為它將讀取數據的快照,這與查詢的開始是一致的。RCSI 的關鍵在於它是一個數據庫級別的設置。與未送出的讀取不同,您不能將其設置為會話範圍的設置。在進行更改之前,您必須更全面地考慮此更改。但是一般來說,如果您需要此查詢的一致讀取,您可能需要整個應用程序的一致讀取。
雖然可重複讀取隔離級別看起來很適合解決您的問題,但請注意連結文章中的以下詳細資訊:
可重複讀隔離級別保證數據 在第一次被讀取後在事務的生命週期內不會改變 。
這意味著數據在被訪問之前仍然可以更改,但在查詢執行期間。它還受到與已送出讀隔離級別相同的一些不一致讀取的影響——尤其是幻像。