多用户执行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基本上是这样的:
- 更新Table
- SELECT 从 Table
- 更新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
我们有一个存储过程,它是 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基本上是这样的:
- 更新Table
- SELECT 从 Table
- 更新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