Sql-Server
了解 SQL Server 版本儲存
我正在嘗試了解 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和UPDLOCK
table 提示)。因此,您將使用 讀取您想要用於 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 所做的。