Как ускорить удаление из большой таблицы базы данных? - PullRequest
21 голосов
/ 21 июля 2009

Вот проблема, которую я пытаюсь решить: я недавно выполнил редизайн слоя данных, который позволяет мне распределять нагрузку на мою базу данных по нескольким сегментам. Чтобы сбалансировать сегменты, мне нужно иметь возможность переносить данные из одного сегмента в другой, что включает копирование из сегмента A в сегмент B, а затем удаление записей из сегмента A. Но у меня есть несколько очень больших таблиц, и на них указано много внешних ключей, поэтому удаление одной записи из таблицы может занять более одной секунды.

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

Отключение внешних ключей не вариант. Удаление больших пакетов строк также невозможно, поскольку это производственное приложение, а большие удаляют блокировку слишком большого количества ресурсов, что приводит к сбоям. Я использую Sql Server и знаю о секционированных таблицах, но ограничения на секционирование (и лицензионные сборы для корпоративной версии) настолько нереальны, что они невозможны.

Когда я начал работать над этой проблемой, я подумал, что трудной частью будет написание алгоритма, который выясняет, как удалять строки с уровня листа до вершины модели данных, чтобы не нарушать никаких ограничений внешнего ключа вдоль путь. Но решение этой проблемы не принесло мне пользы, поскольку на удаление записей, которые должны исчезнуть в одночасье, уходят недели.

Я уже создал способ пометить данные как фактически удаленные, так что в случае приложения данные исчезли, но я все еще имею дело с большими файлами данных, большими резервными копиями и более медленными запросами из-за абсолютный размер столов.

Есть идеи? Я уже прочитал старые посты и не нашел ничего, что могло бы помочь.

Ответы [ 7 ]

27 голосов
/ 21 июля 2009

Пожалуйста, смотрите: Оптимизация удаления на SQL Server

Эта статья по поддержке MS может представлять интерес: Как решить проблемы блокировки, вызванные эскалацией блокировки в SQL Server :

Разбейте большие пакетные операции на несколько меньших операций . За Например, предположим, что вы запустили следующее запрос на удаление нескольких сотен тысячи старых записей из аудита стол, а потом вы обнаружили, что это вызвало эскалацию блокировки, которая заблокировала другие пользователи:

DELETE FROM LogMessages WHERE LogDate < '2/1/2002'    

Удалив эти записи несколько сто за один раз, вы можете резко сократить количество блокировки, которые накапливаются за транзакцию и предотвратить эскалацию блокировки. За Пример:

SET ROWCOUNT 500
delete_more:
     DELETE FROM LogMessages WHERE LogDate < '2/1/2002'
IF @@ROWCOUNT > 0 GOTO delete_more
SET ROWCOUNT 0

Уменьшите объем блокировки запроса, сделав запрос таким же эффективным, как и возможно. Большие сканы или большие количество просмотров Bookmark может увеличить шанс блокировки эскалация; кроме того, это увеличивает вероятность тупиков, и вообще отрицательно влияет на параллелизм и производительность.

16 голосов
/ 31 июля 2013
delete_more:
     DELETE TOP(500) FROM LogMessages WHERE LogDate < '2/1/2002'
IF @@ROWCOUNT > 0 GOTO delete_more

Вы можете достичь того же результата, используя SET ROWCOUNT, как предложено Mitch, но в соответствии с MSDN , он не будет поддерживаться для DELETE и некоторых других операций в будущих версиях SQL Server:

Использование SET ROWCOUNT не повлияет на DELETE, INSERT и UPDATE. заявления в будущем выпуске SQL Server. Избегайте использования SET ROWCOUNT с инструкциями DELETE, INSERT и UPDATE в новых разработках, и планируют модифицировать приложения, которые в настоящее время используют его. Для аналогичного поведение, используйте синтаксис TOP. Для получения дополнительной информации см. ТОП (Transact-SQL).

1 голос
/ 31 июля 2013

Еще одно предложение - переименовать таблицу и добавить столбец состояния. Когда статус = 1 (удален), вы не захотите, чтобы он отображался. Затем вы создаете представление с тем же именем, что и у исходной таблицы, которое выбирается из таблицы, когда статус равен нулю или = 0 (в зависимости от того, как вы его реализуете). Удаление появляется немедленно для пользователя, и фоновое задание может запускаться каждые пятнадцать минут, удаляя записи, которые запускаются, и никто, кроме dbas, не подозревает об этом.

1 голос
/ 21 июля 2009

Вы можете создать новые файлы, скопировать все, кроме «удаленных» строк, а затем поменять местами имена в таблицах. Наконец, отбросьте старые таблицы. Если вы удаляете большой процент записей, это может быть быстрее.

0 голосов
/ 27 декабря 2016

Вы можете удалять небольшие партии, используя цикл while, примерно так:

DELETE TOP (10000) FROM LogMessages WHERE LogDate < '2/1/2002'
WHILE @@ROWCOUNT > 0
BEGIN
    DELETE TOP (10000) FROM LogMessages WHERE LogDate < '2/1/2002'
END
0 голосов
/ 08 декабря 2016

вот решение вашей проблемы.

DECLARE @RC AS INT
SET @RC = -1

WHILE @RC <> 0
BEGIN
    DELETE TOP(1000000) FROM [Archive_CBO_ODS].[CBO].[AckItem] WHERE [AckItemId] >= 300
    SET @RC = @@ROWCOUNT
    --SET @RC = 0
END
0 голосов
/ 24 июля 2009

Если вы используете SQL 2005 или 2008, возможно, вам поможет «изоляция моментальных снимков». Это позволяет данным оставаться видимыми для пользователей во время обработки основной операции обновления данных, а затем выявляет данные, как только они фиксируются. Даже если удаление займет 30 минут, ваши приложения будут оставаться в сети в течение этого времени.

Вот краткий пример блокировки снимков:

http://www.mssqltips.com/tip.asp?tip=1081

Хотя вам все равно следует попытаться ускорить удаление, чтобы оно было как можно быстрее, это может облегчить некоторые проблемы.

...