Sql-Server
試圖理解一個 RCSI 例子——Read Committed Snapshot Isolation Level 的潛在危險
有這篇文章The Potential Dangers of the Read Committed Snapshot Isolation Level展示了 RC 與 RCSI 隔離級別。
我得到了 RC 範例,但沒有得到 RCSI 範例。特別是查詢如何獲得結果**-3**。有人能解釋一下這個查詢在 RCSI 下是如何工作的嗎?
在鎖定讀取送出下,很可能(但不能保證)當兩個會話執行此程式碼時
BEGIN TRANSACTION DECLARE @QtyRequired int, @QtyRemain int SELECT @QtyRequired = 4 SELECT @QtyRemain = SUM(QTY) FROM Inventory WHERE ItemID = 796 AND LocationID = 1 IF @QtyRemain - @QtyRequired >= 0 BEGIN UPDATE Inventory SET Qty = Qty - @QtyRequired WHERE ItemID = 796 AND LocationID = 1 -- Do other stuff in other tables or databases to check out the item WAITFOR DELAY '00:00:10' SELECT 'Checkout complete' END ELSE SELECT 'Not enough qty!' COMMIT TRANSACTION
第二個會話執行的時間會有足夠的差異
SELECT @QtyRemain = SUM(QTY) FROM Inventory WHERE ItemID = 796 AND LocationID = 1
在第一個會話執行後
UPDATE Inventory SET Qty = Qty - @QtyRequired WHERE ItemID = 796 AND LocationID = 1
因此第二個會話將無法獲得該行的 S 鎖,並且將被阻塞,直到第一個會話送出。但是,這不是保證,因為兩個會話很可能都執行
SELECT @QtyRemain = SUM(QTY) FROM Inventory WHERE ItemID = 796 AND LocationID = 1
在他們中的任何一個執行更新之前。
使用 RCSI,它們更有可能為 @QtyRemain 讀取相同的值,因為 SELECT 永遠不會被另一個會話上的掛起 UPDATE 阻塞,並且只返回版本中行的“last-known-good”值店鋪。
但是由於鎖定讀取送出版本有同樣的問題,儘管在一個較小的視窗中,它們都被破壞了,應該通過將第一個查詢更改為:
SELECT @QtyRemain = SUM(QTY) FROM Inventory with (UPDLOCK) WHERE ItemID = 796 AND LocationID = 1
這保證了即使兩個會話同時嘗試查詢,一個將被授予 U 鎖,另一個將被阻塞,直到第一個會話送出。