如何防止SQL Server存储过程中出现死锁?

时间:2017-05-22 16:25:39

标签: sql-server stored-procedures transactions deadlock

我正在调用一个存储过程,该存储过程根据表中存在的过程键执行INSERTUPDATE

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

  

InsertDDM_UserDashboard错误:RequestError:事务(进程ID 64)在锁资源上与另一个进程死锁,并被选为死锁牺牲品。重新运行该交易。

如何防止SQL Server存储过程中出现死锁?

我查看了this link,这表明可能是SELECTUPDATE同时运行导致死锁的问题。但我的程序将语句与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

2 个答案:

答案 0 :(得分:2)

你可以这样使用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

答案 1 :(得分:1)

需要查看表和索引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