【问题标题】:How to avoid Deadlock between Insert/Delete statements due to non clustered non unique indexes!如何避免由于非聚集非唯一索引导致的插入/删除语句之间的死锁!
【发布时间】:2011-03-14 22:29:44
【问题描述】:

最近我在客户端的 OLTP 框(Sql server 2005)上遇到了死锁场景 发现是两个存储过程被2个不同的线程调用造成的。

1、Insert sp,在X表中插入数据。

Insert Into X (col1 , col2  , col3  ) 
Values ('value 1' , 'value 2'  , 'value 3'  )

2、删除从X表中删除数据的sp。

  DELETE X  
  FROM X T1 WITH (NOLOCK)   
  INNER JOIN Y T2 WITH (NOLOCK)
  ON T1.[col2] = T2.[col2]  
   WHERE t2.date < 'date time value'  

X 表有一个唯一的聚簇主键和两个非聚簇的非唯一索引。 我通过设置 t1222 tace 标志分析了死锁,输出总结如下;

Insert sp 在列 1 的非聚集索引上获得了 IX 锁。 在此期间,删除 sp 正在等待列 1 的同一非聚集索引上的 X 锁定。

删除 sp 在第 2 列的非聚集索引上获得了 U 锁。 在此期间,插入 sp 正在等待列 2 的同一非聚集索引上的 IX 锁。

非常感谢任何避免死锁的想法或建议。

编辑

跟踪标志 t1222 的输出

deadlock-list  
deadlock victim=process3c77d68  
process-list  
process id=process3c12c58 taskpriority=0 logused=1044 waitresource=PAGE: 17:8:7726 waittime=1250 ownerId=5169682909 transactionname=user_transaction lasttranstarted=2011-02-03T03:34:03.443 XDES=0xfe64d78b0 lockMode=IX schedulerid=2 kpid=9544 status=suspended spid=219 sbid=0 ecid=0 priority=0 transcount=2 lastbatchstarted=2011-02-03T03:34:03.457 lastbatchcompleted=2011-02-03T03:34:03.453 clientapp=.Net SqlClient Data Provider hostname=HQMTSRV026 hostpid=3308 loginname=EASUser isolationlevel=read committed (2) xactid=5169682909 currentdb=17 lockTimeout=4294967295 clientoption1=671088672 clientoption2=128056  
executionStack  
frame procname=adhoc line=1 stmtend=296 sqlhandle=0x0200000084ce2a1d0e95a5623fa3a9c0981d422e33cab999  
(@1 int<c/>@2 varchar(8000)<c/>@3 nvarchar(4000))INSERT INTO [VB_Audit_TransactionDetail]([ItemID]<c/>[TransactionID]<c/>[ItemValue]) values(@1<c/>@2<c/>@3)
frame procname=adhoc line=1 stmtend=296 sqlhandle=0x02000000afcb1733f435fb93e13556600acf32bb32e10020
Insert Into VB_Audit_TransactionDetail (ItemID  <c/> TransactionID  <c/> ItemValue  ) Values (4 <c/> '0255978c-f56e-477e-b361-8abe62433cff'  <c/> N'HQOLB006'  )  
frame procname=EAS.dbo.SP_Insert line=13 stmtstart=482 stmtend=522 sqlhandle=0x03001100805efa5997d69400719600000100000000000000  
exec (@CommandText)  
inputbuf  
Proc [Database Id = 17 Object Id = 1509580416]  
process id=process3c77d68 taskpriority=0 logused=364 waitresource=PAGE: 17:6:334008 waittime=1234 ownerId=5169682116 transactionname=user_transaction lasttranstarted=2011-02-03T03:34:03.053 XDES=0xa8e297cd0 lockMode=X schedulerid=12 kpid=10300 status=suspended spid=327 sbid=0 ecid=0 priority=0 transcount=2 lastbatchstarted=2011-02-03T03:33:41.137 lastbatchcompleted=2011-02-03T03:33:41.133 clientapp=Microsoft SQL Server hostname=HQSSISSRV002 hostpid=7632 loginname=NBKDOM\SQLCSRVC isolationlevel=read committed (2) xactid=5169682116 currentdb=17 lockTimeout=4294967295 clientoption1=671350816 clientoption2=128056  
executionStack  
frame procname=EAS.dbo.PurgeAuditTransactionTables line=59 stmtstart=4202 stmtend=4728 sqlhandle=0x030011006354a2313d11ae00979a00000100000000000000  
DELETE [dbo].[Audit_TransactionDetail]  
FROM [dbo].[Audit_TransactionDetail] T1 WITH (NOLOCK)  
INNER JOIN [dbo].[Audit_NBKTransaction] T2 WITH (NOLOCK)ON T1.[TransactionID] = T2.[TransactionID]  
WHERE TransactionPostedDateTime < @LastReplicationDateTime  
frame procname=adhoc line=1 sqlhandle=0x0100110096968c0560c430ff190000000000000000000000  
EXEC PurgeAuditTransactionTables '02 Feb 2011 19:00:13:870'  
inputbuf  
EXEC PurgeAuditTransactionTables '02 Feb 2011 19:00:13:870'  
resource-list  
pagelock fileid=8 pageid=7726 dbid=17 objectname=EAS.dbo.Audit_TransactionDetail id=lock4f79500 mode=U associatedObjectId=886415243542528  
owner-list  
owner id=process3c77d68 mode=U  
waiter-list  
waiter id=process3c12c58 mode=IX requestType=wait  
pagelock fileid=6 pageid=334008 dbid=17 objectname=EAS.dbo.Audit_TransactionDetail id=lock846afca00 mode=IX associatedObjectId=604940266831872  
owner-list  
owner id=process3c12c58 mode=IX  
waiter-list  
waiter id=process3c77d68 mode=X requestType=wait  

还有一件更重要的事情;删除和插入语句总是涉及 2 组不同的数据。

【问题讨论】:

  • 删除影响了多少行?
  • 你能发布原始的 t1222 跟踪输出吗?
  • 我已合并您未注册的帐户。请简单地编辑您的问题以提供更多信息。答案应该是问题的解决方案,而不是附加信息:)

标签: sql-server database indexing deadlock


【解决方案1】:

与其发布您对死锁图的理解的描述,不如发布死锁图本身。 XML,而不是图形渲染的位图。乍一看,涉及的 IX 锁存在冲突这一事实表明正在发生锁升级,这表明没有索引可以为 DELETE 提供服务,或者连接上的索引临界点被命中。但话又说回来,这只是由于信息不足而引起的猜测。要给出任何有意义的答案,需要实际的死锁 XML 和所涉及对象的确切架构定义。

更新后

您仍然没有提供所要求的信息:所涉及的所有对象的确切架构,包括聚集和所有非聚集索引定义。在那之前,最初的怀疑仍然存在:DELETE 正在执行表扫描并且已升级到页面锁定粒度。这是由于索引错误造成的。

您对“delete 和 insert 语句总是触及 2 组不同的数据集”的评价在两个帐户上是错误的:

  • 当查询进行表扫描时,它会自动暗示它将触及所有数据,而与实际符合条件的行无关
  • 即使在所有操作都有覆盖索引的经过适当调整的数据库上,锁也会被散列并且散列冲突的方式比预期的要频繁。由于birthday paradox,大型扫描将与仅其自己的扫描行发生冲突。见%%lockres%% collision probability magic marker: 16,777,215

作为旁注,审计表几乎总是需要按事件日期/时间进行聚类,因为对它们的所有查询都需要特定的时间间隔(“...和...之间发生了什么”),并且项目搜索可以是由 ID 中的非聚集主键满足。清除审计记录,即使在正确集群的情况下,也充满了性能问题,并且需要批处理以避免日志爆炸。最好的解决方案是部署automated sliding window using partitioning,但这也有其自身的挑战。

【讨论】:

    【解决方案2】:

    我猜桌子 T2 一定很大。列 t2.date 是否已编入索引?如果没有,那么对大表进行表扫描可能会导致您的问题。索引该列可以通过避免表扫描来优化删除。或者,如果 col1 或 col2 上的索引没有真正被使用(或使用得足够多),删除它们也可能会避免问题。

    这些死锁多久发生一次?如果它们非常罕见,一个笨拙的解决方法可能就足够了:将每个语句包装在 try/catch 块中,在 catch 检查错误是否是由于死锁,如果是,重试命令。您也可以小心使用SET DEADLOCK_PRIORITY 来选择哪个查询总是会赢/输(但您必须平衡所有对表格的调用)。

    哦,放弃那些WITH (NOLOCK)s。 NOLOCK 被插入、更新和删除忽略。

    【讨论】:

      【解决方案3】:

      避免“插入与删除”死锁的一般规则是:

      加快删除速度。

      您的删除操作有一个复杂的连接一个“小于”条件,如果该列没有正确索引,可能会导致表扫描。

      【讨论】:

        猜你喜欢
        • 2017-06-10
        • 1970-01-01
        • 1970-01-01
        • 2014-11-04
        • 2011-09-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多