SQLSERVER——查看阻塞信息(sp_who_lock優化無誤版)

  常常會須要分析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

相關文章
相關標籤/搜索