При обслуживании и оптимизации баз данных платформы 1С:Предприятие разработчики и администраторы нередко сталкиваются с парадоксальной ситуацией: после массового удаления устаревших записей из регистра сведений физический объем таблицы в СУБД не уменьшается, а, наоборот, заметно возрастает. Особенно ярко этот эффект проявляется на регистрах версионирования, таких как ВерсииОбъектов, где хранятся большие бинарные данные.
Разберем детально, почему так происходит, какие механизмы СУБД и платформы 1С приводят к разрастанию таблиц и как правильно вернуть освободившееся дисковое пространство.
Рассмотрим типовой сценарий, который часто приводит к неожиданному результату. Программист решает почистить регистр сведений от дубликатов или устаревших версий, после чего пытается перенумеровать оставшиеся записи с помощью менеджера записи:
Процедура УдалитьВерсии(ТаблицаУдаляемыхВерсий)
Для Каждого Стр Из ТаблицаУдаляемыхВерсий Цикл
МенеджерЗаписи = РегистрыСведений.ВерсииОбъектов.СоздатьМенеджерЗаписи();
МенеджерЗаписи.Объект = Стр.Объект;
МенеджерЗаписи.НомерВерсии = Стр.НомерВерсии;
МенеджерЗаписи.Прочитать();
МенеджерЗаписи.Удалить();
КонецЦикла;
КонецПроцедуры
Процедура ПеренумероватьВерсии(МассивОбрабатываемыхОбъектов)
Для Сч = 0 По МассивОбрабатываемыхОбъектов.Количество() - 1 Цикл
ТекущийОбъект = МассивОбрабатываемыхОбъектов[Сч];
Отбор = Новый Структура("Объект", ТекущийОбъект);
ВыборкаЗаписей = РегистрыСведений.ВерсииОбъектов.Выбрать(Отбор);
СчетчикВерсий = 1;
Пока ВыборкаЗаписей.Следующий() Цикл
МенеджерЗаписи = РегистрыСведений.ВерсииОбъектов.СоздатьМенеджерЗаписи();
МенеджерЗаписи.Объект = ВыборкаЗаписей.Объект;
МенеджерЗаписи.НомерВерсии = ВыборкаЗаписей.НомерВерсии;
МенеджерЗаписи.Прочитать();
МенеджерЗаписи.НомерВерсии = СчетчикВерсий;
МенеджерЗаписи.Записать();
СчетчикВерсий = СчетчикВерсий + 1;
КонецЦикла;
КонецЦикла;
КонецПроцедуры
После выполнения подобного алгоритма количество строк в таблице уменьшается, однако при проверке размера таблицы в обозревателе СУБД (например, в свойствах таблицы _InfoRgXXXX в среде MS SQL Server) объем занятого дискового пространства увеличивается на сотни мегабайт или даже гигабайты.
Выясним физические причины такого поведения СУБД:
ВерсииОбъектов реквизит с содержимым версии имеет тип ХранилищеЗначения. На уровне СУБД он хранится как varbinary(max). Такие данные физически размещаются отдельно от обычных строк — на специальных страницах LOB (Out-of-Row). При удалении или перезаписи строк SQL Server не освобождает эти страницы автоматически для операционной системы. Освобожденные блоки остаются закрепленными за таблицей, а новые версии и измененные записи записываются в новые блоки.Объект и НомерВерсии). Когда скрипт перезаписывает запись с новым номером версии, на уровне СУБД происходит удаление старого индексного ключа и вставка нового в середину уже заполненных страниц кластерного индекса. Это вызывает массовые расщепления страниц данных (Page Splits). Плотность заполнения страниц падает почти до 50%, из-за чего для хранения меньшего количества строк требуется значительно больше страниц данных.DELETE строки не удаляются физически в ту же секунду — они помечаются как фантомные. До тех пор, пока внутренние фоновые процессы СУБД не очистят их и не объединят страницы, место считается занятым таблицей.Проанализируем, как решить проблему стандартными инструментами 1С:Предприятия без прямого вмешательства в структуру СУБД.
Обычное сжатие базы через СУБД (Shrink) не дает результата, так как пустое место заблокировано фрагментированными страницами внутри самой таблицы. Чтобы перестроить физические таблицы средствами 1С, выполним следующие шаги:
После реструктуризации объем таблицы в СУБД уменьшится до минимального фактического размера данных.
Если информационная база имеет большой объем и запуск полного тестирования и исправления требует слишком много времени технологического окна, выполним обслуживание конкретной проблемной таблицы на уровне СУБД.
Простая дефрагментация командой ALTER INDEX ... REORGANIZE не решает проблему освобождения экстентов LOB-данных. Для полного сжатия и очистки таблицы выполним операцию полного перестроения (Rebuild):
-- Перестроение всех индексов конкретной таблицы регистра сведений
ALTER INDEX ALL ON [_InfoRgXXXX] REBUILD;
-- Либо полное перестроение самой таблицы
ALTER TABLE [_InfoRgXXXX] REBUILD;
Здесь _InfoRgXXXX — имя физической таблицы регистра сведений в базе данных. Выяснить точное имя таблицы можно с помощью метода платформы 1С ПолучитьСтруктуруХраненияБазыДанных().
После выполнения команды REBUILD структура таблицы будет пересоздана, фрагментация сброшена до нуля, а неиспользуемые страницы перейдут в пул свободного места файла базы данных. Если после этого требуется уменьшить физический размер файла .mdf на жестком диске, выполним сжатие файла данных:
DBCC SHRINKFILE (ИмяФайлаДанных, ЖелаемыйРазмерМБ);
Чтобы избежать подобных проблем в будущем, разберем грубые ошибки исходного кода и правила эффективной работы с регистром ВерсииОбъектов:
МенеджерЗаписи.Прочитать() полностью считывает реквизит ХранилищеЗначения в память сервера 1С. Для миллионов записей это приводит к катастрофическому расходу оперативной памяти и трафика СУБД. Удаление необходимо выполнять через пустой РегистрСведенийНаборЗаписей с установленным отбором по объекту и номеру версии без чтения самих данных.