SQL Server Bookmark Lookup死锁解决方法

SQL Server Bookmark Lookup Deadlock solution

我在我的应用程序中检测到书签查找死锁,我无法决定使用哪个解决方案。 None 其中似乎是最优的。

查询如下:

 UPDATE TEST SET DATA = @data WHERE CATEGORY = @cat

 SELECT DATA, EXTRA_COLUMN FROM TEST WHERE CATEGORY = @cat

问题是 CATEGORY 和 DATA 中有一个非聚集索引,两个查询都以与聚集索引相反的顺序使用该索引。

即:更新锁定聚集索引并更新 table,而 select 锁定非聚集索引以进行书签查找,并且它们都希望彼此锁定(死锁)。

以下是我找到的选项:

1 - 创建一个包含 select 查询中所有列的索引。 - 它奏效了,但我认为这不是一个好主意,我必须包括任何 select 查询中使用的任何列,这些列可以在应用程序的任何地方更新。

2 - 将数据库的事务隔离级别更改为COMMITTED_SNAPSHOT

3 - 将 NOLOCK 提示添加到 select

4 - 删除索引

5 - 强制其中一个事务在较早的时间点阻塞,在它有机会获得最终阻塞另一个事务的锁之前。 (没用)

我认为第二个选项是最佳选择,但我知道它会产生其他问题,COMMITTED_SNAPSHOT 不应该是 SQL SERVER 中的默认隔离级别吗?

在我看来,应用程序或数据库逻辑中都没有任何错误,这是一个简单的 table,具有非聚集索引和两个访问相同 table 的查询, 一个要更新,另一个要更新到 select.

解决这个问题的最佳方法是什么?还有其他解决办法吗?

真希望SQL服务器能自己解决

请尝试在类别上添加非聚集索引(包括数据和 Extra_Column)并在您的查询中添加以下提示:

UPDATE t SET t.DATA = @data FROM TEST WITH (index(ix_Cat)) WHERE CATEGORY = @cat

SELECT DATA, EXTRA_COLUMN FROM TEST WITH (index(ix_Cat)) WHERE CATEGORY = @cat

这将确保两个查询 Update/Select 数据的顺序相同,并防止它们彼此死锁。

快照隔离是从等式中删除读取的非常可靠的解决方案。许多 RDBMS 都让它们始终处于打开状态。它们在实践中不会造成很多问题。比起一些手动的脆弱解决方案,例如非常具体的索引或提示,更喜欢此解决方案。