Sql-Server

試圖理解一個 RCSI 例子——Read Committed Snapshot Isolation Level 的潛在危險

  • January 4, 2021

有這篇文章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 鎖,另一個將被阻塞,直到第一個會話送出。

引用自:https://dba.stackexchange.com/questions/282500