UPDATE WHERE 中的表扫描如何以未索引的列为谓词工作?

Col*_*nee 3 sql-server deadlock sql-server-2008-r2 locking update

我正在调查由两个并发 UPDATE 语句引起的死锁:

UPDATE [table] 
SET [column] = 0 
WHERE [unindexed_column] = @id
Run Code Online (Sandbox Code Playgroud)

我的理解是,因为WHERE预测的列是未索引的,所以执行了全表扫描。对于每一行,获取一个更新锁。如果它与WHERE子句匹配,则它升级到排他锁并保持它直到语句完成。

当会话 A 拥有第 2 行的排他锁并试图获取第 1 行的更新锁,而会话 B 拥有第 1 行的排他锁并试图获取第 2 行的更新锁时,就会发生死锁。

死锁的原因是有道理的,但我不完全理解如何执行表扫描使这种情况成为可能。如果两个查询以相同的顺序执行扫描,最坏的情况似乎是其中一个查询在获取更新锁时被阻塞,直到另一个查询完成并释放它的锁。

表扫描是如何执行的?表格行的扫描顺序是否不一致?如果更新语句未能获得更新锁,它是否会转到下一行并稍后再次尝试上一行?究竟是什么让这种僵局成为可能?

a1e*_*x07 6

表扫描的顺序永远无法保证。行被锁定的顺序也不能保证。另外,SQL Server 有锁升级,所以你真的不能说引擎决定锁什么,行,页,还是表本身。

因此,即使并发会话中@id 的值不同,也可能发生死锁,但感兴趣的行恰好位于相同的页面上。在 SQL Server 2008 及更高版本中,您可以添加提示ROWLOCK以减少锁升级的机会(但同样不能保证)。如果UPDATE无法获取锁,则等待(LOCK_TIMEOUT指定等待多长时间)。