快照,仍然死锁,ROWLOCK

Pat*_*tto 5 sql t-sql sql-server

我使用以下代码在我的数据库中打开了快照隔离

ALTER DATABASE MyDatabase
SET ALLOW_SNAPSHOT_ISOLATION ON

ALTER DATABASE MyDatabase
SET READ_COMMITTED_SNAPSHOT ON
Run Code Online (Sandbox Code Playgroud)

并摆脱了许多僵局.

但是当我需要每小时运行一个脚本来清理100,000多行时,我的数据库仍会产生死锁.

  • 有没有办法可以避免清理脚本中的死锁,我是否需要在该查询中专门设置ROWLOCK?
  • 有没有办法增加数据库使用的行级锁的数量?
  • 如何提升锁?从行级到页面级到表级?

我的删除脚本很简单:

delete statvalue
from statValue,
(select  dateadd(minute,-60, getdate()) as cutoff_date) cd
where temporaryStat = 1
and entrydate < cutoff_date
Run Code Online (Sandbox Code Playgroud)

现在我正在寻找快速解决方案,但长期解决方案会更好.

非常感谢,Patrikc

Rem*_*anu 6

SNAPSHOT隔离只能缓解(某些)涉及读取的死锁,但它绝对没有什么可以避免写入和写入死锁.如果每小时生成100k +行,即每秒约30次插入,那么删除扫描几乎可以保证与其他写入操作冲突.如果你所做的只是插入,永远不更新,那么删除块,但不是行锁定死锁,但由于表足够大而删除正在进行扫描,引擎可能会为删除选择页锁,因此可能是你得到的僵局.

没有入口日期的索引,删除别无选择,只能扫描整个表.经常插入顶部并在底部删除的这种表实际上是队列,您应该通过entrydate组织它们.这意味着entrydate可能应该是聚集索引中最左边的键.该组织允许清楚地分离在桌子末端发生的插入物与在另一端发生的删除.但这是一个相当激进的变化,特别是如果你使用statvalueid来读取这些值.我想你现在有一个基于自动增量字段(StatValueId)的聚簇索引.此外,我假设entrydate和statvalueid是相关的.如果两个假设都为真,那么你应该删除base not statvalueid:找到可以安全删除的最大id,然后删除此id左侧聚集索引上的所有内容:

declare @statvalueidmax int;
select @statvalueidmax = max(statvalueid) 
 from statvalue with (readpast)
 where entrydate <  dateadd(minute,-60, getdate());

delete statvalue
 where statvalueid <= @statvalueidmax;
Run Code Online (Sandbox Code Playgroud)

我做了很多假设,但可能是错的.但这个想法的要点是你必须将插入与删除分开,以便它们不重叠.


Mit*_*eat 0

减少(或避免)死锁的方法是分批删除,并在每批之间短暂等待(使用 WAITFOR DELAY)

此外,可以通过覆盖索引来缓解死锁。

此代码需要一些声明,并且仅作为示例(运行风险自负!)。

SELECT  @intRowCount = 1,
    @intErrNo = 0

DECLARE @cutoff_date DATETIME

SET @cutoff_date = dateadd(minute,-60, getdate())

SELECT  @intRowsToDelete = COUNT(*) -- Number of rows to be deleted
FROM dbo.statValue 
WHERE temporaryStat = 1
AND  entrydate < @cutoff_date

WHILE @intRowCount > 0 AND @intErrNo = 0
BEGIN

    SET ROWCOUNT @DEL_ROWCOUNT

    delete statvalue
    FROM dbo.statValue 
    WHERE temporaryStat = 1
    AND  entrydate < @cutoff_date

    SELECT  @intErrNo = @@ERROR, @intRowCount = @@ROWCOUNT

    SET ROWCOUNT 0  -- Reset batch size to "all"

    SELECT  @intRowsToDelete = @intRowsToDelete - @intRowCount

    WAITFOR DELAY '000:00:
 END
Run Code Online (Sandbox Code Playgroud)

我还纳入了约翰关于不要重复计算日期范围标准的建议。