我正在调用一个存储过程,该存储过程根据表中存在的过程键执行INSERT
或UPDATE
。
到目前为止,该程序按预期工作。直到我们的用户群开始增长。今天我收到了以下错误,该错误通过重新启动运行服务的应用程序池解决:
InsertDDM_UserDashboard错误:RequestError:事务(进程ID 64)在锁资源上与另一个进程死锁,并被选为死锁牺牲品。重新运行该交易。
如何防止SQL Server存储过程中出现死锁?
我查看了this link,这表明可能是SELECT
和UPDATE
同时运行导致死锁的问题。但我的程序将语句与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
答案 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