我在我的应用程序中检测到书签查找死锁,我无法决定使用哪种解决方案。它们似乎都不是最佳的。
以下是查询:
UPDATE TEST SET DATA = @data WHERE CATEGORY = @cat
SELECT DATA, EXTRA_COLUMN FROM TEST WHERE CATEGORY = @cat
问题是CATEGORY和DATA中存在一个非聚集索引,两个查询都使用聚簇索引的反向顺序。
即:更新锁定聚簇索引并更新表,而select锁定非聚集索引以进行书签查找,并且它们都希望彼此锁定(死锁)。
以下是我找到的选项:
1 - 创建一个索引,其中包含select查询中的所有列。 - 它有效,但我认为不是一个好主意,我必须包含任何可在应用程序中任何位置更新的选择查询中使用的列。
2 - 将数据库的事务隔离级别更改为COMMITTED_SNAPSHOT
3 - 将NOLOCK提示添加到选择
4 - 删除索引
5 - 在有机会获得最终阻止其他交易的锁之前,强制其中一个交易在之前的某个时间点被阻止。 (没用)
我认为第二个选项是最佳选择,但我知道它可以创建其他问题,COMMITTED_SNAPSHOT不应该是SQL SERVER中的默认隔离级别吗?
在我看来,在应用程序或数据库逻辑中没有任何错误,它是一个带有非聚集索引的简单表和两个访问同一个表的查询,一个用于更新,另一个用于选择。
哪种方法可以解决这个问题?还有其他解决方案吗?
我真的希望SQL Server能够自己解决它。
答案 0 :(得分:1)
请尝试在类别上添加非聚集索引(包括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
这将确保两个查询都以相同的顺序更新/选择数据,并防止它们相互死锁。
答案 1 :(得分:1)
快照隔离是一种非常强大的解决方案,用于从等式中删除读取。许多RDBMS都始终打开它们。它们在实践中不会引起很多问题。将此解决方案倾向于某些手动脆弱的解决方案,例如非常具体的索引或提示。