Удаление строк в таблице вызывает блокировки
Я запускаю следующую команду для удаления строк в пакетах из большой таблицы (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 ответов
Я видел подобные спорадические проблемы в прошлом, где даже в небольших партиях 0f 5000 записей блокировка все равно произойдет. В нашем случае каждое удаление / обновление содержалось в собственном Begin Tran...Цикл фиксации. Чтобы исправить проблему, логика
WaitFor задержка '00:00:00:01'
был помещен в верхней части каждого цикла и что исправили проблему.
прежде всего-похоже, что ваше удаление выполняет сканирование кластеризованного индекса, я рекомендую сделать следующее:
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 из join, если она не нужна
если он по-прежнему вызывает блокировки-тогда уменьшите количество строк, удаленных в каждом раунд
Я думаю, что вы на правильном пути.
посмотрите и на эти две статьи:
и:
http://www.dbforums.com/microsoft-sql-server/985516-deleting-without-locking.html
перед запуском delete проверьте план предполагаемого запроса, чтобы увидеть, если он выполняет поиск индекса для удаления или все еще делает полную таблицу сканирование / доступ.
в дополнение к другим предложениям (которые направлены на сокращение работы, выполняемой во время удаления) вы также можете настроить SQL Server, чтобы не блокировать другие читатели при выполнении удалений в таблице.
Это можно сделать с помощью "изоляции моментальных снимков", которая была введена с SQL Server 2005:
http://msdn.microsoft.com/en-us/library/ms345124%28v=sql.90%29.aspx
Если у вас есть что-либо с каскадными удалениями, убедитесь, что они индексированы.
выделение запроса на удаление и щелчок Display estimated execution plan
покажет предлагаемые индексы , которые в моем случае включали некоторые каскадные удаления.
добавление индексов для тех, кто сделал удаление намного быстрее , но я все равно не буду пытаться удалить все строки сразу.
лучший способ, который я нашел, - это форма asp.net DeleteExpiredSessions . вы делаете READUNCOMMITTED select и помещаете записи во временную таблицу, чем удаляете запись с помощью курсора.
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
попробуйте это,
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