2017-05-22 110 views
1

我正在調用一個存儲過程,該存儲過程根據表中存在的過程密鑰執行INSERTUPDATE如何防止SQL Server存儲過程中出現死鎖?

到目前爲止程序正常工作。直到我們的用戶基礎開始增長。今天,我得到了以下錯誤這是通過重新啓動應用程序池運行的服務解決:

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 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][email protected]_dashboardPreferences 
     WHERE [Email][email protected]_email 

     UPDATE [dbo].[DDM_UserProfile] 
     SET [email protected]_userDefaultDashboard 
     WHERE [Email][email protected]_email 

END 

回答

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][email protected]_dashboardPreferences, 
     [email protected]_userDefaultDashboard 
    WHERE [Email][email protected]_email 

END 

commit transaction 
end 
1

您可以使用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 [email protected]_dashboardPreferences 
     , DefaultDashboard [email protected]_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