Sql-Server

了解 SQL Server 版本儲存

  • August 21, 2022

我正在嘗試了解 SQL Server 版本儲存和相關的隔離級別。據我了解,當數據庫啟用讀取送出快照選項時,可能會發生這種情況:

  • 一個項目 (id = 1) 在數據庫中的價格為 $1000
  • 會話 1 啟動更新語句:update products set price = price * 1.5. 由於這涉及表格的所有行,因此需要很長時間。
  • update語句仍在進行中時,會話 2 開始查詢:select * from products where id = 1。由於數據庫處於讀取送出快照模式,因此寫入者不會阻塞讀取者。所以會話 1 從版本商店讀取了該行的舊版本,並認為該產品的價格為 1000 美元。
  • session 1的使用者覺得價格還不錯,所以決定買。但 …
  • 在使用者將產品添加到他的購物車之前,上述update語句執行完畢,產品 (id = 1) 的新價格為 1500 美元。如果使用者知道產品的新價格,他就不會購買。

在這種情況下,會發生什麼?這種情況真的可能嗎?如果是這樣,防止這種情況的規範是什麼?

您所指的問題稱為write skew,並且在使用樂觀並發讀取和寫入數據時發生。

是的,這在 下是絕對可能的SNAPSHOT,因為版本化數據沒有被鎖定,並且可以在該隔離級別下讀取。這與實際執行的隔離級別無關update products ...,因為正在讀取的會話只會轉到版本儲存。

為了解決這個問題,一種常見的解決方案是在進行新寫入時確認假設(對於實際預訂)。新的寫入也需要在非SNAPSHOT隔離級別下,並且必須至少是REPEATABLE READ級別(或者您可以一起READCOMMITTEDLOCK使用 a和UPDLOCKtable 提示)。

因此,您將使用 讀取您想要用於 UI 目的的數據SNAPSHOT,這允許出於 UI 目的進行非阻塞讀取。然後在實際創建預訂時,您將執行以下操作:

SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;

SET XACT_ABORT, NOCOUNT ON;

BEGIN TRAN;

IF @price <> (
   SELECT p.price
   FROM products p
   WHERE p.id = @productId
)
   THROW 50001, 'New price detected', 1;


INSERT OrderDetail (....)
VALUES (....);

COMMIT;

此方法的另一個變體是返回相關rowversion行的值,然後檢查(使用與上面相同的事務語義)rowversion是否相同。如果配置,這就是實體框架等 ORM 所做的。

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