我遇到SQL Server的问题,我将请求更改为多线程,并且它在与数据库的多个连接上执行此存储过程并导致死锁。我无法想象它是如何陷入僵局的。如果我这样做单线程它运行正常。有一次(当我第一次写这篇文章时),它插入了22,000条没有死锁的记录(表是空的,因此在这种情况下没有更新)代码如下所示:
Create Procedure InsertUpdateRecord
@Email varchar(255),
@State Varchar(50) -- Other parameters, shouldn't matter
as
Begin
if (not exists (select * from [MyTable] WITH (NOLOCK) where email=@email))
begin
insert into [MyTable] (email, state) values (@email, @state)
end
else
begin
update [MyTable] set state=@state where email=@email
end
end
end
非常简单,除了简单的行锁定之外,我还没有看到任何可能做任何其他事情的电子邮件'电子邮件'并且我没有看到它的任何方式可能会造成僵局。有人可以解释一下并提供解决方案吗?
答案 0 :(得分:0)
这是您应该在查询中考虑UPDLOCK
的典型案例:
begin tran
if exists (select * from table with (updlock,serializable) where key = @key)
begin
update table set ...
where key = @key
end
else
begin
insert into table (key, ...)
values (@key, ...)
end
commit tran
UPDLOCK
如果您使用第一个选择按下记录,则会声明锁定,并避免多个方法尝试声明相同锁定的情况。
serializable
用于:
为了完整起见,您的表格已正确编入索引并且不是堆,对吗?
此外,如果不存在,请尝试向MyTable
添加索引:
CREATE NONCLUSTERED INDEX [IX_MyTable_email] ON [dbo].[MyTable]
(
[email] ASC
)
您也可以继续启用以下跟踪标记:
DBCC TRACEON (1204,-1)
DBCC TRACEON (1222,-1)
这不会阻止死锁,但会在SQL Server日志中提供有关它们的更多详细信息。它会告诉您交易试图声明的锁定以及当前拥有该锁定的锁定。
答案 1 :(得分:0)
从
更改此行代码if exists (select * from table where key = @key)
要
if exists (select 1 from table where key = @key)
这更有效率
还要遵循这些常见做法,以帮助最大限度地减少死锁: 以相同的顺序访问对象。 避免交易中的用户交互。 保持交易简短且一批交易。 使用较低的隔离级别。 使用基于行版本控制的隔离级别。 使用绑定连接。
详细说明如下:Minimize Deadlocks