在SQLServer 2012中插入表的死锁(10.50.6000.34)

时间:2016-10-18 14:34:29

标签: sql-server multithreading

我遇到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

非常简单,除了简单的行锁定之外,我还没有看到任何可能做任何其他事情的电子邮件'电子邮件'并且我没有看到它的任何方式可能会造成僵局。有人可以解释一下并提供解决方案吗?

2 个答案:

答案 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