我正在调查由两个并发 UPDATE 语句引起的死锁:
UPDATE [table]
SET [column] = 0
WHERE [unindexed_column] = @id
我的理解是,因为所WHERE
预测的列是未索引的,所以执行了全表扫描。对于每一行,获取一个更新锁。如果它匹配该WHERE
子句,则它会升级为独占锁并持有它直到语句完成。
当会话 A 对第 2 行具有排他锁并试图获取对第 1 行的更新锁时,就会发生死锁,而会话 B 对第 1 行具有排他锁并试图获取对第 2 行的更新锁。
死锁的原因是有道理的,但我不明白表扫描是如何执行的,从而使这种情况成为可能。如果这两个查询以相同的顺序执行扫描,那么最坏的情况似乎是其中一个查询在获取更新锁时被阻塞,直到另一个查询完成并释放它的锁。
表扫描是如何执行的?表格行的扫描顺序是否不一致?如果update语句获取更新锁失败,是否跳转到下一行,稍后再尝试上一行?究竟是什么让这种僵局成为可能?
永远无法保证表扫描的顺序。也不能保证行被锁定的顺序。另外,SQL Server 有锁升级,所以你不能真正说出引擎决定锁什么,行,页,还是表本身。
因此,即使 @id 的值在并发会话中不同,但感兴趣的行恰好驻留在相同的页面上,也可能会发生死锁。在 SQL Server 2008 及更高版本中,您可以添加
ROWLOCK
减少锁定升级机会的提示(但同样不能保证)。如果UPDATE
无法获取锁,它会等待(LOCK_TIMEOUT
指定要等待多长时间)。