Sql-Server
基於未索引列的 UPDATE WHERE 中的表掃描如何工作?
我正在研究由兩個並發 UPDATE 語句引起的死鎖:
UPDATE [table] SET [column] = 0 WHERE [unindexed_column] = @id
我的理解是,因為所基於的列
WHERE
是未索引的,所以會執行全表掃描。對於每一行,獲取一個更新鎖。如果它匹配該WHERE
子句,則升級為排他鎖並持有它直到語句完成。當會話 A 在第 2 行上具有排他鎖並試圖在第 1 行上獲取更新鎖時,會發生死鎖,而會話 B 在第 1 行上具有排他鎖並試圖在第 2 行上獲取更新鎖。
死鎖的原因是有道理的,但我不完全了解如何執行表掃描以使這種情況成為可能。如果兩個查詢以相同的順序執行掃描,最壞的情況似乎是其中一個查詢在獲取更新鎖時被阻塞,直到另一個查詢完成並釋放它的鎖。
如何執行表掃描?表格行的掃描順序是否不一致?如果更新語句獲取更新鎖失敗,是否會轉到下一行,稍後再嘗試上一行?究竟是什麼使這種僵局成為可能?
表掃描的順序永遠無法保證。也不保證鎖定行的順序。此外,SQL Server 具有鎖升級功能,因此您無法真正說出引擎決定鎖定的內容、行、頁或表本身。
因此,即使並發會話中@id 的值不同,也可能發生死鎖,但感興趣的行恰好位於相同的頁面上。在 SQL Server 2008 及更高版本中,您可以添加
ROWLOCK
減少鎖升級機會的提示(但同樣不能保證)。如果UPDATE
無法獲取鎖,它會等待(LOCK_TIMEOUT
指定它會等待多長時間)。