常常會須要分析SQLSERVER的阻塞狀況,尤爲是某些SQL操做異常緩慢從而懷疑是有人在搞事情的狀況下。網上有許多如出一轍的帖子,是關於sp_who_lock這個存儲過程的,然而,網上流傳的這個是略有問題的(被阻塞的SQL輸出有誤),爲此改造了一下實現,順便優化了一下輸出結構:sql
CREATE PROCEDURE [dbo].[sp_who_lock] AS BEGIN DECLARE @spid INT , @bl INT , @intTransactionCountOnEntry INT , @intRowcount INT , @intCountProperties INT , @intCounter INT, @sql_handle VARBINARY(64) DECLARE @tmp_lock_who TABLE ( id INT IDENTITY(1, 1) , spid SMALLINT , bl SMALLINT, sql_handle VARBINARY(64) ) IF @@ERROR <> 0 RETURN @@ERROR ; WITH tb_blocked AS( SELECT spid, blocked, sql_handle FROM master..sysprocesses WHERE blocked > 0 ) INSERT INTO @tmp_lock_who ( spid , bl, sql_handle ) SELECT DISTINCT blocked,0, p_bl.sql_handle FROM tb_blocked CROSS APPLY (SELECT p_bl.sql_handle FROM master..sysprocesses p_bl WHERE p_bl.spid = tb_blocked.blocked) p_bl WHERE NOT EXISTS ( SELECT * FROM tb_blocked a WHERE tb_blocked.blocked = a.spid ) UNION ALL SELECT spid, blocked, sql_handle FROM tb_blocked IF @@ERROR <> 0 RETURN @@ERROR -- 找到臨時表的記錄數 SELECT @intCountProperties = COUNT(*), @intCounter = 1 FROM @tmp_lock_who IF @@ERROR <> 0 RETURN @@ERROR IF @intCountProperties = 0 SELECT '如今沒有阻塞和死鎖信息' AS message -- 循環開始 WHILE @intCounter <= @intCountProperties BEGIN -- 取第一條記錄 SELECT @spid = spid, @bl = bl, @sql_handle = sql_handle FROM @tmp_lock_who WHERE id = @intCounter BEGIN IF @bl = 0 BEGIN SELECT '阻塞根源' + CAST(@spid AS VARCHAR(10)) AS [description], text AS [sql_text] FROM sys.dm_exec_sql_text(@sql_handle) AS dest END ELSE BEGIN SELECT CAST(@spid AS VARCHAR(10)) + '被' + CAST(@bl AS VARCHAR(10)) + '阻塞' AS [description], text AS [sql_text] FROM sys.dm_exec_sql_text(@sql_handle) AS dest END DBCC INPUTBUFFER(@spid) END -- 循環指針下移 SET @intCounter = @intCounter + 1 END RETURN 0 END GO
關於輸出的SQL文本,我使用了sys.dm_exec_sql_text與DBCC INPUTBUFFER兩種方式,這兩種方式是的結果是略有差異的,在SQL批裏有多條SQL語句的情形下,前者能夠精肯定位到當前阻塞/被阻塞是哪一條語句,然而輸出的並不是原始的SQL文本,然後者則輸出的是原始SQL批,但並不能精肯定位是哪一條。二者結合方可更快的排查問題。舉例以下:優化
假設有以下兩個連接的SQL語句:spa
連接一: 指針
BEGIN TRAN UPDATE dbo.t_UserDataAccess SET ObjectValue = '' WHERE UserID = 1024
連接二:code
BEGIN TRAN UPDATE dbo.t_UserDataAccess SET ObjectValue = '' WHERE UserID = 1023 SELECT * FROM dbo.t_UserDataAccess AS tuda WHERE UserID = 1024
在連接一和連接二順序執行的情形下,很顯然,連接2的SELECT語句將會被阻塞,這時來看sys.dm_exec_sql_text和DBCC INPUTBUFFER的不一樣表現:blog
應該不須要解釋了。ip