多用户执行SP时死锁

deadlocking when multple user execute SP

我们有一个存储过程,它是 inserting/updateing/deleteing 项目资源文件,我们有一个问题,用户有时会陷入僵局,我相信是因为另一个用户正在使用该存储过程。

SP

  ALTER PROCEDURE [file].[usp_iudItemResourceFile]
    @p_ItemID INT
    , @p_ID INT = NULL OUTPUT
    , @p_IsDefault BIT = NULL
    , @p_Description NVARCHAR ( MAX ) = NULL

    --, @p_RelPath NVARCHAR ( 2000 ) = NULL
    , @p_Name NVARCHAR ( 250 ) = NULL
    , @p_sequenceNumberID INT = NULL
    , @p_Type TINYINT = NULL
    , @p_Size INT = NULL
    , @p_Status TINYINT = NULL
    , @p_DoerTicket VARCHAR ( 200 ) = NULL
AS
BEGIN
    SET NOCOUNT ON;
    SET XACT_ABORT ON;

    DECLARE @doerUserID INT
            , @doerCompanyID INT
    EXEC system.usp_validateAuthenticationTicket @p_Ticket = @p_DoerTicket
                                                , @p_UserID = @doerUserID OUTPUT
                                                , @p_CompanyID = @doerCompanyID OUTPUT


    BEGIN TRANSACTION

    IF ( @p_ID < 0 )
    BEGIN
        PRINT 'TBD'
    END

    DECLARE @res INT
    EXEC @res = [file].usp_iudResourceFile @p_ID = @p_ID OUTPUT
                                    --, @p_RelPath = @p_RelPath
                                    , @p_Name = @p_Name
                                    , @p_Type = @p_Type
                                    , @p_Size = @p_Size
                                    , @p_Status = @p_Status
                                    , @p_DoerTicket = @p_DoerTicket

    IF ( ( @@ERROR <> 0 ) OR ( @res <> 0 ) )    
    BEGIN
        IF ( @@TRANCOUNT > 0 ) ROLLBACK TRANSACTION
        RETURN 1050
    END

    IF ( ( @p_ID > 0 ) AND ( @p_IsDefault = 1 ) )
    BEGIN
        UPDATE [file].ItemResourceFile SET
            IsDefault = 0
        WHERE Item_ID = @p_ItemID

        IF ( @@ERROR <> 0 ) 
        BEGIN
            IF ( @@TRANCOUNT > 0 ) ROLLBACK TRANSACTION
            RETURN 1053
        END
    END

    MERGE INTO [file].ItemResourceFile AS target
    USING ( SELECT @p_ItemID
                    , @p_ID
                    , @p_IsDefault
                    , @p_Description
                    , @p_Status
                    , @p_sequenceNumberID
                    , @doerUserID
                    , @doerCompanyID ) AS source ( ItemID, ResourceFileID, IsDefault, Description, Status, sequenceID, DoerUserID, DoerCompanyID )
        ON ( target.Item_ID = source.ItemID )
            AND ( target.ResourceFile_ID = source.ResourceFileID )
        WHEN MATCHED THEN
            UPDATE SET 
                target.Description = NULLIF ( ISNULL ( source.Description, target.Description ), N'' )
                , target.IsDefault = ISNULL ( source.IsDefault, target.IsDefault )
                , target.Status = ISNULL ( source.Status, target.Status )
                , target.sequenceID = source.sequenceID
                , target.LastModifierUser_ID = source.DoerUserID
                , target.DateTimeModified = GETUTCDATE ( )
        WHEN NOT MATCHED BY TARGET AND source.ResourceFileID > 0 THEN
            INSERT ( Item_ID
                    , ResourceFile_ID
                    , Description
                    , IsDefault
                    , Status
                    , sequenceID
                    , CreatorUser_ID
                    , LastModifierUser_ID )
                VALUES ( source.ItemID
                        , source.ResourceFileID
                        , NULLIF ( source.Description, N'' )
                        , ISNULL ( source.IsDefault, 0 )
                        , ISNULL ( source.Status, 0 ) --0: Active
                        , source.sequenceID
                        , source.DoerUserID
                        , source.DoerUserID
                        )
        WHEN NOT MATCHED BY SOURCE 
                AND target.Item_ID = @p_ItemID
                AND target.ResourceFile_ID = ABS ( @p_ID ) THEN
            DELETE;

    IF ( @@ROWCOUNT <> 1 )
    BEGIN
        IF ( @@TRANCOUNT > 0 ) ROLLBACK TRANSACTION
        RAISERROR ( 'DBException_ItemNotFound', 16, 1 )
        RETURN 1060
    END

    --IF ( @p_ID IS NULL )
    --  SET @p_ID = SCOPE_IDENTITY ( )

    COMMIT TRANSACTION

    RETURN 0
END

我该如何修复它以便它可以被多个用户使用或找出真正触发它的原因

你的SP基本上是这样的:

  1. 更新Table
  2. SELECT 从 Table
  3. 更新Table或插入Table

所有发生在同一个事务中(其中 2+3 = MERGE)

当两个实例同时运行语句 1 时,可能会发生死锁情况。两个实例都无法执行语句 2,因为 table 中的某些行被另一个实例中的更新锁定。

通过多次更新避免一个 table 死锁的方法是确保不同的实例不会尝试读取可以被其他实例锁定的行。一个常见的错误是在 WHERE 子句或聚合(例如 MAX)中使用非索引列导致 table 扫描。

此查询可以为您提供(有些神秘的)有关死锁发生位置的详细信息

WITH XmlData AS (
    SELECT CONVERT(xml, [target_data]) AS Target_Data
    FROM sys.dm_xe_session_targets AS xt
         INNER JOIN sys.dm_xe_sessions AS xs
             ON xs.address = xt.event_session_address
    WHERE xs.name = N'system_health'
          AND xt.target_name = N'ring_buffer'
)
SELECT xed.value('@timestamp', 'datetime') as Creation_Date,
       xed.query('.') AS Extend_Event
FROM XmlData 
     CROSS APPLY Target_Data.nodes('RingBufferTarget/event[@name="xml_deadlock_report"]') AS XEventData(xed)
ORDER BY Creation_Date DESC