Начнем с хороших новостей. Какое-то время назад я написал статью Дефрагментация таблиц в высоко нагруженных базах данных (MSSQL). За это время я еще больше отшлифовал скрипт на production, и отдел безопасности фирмы, где я сейчас работаю, разрешил выложить его в open source (репо на github). Приглашаю воспользоваться им и писать мне о багах и пожеланиях.

Ниже я приведу краткий update к статье - кое в чем я теперь с ней не согласен. Кроме того, опишу опыт SHRINK - почему его лучше никогда не делать, почему все таки иногда нужно делать и как его готовить.

Update к статье

Во-первых, я отказался полностью от INDEX REORGANIZE. На реальных объемах дождаться этого процесса нереально. Поэтому только REBUILD.

Во-вторых, я начал перестраивать и COLUMNSTORE таблицы. Прогресс виден и вычисляется по числу перестроенных ROWSTORE. Однако до сих пор я считаю поддержку COLUMNSTORE таблиц экспериментальной

Я также словил несколько инцидентов, когда перестройка индекса так изменяла план выполнения UPDATE и особенно MERGE (а SQL меняет план выполнения чтобы трекать изменения), что время выполнения запроса сильно деградировало

SHRINK - почему это не надо делать

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

Во-первых, объем данных у вас в базе растет естественным образом, поэтому рано или поздно это место будет заполнено.

Во-вторых, пустые страницы не пишутся в FULL BACKUP и не кешируются, так что это дырка от удаленной таблицы ничем вам не мешает.

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

В каких же случаях все таки приходится делать SHRINK?

SHRINK данных - когда это делают

Для данных SHRINK имеет делать смысл, когда необходимо именно уменьшить размер MDF файла. Например, для того, чтобы на тот же диск поместить другую базу, или облегчить работу внешним storage (которые не знают, что внутри файла есть свободное место). Также для SSD наличие свободного места полезно для времени его жизни.

Как это не надо делать?

Сразу забываем о Tasks -> Shrink -> Database и пользуемся только Tasks -> Shrink -> Files, работая с каждым файлом индивидуально. Общая ошибка здесь - увидеть, что в базе 6Tb свободно 50%, и указать в качестве желаемого размера 4Tb, и нажать OK. Работать это будет в одной транзакции, долго, до момента пока не найдет страницу, которую не может переместить и потом эту транзакцию откатит.

SHRINK надо делать мелкими порциями по несколько гигабайт. Рекомендую создать джоб и выполнять в нем примерно следующее (подправьте скрипт по своему вкусу):

declare @fsize bigint
while datepart(hh,getdate())<22 begin -- only during day hous
  SELECT   @fsize = Size/128.0 --AS [TotalSize in MB]
  FROM sys.database_files  (NOLOCK) where (sys.database_files.name='MyDbFile')
  OPTION (RECOMPILE)
  set @fsize=@fsize-1000
  DBCC SHRINKFILE (N'MyDbFile' , @fsize)
  --waitfor delay '00:02:00' -- when commented, shrinks aggresively
  end

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

И естественно, после SHRINK сделайте INDEX REBUILD.

SHRINK лога транзакций

Как правило, размер LDF "high watermark" - верхний уровень прилива, и трогать размер файла не нужно. Исключения - runaway операции и исключительно долгие транзакции.

Если для SIMPLE RECOVERY вам просто стоит пытаться много раз сделать SHRINK (и часто вам может повезти и простого усечения будет достаточно), то для FULL recovery/AlwaysOn многие попытки могут быть безуспешными.

Я рекомендую временно прицепить SHRINK лога в виде скрипта к BACKUP LOG для данной базы - как только BACKUP LOG закончен, то сразу делается попытка уменьшить LDF. Если такой скрипт оставить на день, то он наверняка справится даже с 'неподдающийся' базой.

SHRINK tempdb

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

Интернет рекомендует до SHRINK сделать:

USE [tempdb]
CHECKPOINT
DBCC DROPCLEANBUFFERS WITH NO_INFOMSGS
DBCC FREEPROCCACHE WITH NO_INFOMSGS
DBCC FREESYSTEMCACHE ('ALL') WITH NO_INFOMSGS
DBCC FREESESSIONCACHE WITH NO_INFOMSGS

Но это помогает отнюдь не всегда.

Комментарии (5)


  1. GorchilinD
    13.06.2023 14:53

    Не очень понятны претензии к шринку. На практике место на СХД ограничено, выделяют его неохотно, после отдельных операций в базе разносит некоторые файлы. Я обычно формирую единый скрипт по всем файлам базы, первый заход транкейтонли, дальше просто шринк, во всех случаях ограничение времени. Да и можно вычислить в каком из файлов осталось много свободного места, отсортировать очередность. Запустил в студии- оно и давит. При этом в рамках свободного места стараюсь выполнять лесенкой, в несколько иттераций.
    Ребилд- очень нехорошая штука. Ребилд в стандарт редакции блокирует таблицу, ребилд создает копию таблицы. То есть, для больших таблиц ребилд упорото жрет место. Если таблица занимает треть базы- значит ее объем увеличится тоже на треть, потом это место будет освобождено. При этом база может запросто сожрать все место на диске. Вот тут и пригодится шринк.
    На практике для крупных таблиц лучше использовать только (исключительно) реоганайз. Можно исходить из того, что скорость чтения из базы для не самого могучего сервера на уровне 8-10 Гб в минуту. При перестроении индексов на это идет 1-3 Гб чтения в минуту. Вот из этого и нужно исходить. 800 Гб таблица (резервед, со всеми индексами) реорг часов 5, вполне реалистично.


    1. Tzimie Автор
      13.06.2023 14:53

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


      1. GorchilinD
        13.06.2023 14:53

        У нас есть базы больше десятка Тб. Каждая из таких баз- это сотни файлов, все крупные объекты секционированы. То есть, сделать реорг всем объектам- вполне реалистичный сценарий.
        А вот если в 10 Тб базе два файла, лога и данных, при этом самая крупная таблица с индексами занимает половину- во, там дождаться шансов нет- но такая ситуация свидетельствует о крайне неудачной архитектуре приложения.
        Так-то базы размером до 600-800 Гб вполне комфортно поддаются обслуживанию, даже без секционирования.


      1. fr0z
        13.06.2023 14:53

        у меня норм на многотерабайтных реорг идет, у всех все по разному


    1. Tzimie Автор
      13.06.2023 14:53

      Разумеется о standard речи не идёт