Удаление строк в таблице вызывает LOCKS - PullRequest
11 голосов
/ 25 января 2012

Я выполняю следующую команду для удаления строк в пакетах из большой таблицы (150 миллионов строк):

DECLARE @RowCount int
WHILE 1=1
    BEGIN
        DELETE TOP (10000) t1
        FROM table t1
        INNER JOIN table2 t2 ON t2.PrimaryKey = t1.PrimaryKey
        WHERE t1.YearProcessed <= 2007

        SET @RowCount = @@ROWCOUNT

        IF (@RowCount < 10000) BREAK
    END

Эта таблица НАСТОЛЬКО используется.Тем не менее, он удаляет записи, но также вызывает блокировку некоторых записей, что приводит к ошибкам пользователя (что недопустимо в среде, в которой мы находимся).

Как удалить старые записи безвызывая замки?Должен ли я уменьшить размер пакета с 10000 записей до 1000?Как это повлияет на размеры журналов (у нас очень мало места на жестком диске для увеличения журнала).

Есть предложения?

Ответы [ 7 ]

7 голосов
/ 26 января 2012

В прошлом я сталкивался с подобными спорадическими проблемами, когда даже в небольших пакетах 0-5000 записей блокировка все равно происходила. В нашем случае каждое удаление / обновление содержалось в своем собственном цикле Begin Tran ... Commit. Чтобы исправить проблему, логика

Ожидание ЗАДЕРЖКИ '00: 00: 00: 01 '

был размещен в верхней части каждого цикла, и это исправило проблему.

4 голосов
/ 25 января 2012

Прежде всего - похоже, что ваш DELETE выполняет сканирование кластерного индекса, я рекомендую сделать следующее:

create index [IX.IndexName] ON t1(YearProcessed, PrimaryKey)

Второе - есть ли необходимость присоединиться к таблице t2?

А затем используйте следующий запрос для удаления строк, предполагая, что ваш столбец PrimaryKey имеет тип INT:

declare @ids TABLE(PrimaryKey INT)
WHILE 1=1
    BEGIN
        INSERT @ids 
        SELECT top 10000 DISTINCT t1.PrimaryKey
        FROM table t1
        INNER JOIN table2 t2 ON t2.PrimaryKey = t1.PrimaryKey
        WHERE t1.YearProcessed <= 2007

        IF @@ROWCOUNT = 0 BREAK

        DELETE  t1
        WHERE PrimaryKey in (Select PrimaryKey from @ids)

        delete from @ids

    END

И не забудьте удалить таблицу t2 из объединения, если она не нужна

Если это все еще вызывает блокировки - уменьшите количество строк, удаленных в каждом раунде

1 голос
/ 25 января 2012

Я думаю, вы на правильном пути.

Посмотрите и на эти две статьи:

и

http://www.dbforums.com/microsoft-sql-server/985516-deleting-without-locking.html

Перед запуском удаления, проверьте примерный план запроса, чтобы увидеть, если он выполняет поиск индекса для удаления или все еще выполняет полную таблицу сканирование / доступ.

0 голосов
/ 26 сентября 2017

Попробуйте это,

DECLARE @RowCount int
WHILE 1=1
    BEGIN
        BEGIN TRANSACTION 
        DELETE TOP (10000) t1
        FROM table t1
        INNER JOIN table2 t2 ON t2.PrimaryKey = t1.PrimaryKey
        WHERE t1.YearProcessed <= 2007
         END TRANSACTION 
         COMMIT TRANSACTION 
        SET @RowCount = @@ROWCOUNT

        IF (@RowCount < 10000) BREAK
    END
0 голосов
/ 25 сентября 2017

лучший способ, который я нашел - это форма asp.net DeleteExpiredSessions.Вы делаете выбор READUNCOMMITTED и помещаете записи во временную таблицу, а затем удаляете запись с помощью курсора.

 ALTER PROCEDURE [dbo].[DeleteExpiredSessions]
    AS
        SET NOCOUNT ON
        SET DEADLOCK_PRIORITY LOW 

        DECLARE @now datetime
        SET @now = GETUTCDATE() 

        CREATE TABLE #tblExpiredSessions 
        ( 
            SessionID nvarchar(88) NOT NULL PRIMARY KEY
        )

        INSERT #tblExpiredSessions (SessionID)
            SELECT SessionID
            FROM [ASPState].dbo.ASPStateTempSessions WITH (READUNCOMMITTED)
            WHERE Expires < @now

        IF @@ROWCOUNT <> 0 
        BEGIN 
            DECLARE ExpiredSessionCursor CURSOR LOCAL FORWARD_ONLY READ_ONLY
            FOR SELECT SessionID FROM #tblExpiredSessions 

            DECLARE @SessionID nvarchar(88)

            OPEN ExpiredSessionCursor

            FETCH NEXT FROM ExpiredSessionCursor INTO @SessionID

            WHILE @@FETCH_STATUS = 0 
                BEGIN
                    DELETE FROM [ASPState].dbo.ASPStateTempSessions WHERE SessionID = @SessionID AND Expires < @now
                    FETCH NEXT FROM ExpiredSessionCursor INTO @SessionID
                END

            CLOSE ExpiredSessionCursor

            DEALLOCATE ExpiredSessionCursor

        END 

        DROP TABLE #tblExpiredSessions

    RETURN 0   
0 голосов
/ 18 августа 2016

Если у вас есть что-то с каскадным удалением, убедитесь, что оно проиндексировано.

Подсветка запроса DELETE и нажатие Display estimated execution plan покажут предложенные индексы, которые в моем случае включали некоторые каскадные удаления.

Добавление индексов сделало удаление намного быстрее, но я все равно не буду пытаться удалить все строки сразу.

0 голосов
/ 25 января 2012

В дополнение к другим предложениям (которые направлены на сокращение работы, выполняемой во время удаления), вы также можете настроить SQL Server таким образом, чтобы он не блокировал других читателей при удалении из таблицы.

Это можно сделать с помощью «изоляции моментального снимка», представленной в SQL Server 2005:

http://msdn.microsoft.com/en-us/library/ms345124%28v=sql.90%29.aspx

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...