我在游标内运行一个过程。经过多次成功的迭代后,我得到了这个:
事务(进程 ID 104)在锁定时死锁 |与另一个进程通信缓冲区资源并已被选为死锁受害者。重新运行交易。
我不会发布完整的详细信息,因此我不期望得到细粒度的调试答案。事实:
select
时,此事务被“卡住”(我看到来自 dm exec 请求的正在运行的查询)如果我的两点没有弄错,是否有可能出现僵局?死锁是否会要求资源的所有相关用户都对其进行写入操作,这会在资源请求图中创建一个循环?我理解
select
中的超时错误,但无法理解死锁。我错过了什么?
更新:
我放弃了进一步的调试,因为我注意到我认为存在的索引并不存在。创建时,性能还可以。
但是,为了保持这个有用并希望能找到答案,这里有我调查的更多事情、一些事实和评论的想法:
首先,SQL Server版本是2008。我知道这是不支持的。我无法提出建议,更不用说更新服务器了。
我发现 Jeroen Mostert 的评论很有趣。 “过去”有多少?我注意到在
sys.dm_os_waiting_tasks
中,会话被自身阻塞多次,且等待类型为 CXPACKET。我四处寻找,但option(maxdop 1)
并没有解决问题。但是,请记住不存在的索引将导致性能糟糕。难道是附加了正确的并行性,但是操作太多了?尽管如此,我也目睹了一个巨大的dm_exec_requests.wait_time
。因此,即使查询很糟糕,我还是相信周围存在奇怪的(死)锁。
如果答案/评论提出了追踪问题的具体查询/步骤,我很乐意重新创建它。
如果其他人正在使用该表,则 SELECT 可能会导致死锁。
此示例几乎 100% 源自 Brent Ozar 关于死锁的视频,但将一个命令更改为 SELECT。
首先,创建两个表
CREATE TABLE Lefty (ID int PRIMARY KEY)
CREATE TABLE Righty (ID int PRIMARY KEY)
INSERT INTO Lefty (ID) VALUES (1)
INSERT INTO Righty (ID) VALUES (2)
然后在SSMS中打开两个窗口。首先放入此代码(尚未运行)
BEGIN TRAN
UPDATE Lefty SET ID = 5
SELECT * FROM Righty
COMMIT TRAN
在第二个窗口中输入此代码(也不要运行它)。
BEGIN TRAN
UPDATE Righty SET ID = 5
UPDATE Lefty SET ID = 5
COMMIT TRAN
现在,在第一个窗口中,运行前两个命令 (
BEGIN TRAN
AND UPDATE LEFTY
)。
就这样开始了。
在第二个窗口中,运行整个事务。它坐在那里等待你的第一个窗口,并且会永远等待。
在第一个窗口中,返回并运行
SELECT * FROM Righty
和 COMMIT TRAN
。 5, 4, 3, 2, 1 Boom 死锁 - 因为第二个窗口已经锁定了表,因此第一个窗口中的 SELECT 无法运行(第二个窗口也无法运行,因为第一个窗口锁定了表)在需要的桌子上)。
(我想重申 - 这是 Brent Ozar 的演示,不是我的!我只是传递它。确实,我推荐它们)。