Стоит ли делать свертку базы данных 1С большого объема и как решить проблему битых ссылок?

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

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

В этой статье мы подробно проанализируем, почему стандартная свертка не решает проблемы с быстродействием, рассмотрим альтернативный и более эффективный метод «свертки миграцией», а также разберем детальные алгоритмы работы с неизбежно возникающими битыми ссылками.

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

Рассмотрим ситуацию, с которой сталкиваются многие администраторы: после тестовой свертки базы объемом 450 ГБ ее размер уменьшился до 290 ГБ (сокращение на ~35%), но быстродействие ключевых запросов и отчетов абсолютно не изменилось. Почему так происходит?

1. Особенности работы СУБД с файлами данных. Когда мы удаляем миллионы документов и проводок из базы данных под управлением MS SQL Server или PostgreSQL, СУБД физически не возвращает это место операционной системе. Страницы данных просто помечаются как «свободные» для будущих записей. Физический файл базы данных .mdf (или файлы табличных пространств в Postgres) остается прежнего размера. Чтобы вернуть место операционной системе, необходимо выполнить операцию сжатия (Shrink):

2. Опасность фрагментации индексов. Выполнение операции Shrink на уровне СУБД приводит к катастрофической фрагментации индексов (она может достигать 90–99%). Если сразу после сжатия базы не выполнить полную реиндексацию (перестроение индексов), СУБД начнет работать значительно медленнее, чем до свертки. На чтение фрагментированных данных с диска будет уходить гораздо больше времени.

3. Логарифмическая сложность индексов (B-деревья). Большинство современных СУБД хранят индексы в виде сбалансированных деревьев (B-Tree). Поиск записи в таблице объемом 10 миллионов строк и поиск в таблице объемом 1 миллион строк различается для процессора на доли миллисекунд. Если ваши запросы написаны неоптимально (например, содержат неявные соединения, подзапросы в условиях, виртуальные таблицы без параметров), то сокращение объема данных никак не решит проблему зависаний. Система все равно будет уходить в сканирование всей таблицы (Table Scan) вместо поиска по индексу (Index Seek).

Когда свертка действительно необходима?

Если для ускорения работы свертка бесполезна, то в каких случаях ее действительно имеет смысл проводить? Выделим основные практические сценарии:

  1. Радикальное изменение учетной политики или методологии. Например, организация приняла решение полностью изменить структуру ведения аналитического учета, отказаться от старых неиспользуемых субконто или переделать структуру справочника «Номенклатура» (включить/выключить серии и характеристики). Старые данные не соответствуют новым правилам, и их проще «отрезать».
  2. Требование регуляторов или бизнеса по безопасности. Необходимость скрыть или физически удалить старые коммерческие данные (например, архив за периоды пятилетней давности) при передаче базы третьим лицам или смене собственников компании.
  3. Исправление запущенного учета. Ситуация, когда учет за прошлые годы велся с грубейшими ошибками, исправить которые задним числом невозможно, так как они влияют на текущую себестоимость и расчеты. В этом случае свертка позволяет зафиксировать выверенные остатки вручную и начать учет «с чистого листа».
  4. Ограничения аппаратного обеспечения. Единственный случай, связанный с производительностью — когда размер базы превышает объем оперативной памяти сервера настолько, что СУБД больше не может кэшировать активные индексы в RAM, а дисковая подсистема (особенно если это устаревшие SAS/SATA HDD) не справляется с возросшим потоком случайного чтения/записи.

Альтернативный подход: «Свертка методом миграции»

Классическая свертка (пометка документов на удаление с последующим запуском стандартного удаления помеченных объектов) на базах объемом более 100 ГБ практически невыполнима. Процесс удаления миллионов строк в транзакции может заблокировать работу пользователей на дни, вызвать переполнение логов транзакций (файл .ldf раздуется до терабайтных размеров) и завершиться аварийным сбоем.

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

Шаг 1. Создание документов ввода остатков в рабочей базе. С помощью специализированных обработок (или стандартного помощника свертки) формируем документы ввода остатков на выбранную дату (например, на 01.01.2024). Проверяем корректность сформированных остатков, сверяя оборотно-сальдовую ведомость и отчеты по регистрам накопления до и после проведения документов ввода остатков.

Шаг 2. Подготовка чистой базы-приемника. Создаем абсолютно новую пустую базу данных на сервере СУБД. Накатываем на нее конфигурацию из нашей рабочей базы (файл .cf). Настраиваем параметры учета и константы.

Шаг 3. Перенос нормативно-справочной информации (НСИ). Переносим только те элементы справочников, которые реально используются в учете. Для этого можно использовать правила обмена, написанные в «1С:Конвертация данных», или стандартные механизмы выгрузки/загрузки данных. Это позволяет очистить базу от «мусора» (неактивных контрагентов, дублей номенклатуры, старых договоров).

Шаг 4. Перенос документов ввода остатков. Переносим сформированные на Шаге 1 документы ввода остатков в новую базу. Также переносим документы за текущий период, если учет в рабочей базе продолжался после даты свертки.

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

Как правильно решать проблему «битых ссылок» (<Объект не найден>)

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

Рассмотрим детально, как правильно бороться с этим явлением.

Способ 1. Замена удаляемых ссылок на специальный объект-«заглушку»

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

Проанализируем пример кода, который выполняет поиск ссылок на удаляемый объект и заменяет их на ссылку созданного нами служебного элемента (например, контрагента «Удален при свертке»):


// Пример замены битых ссылок перед удалением
Процедура ЗаменитьСсылкиНаЗаглушку(УдаляемыйОбъектСсылка, ОбъектЗаглушкаСсылка) Экспорт
    
    // Помещаем ссылку в массив для поиска
    МассивСсылок = Новый Массив;
    МассивСсылок.Добавить(УдаляемыйОбъектСсылка);
    
    // Находим все места использования данной ссылки в базе данных
    ТаблицаМестИспользования = НайтиСсылки(МассивСсылок);
    
    Для Каждого СтрокаТаблицы Из ТаблицаМестИспользования Цикл
        
        // Получаем метаданные объекта, где найдена ссылка
        МетаданныеОбъекта = СтрокаТаблицы.Метаданные;
        ДанныеДляИзменения = СтрокаТаблицы.Данные;
        
        // Обрабатываем только ссылочные типы (документы, справочники)
        Если Метаданные.Документы.Содержит(МетаданныеОбъекта) Тогда
            
            ДокОбъект = ДанныеДляИзменения.ПолучитьОбъект();
            Если ДокОбъект <> Неопределено Тогда
                // Отключаем проверки при записи для ускорения процесса
                ДокОбъект.ОбменДанными.Загрузка = Истина;
                
                // Находим реквизит, содержащий битую ссылку, и заменяем его
                ИмяРеквизита = СтрокаТаблицы.Реквизит.Имя;
                ДокОбъект[ИмяРеквизита] = ОбъектЗаглушкаСсылка;
                
                Попытка
                    ДокОбъект.Записать(РежимЗаписиДокумента.Запись);
                Исключение
                    ЗаписьЖурналаРегистрации("Свертка.ОшибкаЗаписи", 
                        УровеньЖурналаРегистрации.Ошибка, , , 
                        "Не удалось записать документ: " + ДокОбъект.Ссылка + " Описание: " + ОписаниеОшибки());
                КонецПопытки;
            КонецЕсли;
            
        КонецЕсли;
    КонецЦикла;
    
КонецПроцедуры

Способ 2. Быстрое прямое удаление документов без контроля ссылочной целостности

Если объемы данных колоссальны, стандартное удаление с контролем ссылок через платформу 1С может длиться неделями. В случаях, когда мы на 100% уверены, что удаляемые документы не связаны с периодом после даты свертки (или мы уже выполнили замену ссылок на заглушки), можно применить метод прямого удаления документов без контроля целостности. Для этого используется свойство ОбменДанными.Загрузка = Истина.

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


// Пример быстрого удаления документов без контроля ссылок
Процедура БыстроеУдалениеДокументовПериода(ИмяДокумента, ДатаНачала, ДатаОкончания) Экспорт
    
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |   Док.Ссылка КАК Ссылка
        |ИЗ
        |   Документ." + ИмяДокумента + " КАК Док
        |ГДЕ
        |   Док.Дата МЕЖДУ &ДатаНачала И &ДатаОкончания";
        
    Запрос.УстановитьПараметр("ДатаНачала", ДатаНачала);
    Запрос.УстановитьПараметр("ДатаОкончания", ДатаОкончания);
    
    РезультатЗапроса = Запрос.Выполнить();
    Если РезультатЗапроса.Пустой() Тогда
        Возврат;
    КонецЕсли;
    
    Выборка = РезультатЗапроса.Выбрать();
    
    // Начинаем транзакцию для ускорения записи и обеспечения надежности
    НачатьТранзакцию();
    
    Попытка
        Пока Выборка.Следующий() Цикл
            
            ДокОбъект = Выборка.Ссылка.ПолучитьОбъект();
            Если ДокОбъект <> Неопределено Тогда
                // Режим загрузки отключает проведение, проверки и подписки на события
                ДокОбъект.ОбменДанными.Загрузка = Истина;
                ДокОбъект.Удалить();
            КонецЕсли;
            
        КонецЦикла;
        
        ЗафиксироватьТранзакцию();
        ПоказатьОповещениеПользователя("Успешно", , "Удаление документов завершено.");
        
    Исключение
        ОтменитьТранзакцию();
        ЗаписьЖурналаРегистрации("Свертка.ОшибкаУдаления", 
            УровеньЖурналаРегистрации.Критическая, , , 
            "Произошла ошибка при массовом удалении: " + ОписаниеОшибки());
        ВызватьИсключение "Процесс удаления прерван из-за ошибки!";
    КонецПопытки;
    
КонецПроцедуры

Способ 3. Очистка независимых регистров сведений

Независимые регистры сведений (такие как «Цены номенклатуры», «Штрихкоды», «История регистрации объектов» и различные логирующие регистры) не связаны с документами жесткой ссылочной целостностью на уровне платформы. При удалении документов записи в этих регистрах остаются мертвым грузом и продолжают раздувать базу данных.

Их очистку необходимо выполнять отдельно. Делается это путем записи пустого набора записей с установленным отбором по периоду или ключевым измерениям:


// Пример очистки независимого регистра сведений за прошлые периоды
Процедура ОчиститьРегистрСведенийЗаПериод(ИмяРегистра, ДатаСреза) Экспорт
    
    НаборЗаписей = РегистрыСведений[ИмяРегистра].СоздатьНаборЗаписей();
    
    // Если регистр периодический, устанавливаем отбор по периоду
    МетаданныеРегистра = Метаданные.РегистрыСведений[ИмяРегистра];
    Если МетаданныеРегистра.ПериодичностьРегистраСведений <> Метаданные.Свойства.ПериодичностьРегистраСведений.Непериодический Тогда
        
        НаборЗаписей.Отбор.Период.Использование = Истина;
        НаборЗаписей.Отбор.Period.ВидСравнения = ВидСравнения.МеньшеИлиРавно;
        НаборЗаписей.Отбор.Период.Значение = ДатаСреза;
        
    КонецЕсли;
    
    // Записываем пустой набор записей, тем самым удаляя старые данные
    НаборЗаписей.ОбменДанными.Загрузка = Истина;
    НаборЗаписей.Записать();
    
КонецПроцедуры

Способ 4. Использование механизма тестирования и исправления (ТИИ)

После проведения всех процедур удаления и очистки обязательно нужно запустить стандартный инструмент платформы — «Тестирование и исправление информационной базы» (ТИИ). Нас интересует раздел настроек проверки ссылочной целостности:

Резюме и пошаговый план действий

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

Если же бизнес-цели проведения свертки обоснованы, рекомендуем придерживаться следующего плана действий:

  1. Всегда работайте на тестовой копии. Никогда не проводите свертку и удаление документов сразу в продуктивной базе данных.
  2. Рассмотрите метод миграции. Перенос остатков и справочников в абсолютно новую базу данных в 90% случаев эффективнее, быстрее и безопаснее, чем попытки очистить старую базу стандартными средствами.
  3. Постепенно удаляйте документы. Если свертка идет в живой базе, удаляйте документы небольшими пакетами (транзакциями) во внерабочее время или в фоновом режиме, чтобы избежать блокировок СУБД и раздувания файлов транзакций.
  4. Контролируйте ссылочную целостность. Предварительно заменяйте удаляемые ссылки на служебные элементы-заглушки, очищайте независимые регистры сведений и проводите финальное тестирование и исправление базы данных.
← На главную