我们有一个存储过程正在插入/更新/删除项目资源文件,我们遇到一个问题,用户有时会遇到死锁,我相信是因为另一个用户正在使用存储过程。
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
如何修复它以便多个用户可以使用它或找出真正触发它的内容
答案 0 :(得分:1)
您的SP基本上是这样做的:
所有发生在同一交易中(其中2 + 3 = MERGE)
当两个实例同时运行语句1时,可能会发生死锁情况。这两个实例都不能执行语句2,因为表中的某些行被另一个实例中的更新锁定。
避免在具有多个更新的一个表上发生死锁的方法是确保不同的实例不会尝试读取可被其他实例锁定的行。常见错误是通过在WHERE子句中使用非索引列或聚合(例如MAX)来导致表扫描。
此查询可以为您(有点神秘)详细说明发生死锁的位置
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