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

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

Многие технические специалисты 1С сталкиваются с парадоксальной ситуацией: при попытке очистить базу данных от старых записей в регистрах накопления объем файлов на диске не уменьшается, а, наоборот, начинает стремительно расти. Ситуация усугубляется, когда штатное «Тестирование и исправление» (ТиИ) приводит к удвоению размера базы, вызывая нехватку дискового пространства и аварийную остановку систем. В рамках этой статьи мы подробно разберем внутренние механизмы работы 1С и SQL Server, выясним причины такого поведения и составим алгоритм правильного удаления данных.

Разберем механизм удаления данных на уровне СУБД

Для начала проанализируем, что происходит в SQL Server, когда 1С выполняет запись пустого набора записей по регистратору. На уровне системы управления базами данных инициируется команда DELETE. Однако физического удаления байтов с диска в этот момент не происходит. Рассмотрим ключевые причины «раздувания» файлов:

  1. Записи-призраки (Ghost Records). Когда строка удаляется, SQL Server лишь помечает её как «удаленную» (ghosted). Это необходимо для обеспечения версионности и корректной работы уровней изоляции транзакций. Специальный фоновый процесс Ghost Cleanup должен очистить эти записи позже. Если мы удаляем данные массово и в цикле, этот процесс просто не успевает за нашими действиями, и база продолжает хранить фактически «мертвые» данные вместе с новыми служебными структурами.
  2. Журнал транзакций (LDF). Каждое удаление записи — это транзакция, которая должна быть зафиксирована в журнале. При массовой очистке регистров объем журнала транзакций может превысить размер самих данных в несколько раз, особенно если используется модель восстановления Full (Полная) и бэкапы лога выполняются редко.
  3. Фрагментация индексов и Page Splits. Удаление данных из середины таблиц приводит к образованию пустых «дыр» в страницах данных. При пересчете итогов или записи новых данных SQL Server может выполнять операцию Page Split (расщепление страницы), создавая новые страницы там, где старые еще не были полностью освобождены. В результате индекс может стать значительно объемнее, чем был до начала очистки.

Влияние таблиц итогов регистров накопления

Проанализируем структуру регистра накопления. Он состоит не только из таблицы движений (_AccumRg), но и из таблиц итогов (_AccumRgT). Когда мы записываем пустой набор записей, используя следующий код:


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

Система 1С вынуждена скорректировать итоги. Если удаляются старые периоды, в таблицах итогов происходит множество операций UPDATE или INSERT для корректировки остатков и оборотов. Это порождает колоссальную нагрузку на дисковую подсистему и взрывной рост логов. При использовании флага ОбменДанными.Загрузка = Истина отключаются прикладные проверки, но физические операции с таблицами итогов на уровне SQL никуда не исчезают.

Почему «Тестирование и исправление» удваивает размер базы?

Выясним причину, по которой штатные средства 1С требуют так много места. В процессе ТиИ при выборе пункта «Реиндексация таблиц» или «Пересчет итогов», SQL Server часто пересоздает индексы целиком. Для выполнения операции ALTER INDEX REBUILD системе требуется временно создать новую структуру индекса рядом со старой. Если ваша таблица занимает 100 ГБ, то в моменте выполнения операции ей потребуется еще около 100 ГБ свободного места в файле данных. Именно поэтому на дисках с малым запасом места ТиИ завершается ошибкой.

Пошаговая инструкция по правильному удалению данных

Чтобы процесс очистки прошел эффективно и не привел к остановке системы, рекомендуем придерживаться следующего алгоритма:

  1. Порционное удаление. Никогда не пытайтесь удалить миллионы записей в одной транзакции или в одном бесконечном цикле без промежуточных фиксаций. Разделите процесс на части (например, по 1000 регистраторов) и делайте паузы для работы Ghost Cleanup.
  2. Управление моделью восстановления. Перед началом массовой очистки, если это допустимо регламентом безопасности, переведите базу данных в модель восстановления Simple (Простая). Это предотвратит бесконтрольный рост файла .LDF. После завершения работ верните модель Full и сделайте полный бэкап.
  3. Очистка итогов. После удаления данных из регистра накопления в таблицах итогов остается много записей с нулевыми значениями. Воспользуйтесь стандартной обработкой «Управление итогами» в 1С или выполните очистку итогов через ТиИ. Это физически удалит «нулевые» строки из таблицы _AccumRgT.
  4. Обслуживание индексов средствами SQL. Вместо использования Shrink (сжатия), которое крайне негативно влияет на производительность из-за дикой фрагментации, выполните операции REORGANIZE или REBUILD для индексов наиболее крупных таблиц. Это переупакует данные и позволит SQL Server использовать освободившееся внутри файла место для новых записей без расширения самого файла .MDF.

Рассмотрим пример оптимизированного кода для удаления

Посмотрим на пример кода, который выполняет удаление порциями с фиксацией транзакций, что позволяет системе «дышать» в процессе работы:


Выборка = Документы.ЗаказПокупателя.Выбрать(ДатаНачала, ДатаОкончания);
Счетчик = 0;

Пока Выборка.Следующий() Цикл
    НаборЗаписей = РегистрыНакопления.ЗаказыПокупателей.СоздатьНаборЗаписей();
    НаборЗаписей.Отбор.Регистратор.Установить(Выборка.Ссылка);
    
    // Использование Загрузка = Истина ускоряет процесс, отключая подписки
    НаборЗаписей.ОбменДанными.Загрузка = Истина;
    
    Попытка
        НаборЗаписей.Записать(Истина);
        Счетчик = Счетчик + 1;
    Исключение
        ЗаписьЖурналаРегистрации("Ошибка удаления", УровеньЖурналаРегистрации.Ошибка,,, ОписаниеОшибки());
    КонецПопытки;
    
    // Каждые 500 записей даем СУБД обработать транзакции
    Если Счетчик % 500 = 0 Тогда
        // Здесь можно вставить небольшую паузу или просто зафиксировать прогресс
        ОбработкаПрерыванияПользователя();
    КонецЕсли;
КонецЦикла;

Выводы и рекомендации

Проанализировав ситуацию, можно сделать вывод, что рост базы при удалении — это естественный процесс работы страниц данных и индексов SQL Server. Важно помнить: размер файла базы данных — это не только объем самих данных, но и зарезервированное место, которое СУБД будет использовать в будущем. Если вы планируете продолжать работу в этой базе, не стремитесь во что бы то ни стало «сжать» файл до минимальных размеров. Вместо этого сфокусируйтесь на регламентном обслуживании индексов и контроле за ростом таблиц итогов.

Если же ваша цель — физически уменьшить размер базы (например, для передачи копии), то последовательность должна быть такой: Удаление данных -> Очистка итогов -> Rebuild индексов -> Shrink (только в этом случае!) -> Full Backup.

← На главную