Работа с большими информационными базами (от 5 ГБ и выше) в файловом варианте 1С часто сопряжена с критическим падением производительности при выполнении регламентных операций. Одной из самых трудоемких задач является удаление помеченных объектов. Когда количество таких объектов исчисляется сотнями тысяч (например, после свертки базы или массовой очистки), стандартные функции НайтиПоСсылкам и УдалитьОбъекты могут выполняться сутками, фактически блокируя работу предприятия. Рассмотрим, почему это происходит, и проанализируем альтернативные способы решения проблемы, которые позволяют сократить время обработки в десятки раз.
Разберем внутреннюю логику работы платформенных механизмов. Главная проблема функции НайтиПоСсылкам заключается в том, что она стремится собрать полную таблицу всех упоминаний объекта во всей базе данных. Если на один ваш документ ссылается десять других документов или записей регистров, функция не остановится на первом найденном упоминании, а переберет все таблицы метаданных до конца, чтобы вернуть вам исчерпывающий список.
Проанализируем ситуацию: если нам нужно просто понять, можно ли удалить объект (есть ли на него ссылки или нет), нам достаточно найти хотя бы одну ссылку. Стандартная же функция выполняет избыточную работу. Ситуация усугубляется в файловом варианте базы, где операции ввода-вывода ограничены скоростью дисковой подсистемы и особенностями работы движка .1CD.
Если вы все же решили использовать штатный механизм УдалитьОбъекты с включенным контролем целостности, важно правильно подобрать размер порции данных. Экспериментальным путем выяснено, что передача объектов по одному — самый медленный путь. Рассмотрим оптимальный алгоритм пакетной обработки.
Как показала практика, наиболее эффективным является размер массива в 50–100 элементов. При увеличении этого числа скорость в пересчете на один объект перестает расти, а при уменьшении — резко падает из-за накладных расходов на инициализацию проверки. Также стоит помнить, что для работы этой функции крайне желательно использование монопольного режима.
Посмотрим на пример кода для пакетного удаления:
// Пример пакетного удаления объектов
МассивДляУдаления = НайтиПомеченныеНаУдаление();
ВсегоОбъектов = МассивДляУдаления.Количество();
РазмерПорции = 50;
ТекущийИндекс = 0;
Пока ТекущийИндекс < ВсегоОбъектов Цикл
Порция = Новый Массив;
Для К = 1 По РазмерПорции Цикл
Если ТекущийИндекс >= ВсегоОбъектов Тогда
Прервать;
КонецЕсли;
Порция.Добавить(МассивДляУдаления[ТекущийИндекс]);
ТекущийИндекс = ТекущийИндекс + 1;
КонецДля;
ТаблицаСсылок = Новый ТаблицаЗначений;
УстановитьМонопольныйРежим(Истина);
// Третий параметр Истина включает контроль ссылочной целостности
УдалитьОбъекты(Порция, Истина, ТаблицаСсылок);
УстановитьМонопольныйРежим(Ложь);
// Обработка объектов, которые не удалось удалить из-за наличия ссылок
Если ТаблицаСсылок.Количество() > 0 Тогда
// Логика обработки конфликтов
КонецЕсли;
КонецЦикла;
Самым эффективным решением для удаления огромного количества объектов (например, 200 000 документов) является отказ от универсальных функций в пользу прямого сканирования метаданных с помощью запросов. Основная идея: мы сами обходим только те объекты метаданных, которые теоретически могут содержать ссылки на наш тип объекта, и прерываем поиск сразу после нахождения первой ссылки.
Выясним, как реализовать такой поиск пошагово:
ДокументСсылка.РеализацияТоваровУслуг.Метаданные находим все реквизиты документов, справочников и измерений регистров, тип которых совпадает с целевым или является составным, включающим целевой тип.ВЫБРАТЬ ПЕРВЫЕ 1. Если запрос вернул результат — объект удалять нельзя.Рассмотрим пример функции, реализующей быструю проверку наличия ссылок:
Функция ЕстьХотьОднаСсылка(СсылкаНаОбъект)
ТипСсылки = ТипЗнч(СсылкаНаОбъект);
// Обходим документы для поиска ссылок в реквизитах
Для каждого МетаДокумент Из Метаданные.Документы Цикл
Для каждого Реквизит Из МетаДокумент.Реквизиты Цикл
Если Реквизит.Тип.СодержитТип(ТипСсылки) Тогда
ТекстЗапроса = "ВЫБРАТЬ ПЕРВЫЕ 1 1 ИЗ Документ." + МетаДокумент.Имя + " ГДЕ " + Реквизит.Имя + " = &Ссылка";
Запрос = Новый Запрос(ТекстЗапроса);
Запрос.УстановитьПараметр("Ссылка", СсылкаНаОбъект);
Если Не Запрос.Выполнить().Пустой() Тогда
Возврат Истина; // Ссылка найдена, дальше искать нет смысла
КонецЕсли;
КонецЕсли;
КонецДля;
КонецДля;
// Аналогично проверяем измерения и реквизиты регистров сведений и накопления
// ... (код для регистров строится по тому же принципу)
Возврат Ложь; // Ссылок нигде нет
КонецФункции
Преимущество данного метода: Запрос с конструкцией ВЫБРАТЬ ПЕРВЫЕ 1 в SQL или файловом движке работает мгновенно при наличии индекса. В то время как НайтиПоСсылкам будет сканировать всю таблицу регистра, чтобы посчитать все ссылки, наш запрос остановится на первой же найденной записи.
Помимо изменения алгоритма, проанализируем инфраструктурные моменты, которые могут замедлять удаление в файловой базе:
Индексировать (или Индексировать с доп. упорядочиванием). Поиск по неиндексированным полям в таблицах с миллионами записей — основная причина "зависания" удаления..dt. Это значительно ускоряет работу с файлом БД большого объема.chdbfl.exe и выполните "Тестирование и исправление" (только пункты "Реиндексация" и "Сжатие таблиц"). Это уберет "дыры" в файле базы и ускорит позиционирование курсора БД.Подводя итог, можно сказать: если перед вами стоит задача удалить сотни тысяч объектов, забудьте про стандартную обработку "Удаление помеченных объектов". Проанализируем ваши действия:
Соответствие в оперативной памяти.ВЫБРАТЬ ПЕРВЫЕ 1.УдалитьОбъекты(Массив, Ложь) (без проверки), так как вы уже проверили их самостоятельно. Это исключит повторную проверку платформой.Такой подход позволяет сократить время удаления 200 000 документов с потенциальных 13 дней (как рассчитал автор темы) до вполне приемлемых 5-6 часов даже на старом оборудовании. Главное — помнить о логической целостности и всегда делать резервную копию перед подобными манипуляциями.