为什么单个SQL delete语句会导致死锁?

时间:2010-06-05 16:26:12

标签: sql-server-2008 deadlock

我正在使用SQL Server 2008 Enterprise。我想知道为什么即使这个存储过程的单个delete语句如果同时由多个线程执行也会导致死锁?

对于delete语句,Param1是表FooTable的列,Param1是另一个表的外键(指另一个表的另一个主键聚簇索引列)。对于表FooTable,Param1本身没有索引。 FooTable有另一列用作聚簇主键,但不是Param1列。

create PROCEDURE [dbo].[FooProc]    
(  
 @Param1 int 
 ,@Param2 int  
 ,@Param3 int  
)    
AS    

DELETE FooTable WHERE  Param1 = @Param1     

INSERT INTO FooTable    
 (  
 Param1  
 ,Param2  
 ,Param3  
  )    
 VALUES    
 (  
 @Param1  
 ,@Param2  
 ,@Param3  
  )    

DECLARE @ID bigint    
 SET @ID = ISNULL(@@Identity,-1)    
 IF @ID > 0    
 BEGIN    
      SELECT IdentityStr FROM FooTable WHERE ID = @ID 
 END  
提前谢谢, 乔治

3 个答案:

答案 0 :(得分:2)

我没有并发经验但是你的程序中有两件事我会改变(并且可能会修复你的死锁):

  • 将您的整个程序包裹在交易中。这是为了防止调用FooProc 1这样的场景 并且即将执行SELECT语句,而FooProc 2刚刚执行了具有相同@Param1的DELETE语句。
  • 请勿使用@@ Identity,请改用SCOPE_IDENTITY。

Interesting link关于@@ Identity vs SCOPE_IDENTITY()vs IDENT_CURRENT()

答案 1 :(得分:1)

通常的答案:这取决于! : - )

主要取决于您的系统流量以及您正在使用的事务隔离级别。

隔离级别控制您获取数据的方式以及锁定的数量。 如果您从未听说过事务隔离级别,那么您可能正在使用默认值 - READ COMMITTED,这应该是一个不错的选择。

但是,如果出于任何原因使用SERIALIZABLE之类的内容,您可能会遇到死锁 - 但会延迟。在一个事务完成之前,表可能会被锁定一段时间。如果所有操作按此顺序工作(首先删除,然后插入,然后选择),我看不出你应该如何遇到任何死锁,真的。

www.sql-server-performance.com上阅读SQL事务隔离级别。

答案 2 :(得分:0)

如果一个大表BarTable有一个引用FooTable的外键约束,那么删除FooTable中的一行就需要检查所有BarTables行以获取对该行的引用。

这不仅非常慢,而且变得更糟:在执行此检查时,FooTable中两个并发删除的两个单行可能会在BarTable的主键上出现死锁(!)

如果在BarTable上为引用FooTable的列创建索引,则删除可以使用该索引来显着加快速度。

所以:

  

确保您的外键约束由索引备份。