2

我在我的应用程序中检测到书签查找死锁,我无法决定使用哪种解决方案。它们似乎都不是最佳的。

以下是查询:

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

 SELECT DATA, EXTRA_COLUMN FROM TEST WHERE CATEGORY = @cat

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

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

以下是我找到的选项:

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

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

3 - 向选择添加 NOLOCK 提示

4 - 删除索引

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

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

在我看来,应用程序或数据库逻辑中都没有任何错误,它是一个具有非聚集索引的简单表和两个访问同一个表的查询,一个用于更新,另一个用于选择。

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

我真的希望 SQL Server 能够自己解决它。

4

2 回答 2

1

请尝试在 Category 上添加非聚集索引(包括 Data & 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

这将确保两个查询都以相同的顺序更新/选择数据,并防止它们相互死锁。

于 2015-04-20T20:19:28.673 回答
1

快照隔离是从等式中删除读取的非常强大的解决方案。许多 RDBMS 始终启用它们。它们在实践中不会引起很多问题。与某些手动脆弱的解决方案(例如非常具体的索引或提示)相比,更喜欢此解决方案。

于 2015-04-20T20:20:09.143 回答