Sql-Server

UPDATE 語句行為

  • August 14, 2020

我對關於 SQL Server 的 UPDATE 語句的內部工作方式有疑問。我試圖了解如果同時收到或服務以下 2 個更新語句會發生什麼:

|    Session 1 - Statement 1 | Session 2 - Statement 2
|   -------------------------+-------------------------
|    update dbo.Table1       |  update dbo.Table1 
|    set Value = 10          |  set Value = 20 
|    where ID = 1            |  where ID = 1 
▼    and Value = 0;          |  and Value = 0;
(t)

據我了解,更新將首先選擇需要使用共享鎖更新的行,這意味著兩個更新語句都可以選擇特定行。然後它會請求一個排他鎖來更新列值。因此,結果似乎將 Value 設置為 20。

我錯過了什麼還是我的理解正確?

連接處的隔離級別設置為 READ UNCOMMITTED。

我對關於 SQL Server 的 UPDATE 語句的內部工作方式有疑問。

該語句描述了對數據庫的期望邏輯更改。執行時物理上發生的事情取決於執行計劃、數據庫狀態以及其他並發執行的語句/查詢正在做什麼。

我提到這一點是因為您的問題與實現細節非常相關。

據我了解,更新將首先選擇需要使用共享鎖更新的行,這意味著兩個更新語句都可以選擇特定行。

這通常不會發生。

在大多數隔離級別(包括READ UNCOMMITTED問題中指定的)下,SQL Server 將U在定位要更新的行時使用更新 () 鎖。這是一個添加的實現細節,以提供一些針對常見形式的轉換死鎖的保護(它並不總是足夠的,但它確實有幫助)。

注意:SQL Server 可以選擇任何方便的訪問方法來定位要更新的行。例如,它可能選擇使用非聚集索引。更新最終仍會更新基表和任何適用的二級索引,但操作順序可能會有所不同。

這意味著U鎖不一定像人們期望的那樣序列化。例如,問題中的一條更新語句可能選擇通過二級索引定位記錄,在這種情況下,該索引中的條目已U應用鎖定。沒有什麼可以阻止第二個更新語句選擇使用基表(堆或集群)掃描來定位記錄的計劃。

在這種情況下,第二次更新可能會在基表中的行上獲取鎖,而第一次更新會在連結到同一基表行的二級索引中的行上U持有鎖。U

在另一種情況下,SQL Server 可能能夠選擇單個運算符更新計劃(例如,如果聚集索引存在於 上(ID, Value))。在這種情況下,X會立即在聚集索引上獲取排他鎖 - 沒有先前的U鎖。

然後它會請求一個排他鎖來更新列值。

數據更改操作總是X在進行更改之前鎖定。這個鎖被持有到事務結束。它可能涉及也可能不涉及將目前持有的U鎖轉換為X.

例如,如果要更新的行是使用基表定位的,則 aU將被保存並轉換為X基表更新運算符。如果該行是使用二級索引定位的,則保留在該索引上,並在基表上獲取U一個新鎖。X兩者都U同時X舉行,在不同的資源上。

因此,結果似乎將 Value 設置為 20。

根據兩個更新語句中的每一個選擇的時間和執行計劃,有許多可能的結果。僅選擇一些更有趣的:

場景一:

  1. 會話 1U在讀取時獲得對基表行的鎖定。
  2. 會話 2 阻塞等待U在基表中的同一行上獲取。
  3. 會話 1 將 Value 設置為 10 並送出。
  4. 會話 2 獲得了它的U鎖並且發現無事可做,因為 Value != 0 現在。

結果:值設置為 10。

場景二:

  1. 會話 2U在讀取時獲得對基表行的鎖定。
  2. 會話 1 阻塞等待U在基表中的同一行上獲取。
  3. 會話 2 將 Value 設置為 20 並送出。
  4. 會話 1 獲得了它的U鎖並且發現無事可做,因為 Value != 0 現在。

結果:值設置為 20。

場景 3:

  1. 會話 1 獲得U二級索引上的鎖(定位要更新的行)。
  2. 會話 2 獲得U對基表的鎖定(定位要更新的行)。
  3. 會話 1 需要獲取X基表行來執行更新,但被會話 2 持有的鎖阻塞。U
  4. 會話 2 將其U對基表的鎖定轉換為X並將 Value 設置為 20。
  5. 會話 2 現在需要維護二級索引(假設索引包含 Value 列)。
  6. 會話 2 需要獲取X二級索引行來更新它,但被會話 1 持有的鎖阻塞。U
  7. 僵局。其中一個會話因錯誤而回滾;另一個成功完成。

結果:不確定。值可能最終為 10 或 20。


這些只是一些可能性。試圖預測詳細的引擎鎖定行為通常是一個錯誤(在我看來)。而是專注於為您要進行的更改編寫正確的邏輯規範,並使用提供所需保證的隔離級別。

關於問題的編寫方式,我認為您可能真的在問“失去的更新”。如果是這種情況,請在提出更具體的新問題之前查看現有的問答,如下所示:

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