2

我有两个 SPID 作为事务运行,下面是每个 SPID 的作用。

SPID A -

  1. 更新表 1 中的第 1-10 行
  2. …………

SPID B -

  1. 更新表 1 中的第 5 行
  2. 从表 1 中选择第 4,5 和 6 行并更新表 2 中的一行。
  3. ……
  4. ……

我面临一个死锁场景,其中 SPID A 已更新第 4 行并等待聚集索引上的 X 锁定以更新第 5 行,而 SPID B 在第 2 步中等待第 4 行的聚集索引上的 S 锁定在 X由 SPID A 锁定。表 1 上还有一个非聚集索引,但它不涵盖选择查询,因此它查找聚集索引。我假设即使我将其设为覆盖索引 SPID A 也会锁定非聚集索引,从而使 SPID B 等待它。我正在考虑将隔离级别升级为序列化,以便在 SPID B 已经开始更新到第 5 行时,或多或少更新整个表的 SPID A 将不得不等待获取其锁定。它还将阻止 SPID B如果 SPID A 已经开始,则从获取 x 锁。

你们还有什么其他建议可以让我摆脱这种僵局?谢谢。

4

1 回答 1

0

请查看此博客条目。这包含一个方便的SQL 脚本来显示现在存在的锁,并显示有关它们的所有信息:

SELECT
db.name DBName,
tl.request_session_id,
wt.blocking_session_id,
OBJECT_NAME(p.OBJECT_ID) BlockedObjectName,
tl.resource_type,
h1.TEXT AS RequestingText,
h2.TEXT AS BlockingTest,
tl.request_mode
FROM sys.dm_tran_locks AS tl
INNER JOIN sys.databases db ON db.database_id = tl.resource_database_id
INNER JOIN sys.dm_os_waiting_tasks AS wt ON tl.lock_owner_address = wt.resource_address
INNER JOIN sys.partitions AS p ON p.hobt_id = tl.resource_associated_entity_id
INNER JOIN sys.dm_exec_connections ec1 ON ec1.session_id = tl.request_session_id
INNER JOIN sys.dm_exec_connections ec2 ON ec2.session_id = wt.blocking_session_id
CROSS APPLY sys.dm_exec_sql_text(ec1.most_recent_sql_handle) AS h1
CROSS APPLY sys.dm_exec_sql_text(ec2.most_recent_sql_handle) AS h2
GO

一般来说,我的猜测只是你的 SQL Server 默认使用 PAGE 锁(就像每个 SQL Server 一样),这样对单行的更新可以阻止同一 PAGE 上的所有行。请参阅这篇文章以进一步了解 SQL Server 中的锁定,而此博客只是为了好玩。该博客解释说,在 oracle 之后可能会很奇怪,选择可以阻止更新,这在 SQL 服务器中是可能的。

为避免您的问题,请在更新中使用ROWLOCK提示,您将失去一点性能和死锁。第二种解决方案是在SQL server 中设置较低的死锁检测超时,并捕获SQL server 错误,如果是死锁牺牲品错误,则重试。

于 2012-10-21T18:54:17.133 回答