如何防止 SQL 服务器存储过程中的死锁?

How to prevent deadlock in SQL Server stored procedure?

我正在调用一个存储过程,该过程执行 INSERTUPDATE 取决于 table 中存在的过程键。

到目前为止,程序按预期运行。直到我们的用户群开始扩大。今天我收到以下错误,通过重新启动应用程序池 运行 服务解决了该错误:

InsertDDM_UserDashboard error: RequestError: Transaction (Process ID 64) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction.

如何防止 SQL 服务器存储过程中的死锁?

我查看了 this link,这表明它可能是 SELECTUPDATE 运行ning 同时导致死锁的问题。但是我的程序用 IF..ELSE 条件将语句分开,因此两者不能同时 运行。

存储过程:

SET ANSI_NULLS ON
SET QUOTED_IDENTIFIER ON
GO

ALTER PROCEDURE [dbo].[InsertDDM_UserDashboard]
   @p_email VARCHAR(255),
   @p_dashboardPreferences VARCHAR(4000),
   @p_userDefaultDashboard VARCHAR(500)
AS 

IF (NOT EXISTS(SELECT * FROM [dbo].[DDM_UserProfile] WHERE Email = @p_email)) 
BEGIN    

    INSERT INTO [dbo].[DDM_UserProfile]
           ([Email]
           ,[DashboardPreferences]
           ,DefaultDashboard
           )
     VALUES
           (@p_email
           ,@p_dashboardPreferences
           ,@p_userDefaultDashboard
           )

END ELSE BEGIN

        UPDATE [dbo].[DDM_UserProfile]
        SET [DashboardPreferences]=@p_dashboardPreferences
        WHERE [Email]=@p_email

        UPDATE [dbo].[DDM_UserProfile]
        SET DefaultDashboard=@p_userDefaultDashboard
        WHERE [Email]=@p_email

END

需要查看 table 和索引 DDL 以及完整的死锁图才能确定,但​​您可能只需要在初始读取时锁定目标行。 EG

ALTER PROCEDURE [dbo].[InsertDDM_UserDashboard]
   @p_email VARCHAR(255),
   @p_dashboardPreferences VARCHAR(4000),
   @p_userDefaultDashboard VARCHAR(500)


AS 
begin
begin transaction

IF (NOT EXISTS(SELECT * FROM [dbo].[DDM_UserProfile] with (updlock, holdlock) WHERE Email = @p_email)) 
BEGIN     
INSERT INTO [dbo].[DDM_UserProfile]
        ([Email]
        ,[DashboardPreferences]
        ,DefaultDashboard
        )
    VALUES
        (@p_email
        ,@p_dashboardPreferences
        ,@p_userDefaultDashboard
        )

END

ELSE 
BEGIN 
    UPDATE [dbo].[DDM_UserProfile]
    SET [DashboardPreferences]=@p_dashboardPreferences,
        DefaultDashboard=@p_userDefaultDashboard
    WHERE [Email]=@p_email

END

commit transaction
end

您可以像这样使用 Sam Saffron upsert approach

create procedure dbo.ddm_UserProfile_Dashboard_upsert (
    @p_email varchar(255)
  , @p_dashboardPreferences varchar(4000)
  , @p_userDefaultDashboard varchar(500)
) as 
begin
  set nocount, xact_abort on;
  begin tran;
    update up
      set DashboardPreferences=@p_dashboardPreferences
        , DefaultDashboard    =@p_userDefaultDashboard
      from  dbo.ddm_UserProfile up with (serializable) 
      where up.Email = @p_email;
    if @@rowcount = 0
    begin;
      insert into dbo.ddm_UserProfile (Email, DashboardPreferences, DefaultDashboard)
      values (@p_email, @p_dashboardPreferences, @p_userDefaultDashboard);
    end;
  commit tran;
end;
go