При достижении базой данных 1С критических размеров (например, 450 ГБ и более) руководство и технические специалисты часто задумываются о проведении свертки. Ожидания от этой процедуры стандартны: ускорение работы системы, уменьшение времени на создание и развертывание резервных копий, а также экономия дискового пространства на серверах разработки. Однако на практике классическая свертка часто приносит больше проблем, чем пользы.
В этой статье мы подробно проанализируем, почему стандартная свертка не решает проблемы с быстродействием, рассмотрим альтернативный и более эффективный метод «свертки миграцией», а также разберем детальные алгоритмы работы с неизбежно возникающими битыми ссылками.
Рассмотрим ситуацию, с которой сталкиваются многие администраторы: после тестовой свертки базы объемом 450 ГБ ее размер уменьшился до 290 ГБ (сокращение на ~35%), но быстродействие ключевых запросов и отчетов абсолютно не изменилось. Почему так происходит?
1. Особенности работы СУБД с файлами данных. Когда мы удаляем миллионы документов и проводок из базы данных под управлением MS SQL Server или PostgreSQL, СУБД физически не возвращает это место операционной системе. Страницы данных просто помечаются как «свободные» для будущих записей. Физический файл базы данных .mdf (или файлы табличных пространств в Postgres) остается прежнего размера. Чтобы вернуть место операционной системе, необходимо выполнить операцию сжатия (Shrink):
DBCC SHRINKDATABASE или DBCC SHRINKFILE.VACUUM FULL или утилита pg_repack.2. Опасность фрагментации индексов. Выполнение операции Shrink на уровне СУБД приводит к катастрофической фрагментации индексов (она может достигать 90–99%). Если сразу после сжатия базы не выполнить полную реиндексацию (перестроение индексов), СУБД начнет работать значительно медленнее, чем до свертки. На чтение фрагментированных данных с диска будет уходить гораздо больше времени.
3. Логарифмическая сложность индексов (B-деревья). Большинство современных СУБД хранят индексы в виде сбалансированных деревьев (B-Tree). Поиск записи в таблице объемом 10 миллионов строк и поиск в таблице объемом 1 миллион строк различается для процессора на доли миллисекунд. Если ваши запросы написаны неоптимально (например, содержат неявные соединения, подзапросы в условиях, виртуальные таблицы без параметров), то сокращение объема данных никак не решит проблему зависаний. Система все равно будет уходить в сканирование всей таблицы (Table Scan) вместо поиска по индексу (Index Seek).
Если для ускорения работы свертка бесполезна, то в каких случаях ее действительно имеет смысл проводить? Выделим основные практические сценарии:
Классическая свертка (пометка документов на удаление с последующим запуском стандартного удаления помеченных объектов) на базах объемом более 100 ГБ практически невыполнима. Процесс удаления миллионов строк в транзакции может заблокировать работу пользователей на дни, вызвать переполнение логов транзакций (файл .ldf раздуется до терабайтных размеров) и завершиться аварийным сбоем.
Проанализируем более профессиональный подход — свертку миграцией данных в чистую базу. Разберем этот процесс по шагам:
Шаг 1. Создание документов ввода остатков в рабочей базе. С помощью специализированных обработок (или стандартного помощника свертки) формируем документы ввода остатков на выбранную дату (например, на 01.01.2024). Проверяем корректность сформированных остатков, сверяя оборотно-сальдовую ведомость и отчеты по регистрам накопления до и после проведения документов ввода остатков.
Шаг 2. Подготовка чистой базы-приемника. Создаем абсолютно новую пустую базу данных на сервере СУБД. Накатываем на нее конфигурацию из нашей рабочей базы (файл .cf). Настраиваем параметры учета и константы.
Шаг 3. Перенос нормативно-справочной информации (НСИ). Переносим только те элементы справочников, которые реально используются в учете. Для этого можно использовать правила обмена, написанные в «1С:Конвертация данных», или стандартные механизмы выгрузки/загрузки данных. Это позволяет очистить базу от «мусора» (неактивных контрагентов, дублей номенклатуры, старых договоров).
Шаг 4. Перенос документов ввода остатков. Переносим сформированные на Шаге 1 документы ввода остатков в новую базу. Также переносим документы за текущий период, если учет в рабочей базе продолжался после даты свертки.
Этот метод гарантирует получение чистой, недефрагментированной базы данных на уровне СУБД, которая не требует процедур физического сжатия файлов и изначально свободна от накопившихся за годы эксплуатации ошибок и битых ссылок.
Если вы все же пошли по пути классической свертки с удалением документов непосредственно в текущей базе, вы обязательно столкнетесь с проблемой битых ссылок. Они остаются в движениях других документов, реквизитах справочников и регистрах сведений.
Рассмотрим детально, как правильно бороться с этим явлением.
Просто удалить документы и оставить битые ссылки в базе крайне опасно. При попытке проведения связанных документов, формировании отчетов или обмене данными система будет выдавать критические ошибки. Чтобы избежать этого, перед физическим удалением документов необходимо написать обработку, которая найдет все связанные ссылки и заменит их на предопределенный элемент-заглушку.
Проанализируем пример кода, который выполняет поиск ссылок на удаляемый объект и заменяет их на ссылку созданного нами служебного элемента (например, контрагента «Удален при свертке»):
// Пример замены битых ссылок перед удалением
Процедура ЗаменитьСсылкиНаЗаглушку(УдаляемыйОбъектСсылка, ОбъектЗаглушкаСсылка) Экспорт
// Помещаем ссылку в массив для поиска
МассивСсылок = Новый Массив;
МассивСсылок.Добавить(УдаляемыйОбъектСсылка);
// Находим все места использования данной ссылки в базе данных
ТаблицаМестИспользования = НайтиСсылки(МассивСсылок);
Для Каждого СтрокаТаблицы Из ТаблицаМестИспользования Цикл
// Получаем метаданные объекта, где найдена ссылка
МетаданныеОбъекта = СтрокаТаблицы.Метаданные;
ДанныеДляИзменения = СтрокаТаблицы.Данные;
// Обрабатываем только ссылочные типы (документы, справочники)
Если Метаданные.Документы.Содержит(МетаданныеОбъекта) Тогда
ДокОбъект = ДанныеДляИзменения.ПолучитьОбъект();
Если ДокОбъект <> Неопределено Тогда
// Отключаем проверки при записи для ускорения процесса
ДокОбъект.ОбменДанными.Загрузка = Истина;
// Находим реквизит, содержащий битую ссылку, и заменяем его
ИмяРеквизита = СтрокаТаблицы.Реквизит.Имя;
ДокОбъект[ИмяРеквизита] = ОбъектЗаглушкаСсылка;
Попытка
ДокОбъект.Записать(РежимЗаписиДокумента.Запись);
Исключение
ЗаписьЖурналаРегистрации("Свертка.ОшибкаЗаписи",
УровеньЖурналаРегистрации.Ошибка, , ,
"Не удалось записать документ: " + ДокОбъект.Ссылка + " Описание: " + ОписаниеОшибки());
КонецПопытки;
КонецЕсли;
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Если объемы данных колоссальны, стандартное удаление с контролем ссылок через платформу 1С может длиться неделями. В случаях, когда мы на 100% уверены, что удаляемые документы не связаны с периодом после даты свертки (или мы уже выполнили замену ссылок на заглушки), можно применить метод прямого удаления документов без контроля целостности. Для этого используется свойство ОбменДанными.Загрузка = Истина.
Рассмотрим пример программного кода для быстрого удаления документов определенного вида за свернутый период:
// Пример быстрого удаления документов без контроля ссылок
Процедура БыстроеУдалениеДокументовПериода(ИмяДокумента, ДатаНачала, ДатаОкончания) Экспорт
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Док.Ссылка КАК Ссылка
|ИЗ
| Документ." + ИмяДокумента + " КАК Док
|ГДЕ
| Док.Дата МЕЖДУ &ДатаНачала И &ДатаОкончания";
Запрос.УстановитьПараметр("ДатаНачала", ДатаНачала);
Запрос.УстановитьПараметр("ДатаОкончания", ДатаОкончания);
РезультатЗапроса = Запрос.Выполнить();
Если РезультатЗапроса.Пустой() Тогда
Возврат;
КонецЕсли;
Выборка = РезультатЗапроса.Выбрать();
// Начинаем транзакцию для ускорения записи и обеспечения надежности
НачатьТранзакцию();
Попытка
Пока Выборка.Следующий() Цикл
ДокОбъект = Выборка.Ссылка.ПолучитьОбъект();
Если ДокОбъект <> Неопределено Тогда
// Режим загрузки отключает проведение, проверки и подписки на события
ДокОбъект.ОбменДанными.Загрузка = Истина;
ДокОбъект.Удалить();
КонецЕсли;
КонецЦикла;
ЗафиксироватьТранзакцию();
ПоказатьОповещениеПользователя("Успешно", , "Удаление документов завершено.");
Исключение
ОтменитьТранзакцию();
ЗаписьЖурналаРегистрации("Свертка.ОшибкаУдаления",
УровеньЖурналаРегистрации.Критическая, , ,
"Произошла ошибка при массовом удалении: " + ОписаниеОшибки());
ВызватьИсключение "Процесс удаления прерван из-за ошибки!";
КонецПопытки;
КонецПроцедуры
Независимые регистры сведений (такие как «Цены номенклатуры», «Штрихкоды», «История регистрации объектов» и различные логирующие регистры) не связаны с документами жесткой ссылочной целостностью на уровне платформы. При удалении документов записи в этих регистрах остаются мертвым грузом и продолжают раздувать базу данных.
Их очистку необходимо выполнять отдельно. Делается это путем записи пустого набора записей с установленным отбором по периоду или ключевым измерениям:
// Пример очистки независимого регистра сведений за прошлые периоды
Процедура ОчиститьРегистрСведенийЗаПериод(ИмяРегистра, ДатаСреза) Экспорт
НаборЗаписей = РегистрыСведений[ИмяРегистра].СоздатьНаборЗаписей();
// Если регистр периодический, устанавливаем отбор по периоду
МетаданныеРегистра = Метаданные.РегистрыСведений[ИмяРегистра];
Если МетаданныеРегистра.ПериодичностьРегистраСведений <> Метаданные.Свойства.ПериодичностьРегистраСведений.Непериодический Тогда
НаборЗаписей.Отбор.Период.Использование = Истина;
НаборЗаписей.Отбор.Period.ВидСравнения = ВидСравнения.МеньшеИлиРавно;
НаборЗаписей.Отбор.Период.Значение = ДатаСреза;
КонецЕсли;
// Записываем пустой набор записей, тем самым удаляя старые данные
НаборЗаписей.ОбменДанными.Загрузка = Истина;
НаборЗаписей.Записать();
КонецПроцедуры
После проведения всех процедур удаления и очистки обязательно нужно запустить стандартный инструмент платформы — «Тестирование и исправление информационной базы» (ТИИ). Нас интересует раздел настроек проверки ссылочной целостности:
Очищать ссылки (если ссылка находится в реквизите, реквизит примет значение Неопределено или пустую ссылку) либо Создавать объекты (платформа создаст пустые элементы справочников с восстановленным UID, которые затем можно будет заполнить программно).Очищать ссылки для удаления оставшихся «хвостов» на финальном этапе.Подведем итог. Свертка базы данных 1С большого объема — это сложный технический процесс, который не должен проводиться ради мифического «ускорения базы». Для оптимизации быстродействия гораздо эффективнее использовать рефакторинг тяжелых запросов, оптимизацию индексов на уровне СУБД, переход на более производительные SSD-накопители нового поколения и увеличение объема оперативной памяти сервера приложений.
Если же бизнес-цели проведения свертки обоснованы, рекомендуем придерживаться следующего плана действий: