【问题标题】:Multiple Loop gives lock in SQL Server database多重循环锁定 SQL Server 数据库
【发布时间】:2023-03-10 11:04:02
【问题描述】:

我很难理解我在 SQL Server 2008 中看到的行为。

我必须创建一个作业,以一种不会创建任何锁的方式删除表内的一些数据。我被建议使用循环来解决这个问题,因为这些表被大量使用并且非常大。

所以以下运行完美,当我自己运行时,查询不会锁定数据库:

DECLARE @pkQ BIGINT
            
DECLARE DEL_CURSOR CURSOR STATIC FOR Select PK from Table1 where Inserted_Date <= DateAdd(WEEK, -1, Getdate()) order by PK desc

OPEN DEL_CURSOR

    FETCH NEXT FROM DEL_CURSOR into @pkQ
    WHILE @@FETCH_STATUS = 0
            BEGIN
                DELETE TOP(10) from Table1 where PK <= @pkQ
                FETCH NEXT FROM DEL_CURSOR into @pkQ
                PRINT '10 deleted from Table1'
                WaitFor DELAY '00:00:01'
            END
CLOSE DEL_CURSOR
DEALLOCATE DEL_CURSOR
PRINT 'Cursor Closed'

但是,如果我有 2 个不同的光标,它就会中断。

DECLARE @pkQ BIGINT
            
DECLARE DEL_CURSOR CURSOR STATIC FOR Select PK from Table1 where Inserted_Date <= DateAdd(WEEK, -1, Getdate()) order by PK desc

OPEN DEL_CURSOR

    FETCH NEXT FROM DEL_CURSOR into @pkQ
    WHILE @@FETCH_STATUS = 0
            BEGIN
                DELETE TOP(10) from Table1 where PK <= @pkQ
                FETCH NEXT FROM DEL_CURSOR into @pkQ
                PRINT '10 deleted from Table1'
                WaitFor DELAY '00:00:01'
            END
CLOSE DEL_CURSOR
DEALLOCATE DEL_CURSOR
PRINT ''Cursor Closed''


                    print N'In SecondCursor'
    DECLARE DEL_CURSORR CURSOR FOR Select top 1000 PK from Table2 where Insert_Date < DateAdd(Month, -6, Getdate()) order by PK desc
    
    OPEN DEL_CURSORR
    
        FETCH NEXT FROM DEL_CURSORR into @pkQ
        WHILE @@FETCH_STATUS = 0
                BEGIN
                    WAITFOR DELAY '00:00:02'
                    Delete top(10) from Table2 where PK <= @pkQ 
                    FETCH NEXT FROM DEL_CURSORR into @pkQ
                    WaitFor DELAY '00:00:01'
                    PRINT '10 deleted from Table2'
                END
    CLOSE DEL_CURSORR
    DEALLOCATE DEL_CURSORR

当我同时运行并尝试查询 table1 或 table2 中的任何内容时,它只是被锁定。

【问题讨论】:

  • 根据您的总记录数,那些WAITFOR 延迟肯定会受到伤害。只有 30k 行,这是 50 分钟的空闲时间。
  • “我必须创建一个作业,以一种不会创建任何锁的方式删除表中的一些数据。” 你不能......你一直在给出一个无意义的要求。执行此类过程的一部分意味着行必须被锁定以进行删除。此外,任何页面和索引都需要更新,这可能会导致锁定。
  • 我的意思是应用程序必须能够在作业运行时询问表。我知道要删除,必须有一个锁。我刚刚尝试了“With (Rowlock),但没有取得多大成功。

标签: sql sql-server tsql sql-server-2008


【解决方案1】:

这是我为此设置的测试数据:

DROP TABLE IF EXISTS #Table1;
DROP TABLE IF EXISTS #Table2;

SELECT 1001999 + n AS ID
  INTO #Table1
  FROM (SELECT TOP (30000)
               n = ROW_NUMBER() OVER (ORDER BY s1.[object_id])
          FROM sys.all_objects      AS s1
         CROSS JOIN sys.all_objects AS s2
         ORDER BY s1.[object_id]) AS x;

SELECT 1001999 + n AS ID
  INTO #Table2
  FROM (SELECT TOP (30000)
               n = ROW_NUMBER() OVER (ORDER BY s1.[object_id])
          FROM sys.all_objects      AS s1
         CROSS JOIN sys.all_objects AS s2
         ORDER BY s1.[object_id]) AS x;

使用(可能)比您尝试删除的数据集小得多的数据集。

当我运行您指定的 DELETE 模式时,每个光标中都有 WAITFOR DELAY,我让它运行了 20 分钟,然后就放弃了。消息窗格中没有打印任何内容,它的行为与您描述的一样。

当我注释掉等待时,代码(已修改以适合我的示例)如下所示:

DECLARE @pkQ BIGINT;

DECLARE DEL_CURSOR CURSOR STATIC FOR
    SELECT ID
      FROM #Table1
     ORDER BY ID DESC;

OPEN DEL_CURSOR;

FETCH NEXT FROM DEL_CURSOR
 INTO @pkQ;
WHILE @@FETCH_STATUS = 0
    BEGIN
        DELETE TOP (10)
        FROM #Table1
         WHERE ID <= @pkQ;
        FETCH NEXT FROM DEL_CURSOR
         INTO @pkQ;
        PRINT '10 deleted from Table1';
        --WAITFOR DELAY '00:00:01';
    END;
CLOSE DEL_CURSOR;
DEALLOCATE DEL_CURSOR;
PRINT 'Cursor Closed';


PRINT N'In SecondCursor';
DECLARE DEL_CURSORR CURSOR FOR
    SELECT TOP 1000
           ID
      FROM #Table2
     ORDER BY ID DESC;

OPEN DEL_CURSORR;

FETCH NEXT FROM DEL_CURSORR
 INTO @pkQ;
WHILE @@FETCH_STATUS = 0
    BEGIN
        --WAITFOR DELAY '00:00:02';
        DELETE TOP (10)
        FROM #Table2
         WHERE ID <= @pkQ;
        FETCH NEXT FROM DEL_CURSORR
         INTO @pkQ;
        --WAITFOR DELAY '00:00:01';
        PRINT '10 deleted from Table2';
    END;
CLOSE DEL_CURSORR;
DEALLOCATE DEL_CURSORR;

我在 6 秒内成功完成。 即使只有区区 30k 行,WAITFOR DELAY 00:00:01 也会为此任务增加 50 分钟的非生产时间。

最后说明:根据表的大小和您要删除的数量,您可能会发现 Brent Ozar 的这篇关于“快速有序删除”的博文——它不会让您绕过删除集,但它可能会帮助您在不影响并发的情况下执行此操作 https://www.brentozar.com/archive/2018/04/how-to-delete-just-some-rows-from-a-really-big-table/

【讨论】:

  • 很棒的工作。我一定会看看的。此外,我最终通过使用“WITH(ROWLOCK)”并隔离事务来纠正问题。但这非常非常有趣。非常感谢。
  • 我通常对使用查询提示非常谨慎,直到我真正按照我所追求的特定查询的步伐运行它们。我怀疑(尽管我不知道)ROWLOCK 提示仍将升级为带有DELETE TOP(n) 的表锁
  • 这个stackoverflow.com/questions/3005973/… 线程有一些有用的信息,并且有点含蓄地指出,如果兑现,ROWLOCK 在性能方面可能比仅仅让一个短暂的表锁定更糟糕被采取
  • 感谢您提供此信息。我真的不介意这项工作需要很长时间才能运行并且效率低下,如果这意味着不会有表范围的锁,因为任何表锁都可能导致客户端的应用程序超时。再次感谢您提供的信息。非常感谢。
  • @InfiniteLoop ROWLOCKDELETE 上没有多大意义,因为只有整个页面被弄脏,因此任何修改查询都需要页面锁定
猜你喜欢
  • 1970-01-01
  • 2022-12-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-09-01
  • 2016-08-17
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多