交易 1 持有表 A 排他鎖、請求表 B;交易 2 持有表 B 排他鎖、請求表 A。雙方各佔一資源、互相等待,形成循環等待鏈。若無外部干預(死結偵測器選犧牲者回滾),將永久僵持。
CREATE PROC dbo.sp_WhoLock
AS
BEGIN
SET NOCOUNT ON;
DECLARE @spid SMALLINT, @blk SMALLINT;
CREATE TABLE #tmp (id INT IDENTITY(1,1), spid SMALLINT, blk SMALLINT);
INSERT #tmp(spid, blk)
SELECT 0, blocked FROM sys.sysprocesses WHERE blocked > 0
AND blocked NOT IN (SELECT spid FROM sys.sysprocesses WHERE blocked > 0)
UNION ALL
SELECT spid, blocked FROM sys.sysprocesses WHERE blocked > 0;
DECLARE @cnt INT = (SELECT COUNT(*) FROM #tmp), @i INT = 1;
IF @cnt = 0 PRINT '目前無阻塞/死結';
WHILE @i <= @cnt
BEGIN
SELECT @spid = spid, @blk = blk FROM #tmp WHERE id = @i;
IF @spid = 0
PRINT '引發死結的根因 SPID: ' + CAST(@blk AS VARCHAR) + ',其 SQL:';
ELSE
PRINT 'SPID ' + CAST(@spid AS VARCHAR) + ' 被 SPID ' + CAST(@blk AS VARCHAR) + ' 阻塞,SQL:';
DBCC INPUTBUFFER(@blk);
SET @i += 1;
END
DROP TABLE #tmp;
END;
執行 EXEC sp_WhoLock 即可列出阻塞鏈與對應 SQL 文本。
sp_getbindtoken/sp_bindsession 讓多連接共享鎖空間,避免自阻塞。WAITFOR DELAY 釋放鎖喘息。WITH (NOLOCK) 或設定 SNAPSHOT 隔離。sp_WhoLock 確認犧牲者 SPID。KILL <spid> 強制回滾釋放鎖。死結無法 100% 根除,但透過「統一順序、縮短交易、索引優化、隔離等級選擇」四大支柱,可將發生機率壓至極低。建立監控告警與自動化 KILL 機制,讓系統在極端情況下也能自癒。