多重循环锁定 SQL 服务器数据库

Multiple Loop gives lock in SQL Server database

我很难理解我在 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

当我同时拥有 运行 并尝试查询表 1 或表 2 中的任何内容时,它只是被锁定了。

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

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 的 post 博客中找到关于“快速有序删除”的博客 - 它不会让你绕过删除集,但它可能会帮助你在不影响并发的情况下做到这一点 https://www.brentozar.com/archive/2018/04/how-to-delete-just-some-rows-from-a-really-big-table/