SQL Server 索引更新死锁

Dal*_*e K 16 sql-server deadlock locking

我有 2 个查询,当同时运行时会导致死锁。

查询 1 - 更新包含在索引 (index1) 中的列:

update table1 set column1 = value1 where id = @Id
Run Code Online (Sandbox Code Playgroud)

在 table1 上使用 X-Lock,然后在 index1 上尝试 X-Lock。

查询 2:

select columnx, columny, etc from table1 where {some condition}
Run Code Online (Sandbox Code Playgroud)

在 index1 上使用 S-Lock,然后在 table1 上尝试 S-Lock。

有没有办法在保持相同查询的同时防止死锁?例如,我可以在更新之前以某种方式在更新事务中的索引上使用 X-Lock 以确保表和索引访问的顺序相同 - 这应该防止死锁?

隔离级别为 Read Committed。为索引启用了行锁和页锁。同一个记录可能同时参与了两个查询 - 我无法从死锁图中判断出来,因为它没有显示参数。

死锁图

Pau*_*ite 16

有没有办法在保持相同查询的同时防止死锁?

死锁图显示此特定死锁是与书签查找(在本例中为 RID 查找)关联的转换死锁:

死锁图

正如问题所指出的,由于查询可能以不同的顺序在相同资源上获得不兼容的锁,因此会出现一般死锁风险。该SELECT查询需要访问表之前的指数,由于RID查询,而UPDATE查询修改表,然后再索引。

消除死锁需要去除死锁成分之一。以下是主要选项:

  1. 通过覆盖非聚集索引来避免 RID 查找。这在您的情况下可能不切实际,因为SELECT查询返回 26 列。
  2. 通过创建聚集索引来避免 RID 查找。这将涉及在列上创建聚集索引Proposal。这是值得考虑的,尽管此列似乎​​是类型uniqueidentifier,这可能是也可能不是聚集索引的好选择,具体取决于更广泛的问题。
  3. 通过启用READ_COMMITTED_SNAPSHOTSNAPSHOT数据库选项避免在读取时使用共享锁。这需要仔细测试,尤其是对于任何设计的阻塞行为。触发代码还需要测试以确保逻辑正确执行。
  4. 通过READ UNCOMMITTEDSELECT查询使用隔离级别,避免在读取时使用共享锁。所有通常的警告都适用。
  5. 通过使用排他应用程序锁(请参阅sp_getapplock),避免同时执行有问题的两个查询。
  6. 使用表锁提示来避免并发。这比选项 5 的影响更大,因为它可能会影响其他查询,而不仅仅是问题中确定的两个查询。

我可以在更新之前以某种方式对更新事务中的索引进行 X-Lock 以确保表和索引访问的顺序相同吗

你可以试试这个,通过在显式事务包装更新,并执行SELECTXLOCK在更新之前的非聚集索引值提示。这依赖于您确定地知道非聚集索引中的当前值是什么,获得正确的执行计划,并正确预测获取这个额外锁的所有副作用。它还依赖于锁定引擎不够聪明,无法避免在判断为多余的情况下获取锁定。

总之,虽然这在原则上是可行的,但我不建议这样做。很容易错过一些东西,或者以创造性的方式超越自己。如果您真的必须避免这些死锁(而不仅仅是检测它们并重试),我鼓励您查看上面列出的更通用的解决方案。