Оптимизация удаления на SQL Server - PullRequest
38 голосов
/ 05 июня 2009

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

до сих пор:

  • убедитесь, что внешние ключи имеют индексы

  • убедитесь, что индексируются условия

  • использование WITH ROWLOCK

  • уничтожить неиспользуемые индексы, удалить, перестроить индексы

теперь ваша очередь.

Ответы [ 15 ]

22 голосов
/ 05 июня 2009

Следующая статья, Операции быстрого упорядоченного удаления может представлять интерес для вас.

Выполнение быстрых операций удаления SQL Server

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

13 голосов
/ 05 июня 2009

У меня гораздо больше опыта работы с Oracle, но, скорее всего, то же самое относится и к SQL Server:

  • при удалении большого количества строк используйте блокировку таблицы, чтобы базе данных не приходилось выполнять много блокировок строк
  • если на таблицу, из которой вы удаляете ссылку, ссылаются другие таблицы, убедитесь, что эти другие таблицы имеют индексы для столбцов внешнего ключа (в противном случае база данных будет выполнять полное сканирование таблицы для каждой удаленной строки в другой таблице, чтобы убедиться, что удаление строки не нарушает ограничение внешнего ключа)
9 голосов
/ 05 июня 2009

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

7 голосов
/ 11 декабря 2014

Сводка ответов за 2014-11-05

Этот ответ помечен как вики сообщества, так как это постоянно развивающаяся тема с множеством нюансов, но в целом очень мало возможных ответов.

Первая проблема: вы должны спросить себя, для какого сценария вы оптимизируете? Обычно это либо производительность с одним пользователем на БД, либо масштабирование с большим количеством пользователей на БД. Иногда ответы прямо противоположны.

Для однопользовательской оптимизации

  • Подсказка TABLELOCK
  • Удалить индексы, не использованные при удалении, а затем перестроить их
  • Пакетное использование что-то вроде SET ROWCOUNT 20000 (или что-то еще, в зависимости от пространства журнала) и цикл (возможно, с WAITFOR DELAY), пока вы не избавитесь от всего этого (@@ROWCOUNT = 0)
  • Если удалить большой% таблицы, просто создайте новую и удалите старую таблицу
  • Разделите строки для удаления, затем отбросьте разделение. [Подробнее ...]

Для многопользовательской оптимизации

  • Подсказка рядных замков
  • Использовать кластерный индекс
  • Разработка кластерного индекса для минимизации реорганизации страницы при удалении больших блоков
  • Обновите столбец "is_deleted", затем выполните фактическое удаление позже в течение периода обслуживания

Для общей оптимизации

  • Убедитесь, что у FK есть индексы в исходных таблицах
  • Убедитесь, что в предложении WHERE есть индексы
  • Определите строки для удаления в предложении WHERE с помощью представления или производной таблицы вместо прямой ссылки на таблицу. [Подробнее ...]
5 голосов
/ 05 июня 2009

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

Мои предложения:

  • Убедитесь, что таблица имеет первичный ключ и кластерный индекс (это жизненно важно для всех операций).
  • Убедитесь, что кластеризованный индекс таков, чтобы при удалении большого блока строк происходила минимальная реорганизация страницы.
  • Убедитесь, что ваши критерии выбора SARGable.
  • Убедитесь, что все ограничения внешнего ключа в настоящее время являются доверенными.
4 голосов
/ 05 июня 2009

Проблема в том, что вы недостаточно определили свои условия. То есть что именно вы оптимизируете?

Например, не работает ли система из-за ночного техобслуживания и нет ли пользователей в системе? И вы удаляете большой% базы данных?

Если в автономном режиме и удаление большого%, возможно, имеет смысл просто создать новую таблицу с данными для хранения, удалить старую таблицу и переименовать. При удалении небольшого% вы, вероятно, захотите группировать вещи настолько большими партиями, насколько позволяет пространство журнала. Это полностью зависит от вашей базы данных, но удаление индексов на время перестройки может повредить или помочь - если вообще возможно из-за того, что он находится в автономном режиме.

Если вы в сети, какова вероятность того, что ваши удаления конфликтуют с пользовательской активностью (и является ли пользовательская активность преимущественно чтением, обновлением или чем-то еще)? Или вы пытаетесь оптимизировать работу с пользователем или скорость выполнения запроса? Если вы удаляете из таблицы, которая часто обновляется другими пользователями, вам нужно выполнить пакет, но с меньшим размером пакета. Даже если вы делаете что-то вроде блокировки таблицы для обеспечения изоляции, это не принесет особой пользы, если ваш оператор удаления занимает час.

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

4 голосов
/ 05 июня 2009

(если индексы "не используются", зачем они вообще там?)

Один из вариантов, который я использовал в прошлом, - это выполнять работу партиями. Грубым способом было бы использовать SET ROWCOUNT 20000 (или что угодно) и цикл (возможно, с WAITFOR DELAY), пока вы не избавитесь от всего этого (@@ ROWCOUNT = 0).

Это может помочь уменьшить воздействие на другие системы.

3 голосов
/ 05 июня 2009

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

Удалить в пакетном режиме.

Если у вас есть таблицы внешних ключей, которые больше не используются (вы будете удивлены, как часто рабочие базы данных заканчиваются старыми таблицами, от которых никто не избавится), избавьтесь от них или, по крайней мере, разорвите соединение FK / PK , Нет смысла проверять таблицу на предмет записей, если она не используется.

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

2 голосов
/ 18 июня 2011

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

2 голосов
/ 05 июня 2009

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

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

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