Delete 语句上的死锁

K09*_*K09 11 sql-server deadlock

SQL Server 作业运行时出现死锁。死锁发生在一个简单的 DELETE 语句上。我原以为必须运行 SELECT/UPDATE 查询才能导致死锁?但看起来是 DELETE/DELETE 死锁...

我正在寻找的是为什么我会遇到 DELETE/DELETE 死锁。这是(据我所知)传入不同的参数。

有任何想法吗?谢谢。

deadlock-list
2014-05-20 07:30:09.66 spid25s      deadlock victim=process409048
2014-05-20 07:30:09.66 spid25s       process-list
2014-05-20 07:30:09.66 spid25s        process id=process409048 taskpriority=0 logused=0 waitresource=PAGE: 12:1:7127294 waittime=4352 ownerId=629860973 transactionname=DELETE lasttranstarted=2014-05-20T07:30:05.307 XDES=0x397219620 lockMode=U schedulerid=5 kpid=3792 status=suspended spid=150 sbid=0 ecid=3 priority=0 trancount=0 lastbatchstarted=2014-05-20T07:30:05.307 lastbatchcompleted=2014-05-20T07:30:05.307 clientapp=QSQL25 hostname=MORRIS hostpid=1528 isolationlevel=read committed (2) xactid=629860973 currentdb=12 lockTimeout=4294967295 clientoption1=671088672 clientoption2=128056
2014-05-20 07:30:09.66 spid25s         executionStack
2014-05-20 07:30:09.66 spid25s          frame procname=adhoc line=1 stmtstart=68 sqlhandle=0x020000000b887a18f75d0aa07c25a9b8630fca696aa0e5d2
2014-05-20 07:30:09.66 spid25s     DELETE FROM dbo.UserDetailsData WHERE        (Username = @P1) AND (UserDate = @P2)     
2014-05-20 07:30:09.66 spid25s          frame procname=unknown line=1 sqlhandle=0x000000000000000000000000000000000000000000000000
2014-05-20 07:30:09.66 spid25s     unknown     
2014-05-20 07:30:09.66 spid25s         inputbuf
2014-05-20 07:30:09.66 spid25s        process id=process432e08 taskpriority=0 logused=0 waitresource=PAGE: 12:1:7127916 waittime=2648 ownerId=629859744 transactionname=DELETE lasttranstarted=2014-05-20T07:30:04.833 XDES=0x4c3426b50 lockMode=U schedulerid=6 kpid=5988 status=suspended spid=146 sbid=0 ecid=3 priority=0 trancount=0 lastbatchstarted=2014-05-20T07:30:04.833 lastbatchcompleted=2014-05-20T07:30:04.820 clientapp=QSQL25 hostname=MORRIS hostpid=1528 isolationlevel=read committed (2) xactid=629859744 currentdb=12 lockTimeout=4294967295 clientoption1=671088672 clientoption2=128056
2014-05-20 07:30:09.66 spid25s         executionStack
2014-05-20 07:30:09.66 spid25s          frame procname=adhoc line=1 stmtstart=68 sqlhandle=0x020000000b887a18f75d0aa07c25a9b8630fca696aa0e5d2
2014-05-20 07:30:09.66 spid25s     DELETE FROM dbo.UserDetailsData WHERE        (Username = @P1) AND (UserDate = @P2)     
2014-05-20 07:30:09.66 spid25s          frame procname=unknown line=1 sqlhandle=0x000000000000000000000000000000000000000000000000
2014-05-20 07:30:09.66 spid25s     unknown     
2014-05-20 07:30:09.66 spid25s         inputbuf
2014-05-20 07:30:09.66 spid25s        process id=process39ea562c8 taskpriority=0 logused=0 waitresource=PAGE: 12:1:7127916 waittime=4352 ownerId=629860973 transactionname=DELETE lasttranstarted=2014-05-20T07:30:05.307 XDES=0x13e0e4b50 lockMode=U schedulerid=2 kpid=7124 status=suspended spid=150 sbid=0 ecid=1 priority=0 trancount=0 lastbatchstarted=2014-05-20T07:30:05.307 lastbatchcompleted=2014-05-20T07:30:05.307 clientapp=QSQL25 hostname=MORRIS hostpid=1528 isolationlevel=read committed (2) xactid=629860973 currentdb=12 lockTimeout=4294967295 clientoption1=671088672 clientoption2=128056
2014-05-20 07:30:09.66 spid25s         executionStack
2014-05-20 07:30:09.66 spid25s          frame procname=adhoc line=1 stmtstart=68 sqlhandle=0x020000000b887a18f75d0aa07c25a9b8630fca696aa0e5d2
Run Code Online (Sandbox Code Playgroud)

Pau*_*ite 15

我正在寻找的是为什么我会遇到 DELETE/DELETE 死锁。

出现死锁的原因似乎是:

  1. spid 54 ecid 0获取更新 ( U) 页锁定PAGE: 12:1:5147422
  2. spid 166 ecid 3U在同一页面上请求更新 ( ) 页面锁定,并被阻止
  3. spid 54 ecid 2U在同一页面上请求更新 ( ) 页面锁定...

正在为查询预取页面,并由 获取更新锁ecid 0。那是上面的第1步。在步骤 3 中,同一并行查询 ( ecid 2)的子线程请求相同的锁。通常这不会有问题。SQL Server 知道ecid 0并且ecid 2是同一父进程的线程。不幸的是,第 2 步妨碍了这一点,并导致死锁。

也就是说,您不应该真正关心为什么会发生死锁,重要的问题是您如何避免它。答案是为DELETE. 该语句需要查找 rows WHERE Username = @P1 AND UserDate = @P2,因此您应该在这些列上设置索引。

当然,您确实有这样的索引。在真正的问题是,为什么你的问题开始您添加过滤索引后发生。

答案是需要额外的列信息来定位要删除的过滤索引行(并检查它们的谓词)。如果查询使用窄/每行执行计划,则执行引擎无法像在宽/每索引计划中那样获取聚集索引删除运算符中的额外列。

您可以在此博客文章中找到有关此的更多详细信息以及有效示例。

在这种情况下,列信息需要来自聚簇索引删除右侧的计划部分,因此使用并行聚簇索引扫描,并且您得到具有高死锁可能性的慢查询。

答案是执行以下操作之一:

  1. 删除过滤的索引
  2. 将过滤的索引键/包含/谓词列添加到现有名称/日期索引
  3. 强制执行广泛的更新计划(不支持执行此操作的方法)
  4. 在快照隔离(不是 RCSI)下运行查询

选项 2 将是我的强烈偏好。

选项 4(感谢 Jack Douglas)具有消除死锁的优势,考虑到更改的不相交性质,不应导致任何“更新冲突”,但它确实需要在数据库级别启用快照隔离,显式更改隔离级别,并且不会解决根本问题:您仍然会以浪费的并行表扫描结束,而一个好的索引查找正是您真正想要的。