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

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При обслуживании и оптимизации баз данных платформы 1С:Предприятие разработчики и администраторы нередко сталкиваются с парадоксальной ситуацией: после массового удаления устаревших записей из регистра сведений физический объем таблицы в СУБД не уменьшается, а, наоборот, заметно возрастает. Особенно ярко этот эффект проявляется на регистрах версионирования, таких как ВерсииОбъектов, где хранятся большие бинарные данные.

Разберем детально, почему так происходит, какие механизмы СУБД и платформы 1С приводят к разрастанию таблиц и как правильно вернуть освободившееся дисковое пространство.

Анализ проблемы: как удаление и перезапись приводят к росту объема

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


Процедура УдалитьВерсии(ТаблицаУдаляемыхВерсий)
    Для Каждого Стр Из ТаблицаУдаляемыхВерсий Цикл        
        МенеджерЗаписи = РегистрыСведений.ВерсииОбъектов.СоздатьМенеджерЗаписи();        
        МенеджерЗаписи.Объект = Стр.Объект;
        МенеджерЗаписи.НомерВерсии = Стр.НомерВерсии;        
        МенеджерЗаписи.Прочитать();        
        МенеджерЗаписи.Удалить();            
    КонецЦикла;    
КонецПроцедуры

Процедура ПеренумероватьВерсии(МассивОбрабатываемыхОбъектов)
    Для Сч = 0 По МассивОбрабатываемыхОбъектов.Количество() - 1 Цикл
        ТекущийОбъект = МассивОбрабатываемыхОбъектов[Сч];
        Отбор = Новый Структура("Объект", ТекущийОбъект);
        ВыборкаЗаписей = РегистрыСведений.ВерсииОбъектов.Выбрать(Отбор);
        СчетчикВерсий = 1;
        Пока ВыборкаЗаписей.Следующий() Цикл
            МенеджерЗаписи = РегистрыСведений.ВерсииОбъектов.СоздатьМенеджерЗаписи();        
            МенеджерЗаписи.Объект = ВыборкаЗаписей.Объект;
            МенеджерЗаписи.НомерВерсии = ВыборкаЗаписей.НомерВерсии;        
            МенеджерЗаписи.Прочитать();        
            МенеджерЗаписи.НомерВерсии = СчетчикВерсий;        
            МенеджерЗаписи.Записать();
            СчетчикВерсий = СчетчикВерсий + 1;
        КонецЦикла;
    КонецЦикла;
КонецПроцедуры

После выполнения подобного алгоритма количество строк в таблице уменьшается, однако при проверке размера таблицы в обозревателе СУБД (например, в свойствах таблицы _InfoRgXXXX в среде MS SQL Server) объем занятого дискового пространства увеличивается на сотни мегабайт или даже гигабайты.

Выясним физические причины такого поведения СУБД:

  1. Особенности хранения LOB-данных (Large Objects): В регистре ВерсииОбъектов реквизит с содержимым версии имеет тип ХранилищеЗначения. На уровне СУБД он хранится как varbinary(max). Такие данные физически размещаются отдельно от обычных строк — на специальных страницах LOB (Out-of-Row). При удалении или перезаписи строк SQL Server не освобождает эти страницы автоматически для операционной системы. Освобожденные блоки остаются закрепленными за таблицей, а новые версии и измененные записи записываются в новые блоки.
  2. Массовое расщепление страниц (Page Splits): Кластерный индекс регистра сведений строится по измерениям (в данном случае Объект и НомерВерсии). Когда скрипт перезаписывает запись с новым номером версии, на уровне СУБД происходит удаление старого индексного ключа и вставка нового в середину уже заполненных страниц кластерного индекса. Это вызывает массовые расщепления страниц данных (Page Splits). Плотность заполнения страниц падает почти до 50%, из-за чего для хранения меньшего количества строк требуется значительно больше страниц данных.
  3. Фантомные записи (Ghost Records): При выполнении операций DELETE строки не удаляются физически в ту же секунду — они помечаются как фантомные. До тех пор, пока внутренние фоновые процессы СУБД не очистят их и не объединят страницы, место считается занятым таблицей.

Способ 1. Устранение проблемы средствами платформы 1С

Проанализируем, как решить проблему стандартными инструментами 1С:Предприятия без прямого вмешательства в структуру СУБД.

Обычное сжатие базы через СУБД (Shrink) не дает результата, так как пустое место заблокировано фрагментированными страницами внутри самой таблицы. Чтобы перестроить физические таблицы средствами 1С, выполним следующие шаги:

  1. Создадим резервную копию информационной базы.
  2. Запустим базу в режиме «Конфигуратор».
  3. В главном меню выберем пункт Администрирование > Тестирование и исправление...
  4. Установим флаг Реструктуризация таблиц информационной базы. Для серверных баз данных именно эта операция пересоздает физические таблицы с нуля, последовательно перенося актуальные данные и полностью очищая фрагментированное пространство и неиспользуемые LOB-страницы.
  5. Нажмем кнопку Выполнить и дождемся окончания процесса.

После реструктуризации объем таблицы в СУБД уменьшится до минимального фактического размера данных.

Способ 2. Восстановление порядка на уровне MS SQL Server

Если информационная база имеет большой объем и запуск полного тестирования и исправления требует слишком много времени технологического окна, выполним обслуживание конкретной проблемной таблицы на уровне СУБД.

Простая дефрагментация командой ALTER INDEX ... REORGANIZE не решает проблему освобождения экстентов LOB-данных. Для полного сжатия и очистки таблицы выполним операцию полного перестроения (Rebuild):


-- Перестроение всех индексов конкретной таблицы регистра сведений
ALTER INDEX ALL ON [_InfoRgXXXX] REBUILD;

-- Либо полное перестроение самой таблицы
ALTER TABLE [_InfoRgXXXX] REBUILD;

Здесь _InfoRgXXXX — имя физической таблицы регистра сведений в базе данных. Выяснить точное имя таблицы можно с помощью метода платформы 1С ПолучитьСтруктуруХраненияБазыДанных().

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

DBCC SHRINKFILE (ИмяФайлаДанных, ЖелаемыйРазмерМБ);

Архитектурные ошибки алгоритма и правильная очистка регистра

Чтобы избежать подобных проблем в будущем, разберем грубые ошибки исходного кода и правила эффективной работы с регистром ВерсииОбъектов:

  1. Отказ от перенумерации: В подсистеме версионирования БСП и типовых конфигураций разрывы в нумерации версий являются абсолютно штатной ситуацией. Номер версии служит уникальным идентификатором слепка объекта во времени, и непрерывность числового ряда не требуется. Попытка принудительно перенумеровать версии создает колоссальную нагрузку, забивает журнал транзакций и порождает сильнейшую фрагментацию.
  2. Использование набора записей вместо менеджера: Вызов МенеджерЗаписи.Прочитать() полностью считывает реквизит ХранилищеЗначения в память сервера 1С. Для миллионов записей это приводит к катастрофическому расходу оперативной памяти и трафика СУБД. Удаление необходимо выполнять через пустой РегистрСведенийНаборЗаписей с установленным отбором по объекту и номеру версии без чтения самих данных.
  3. Штатные регламентные задания: В современных конфигурациях 1С не следует писать произвольные скрипты для удаления версий. Для этого предназначен типовой механизм: настройка правил версионирования (срок хранения или количество версий) и регулярный запуск регламентного задания «Очистка устаревших версий объектов», которое производит удаление порционно и безопасно для производительности СУБД.
← На главную