Подтвердить что ты не робот

Как ускорить удаление из большой таблицы базы данных?

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

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

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

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

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

Любые идеи? Я уже прочитал более старые связанные сообщения здесь и не нашел ничего, что могло бы помочь.

4b9b3361

Ответ 1

Смотрите: Оптимизация удаления на 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

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

Ответ 2

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

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

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

Ответ 3

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

Ответ 4

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

Ответ 5

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

Здесь быстрый праймер блокировки моментальных снимков:

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

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

Ответ 6

Вы можете удалить небольшие партии с помощью цикла 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

Ответ 7

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

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