Как исправить ошибку роста индексного файла CDX свыше 2 ГБ при загрузке данных в 1С?

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

При выполнении объемных переносов данных между конфигурациями (например, при миграции из «Альфа-Авто 3» в «Альфа-Авто 5» с помощью специализированных обработок) разработчики часто сталкиваются с аварийной остановкой процесса. В процессе загрузки промежуточная таблица соответствий tempref.dbf и связанный с ней файл индексов tempref.cdx стремительно увеличиваются в объеме. Как только размер индексного файла достигает ровно 2 147 483 647 байт (2 ГБ), платформа прерывает выполнение с системной ошибкой вида Error#:-30 Determining File Length.

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

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

В платформе 1С:Предприятие для работы с файлами формата DBF и CDX используется встроенный объект XBase, базирующийся на 32-битной библиотеке CodeBase. Внутренняя адресация смещений в файле использует знаковое 32-битное целое число (signed 32-bit integer). Максимальное положительное значение для такого типа составляет 2^31 - 1, что в точности соответствует 2 097 152 КБ (2 ГБ).

Снять данное ограничение никакой системной настройкой, конфигурационным флагом или директивой невозможно: это фундаментальный предел используемого формата файлов. Когда размер файла пытается превысить эту отметку, файловый менеджер библиотеки не может вычислить корректную длину файла и генерирует сбой Error#:-30.

Факторы взрывного роста файла CDX

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

  1. Динамическая фрагментация B-дерева при непрерывной построчной вставке. Если таблица заполняется записями в цикле, а индексный файл уже подключен и активен, движок перестраивает B-дерево индекса при каждой операции Записать(). При неупорядоченном поступлении ключей страницы индекса постоянно расщепляются, освободившиеся блоки не успевают переиспользоваться, и индекс накапливает избыточный служебный «мусор».
  2. Индексация длинных строковых полей. Построение ключей по строкам большой длины приводит к быстрому расходу емкости индексных страниц. Кроме того, в ряде спецификаций DBF существует жесткое ограничение на длину ключа индекса (около 117–240 байт), превышение которого вызывает неэффективную работу алгоритмов балансировки.
  3. Несколько индексных тегов в одном файле. Файл CDX является составным (Compound Index). Если обработка создает в одном файле сразу несколько тегов по разным полям (например, по коду 7.7, типу объекта и GUID 8.x), размер файла кратно умножается на число этих тегов.

Решение 1. Отложенное создание индекса («холодная» индексация)

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

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


// Создаем структуру DBF-файла без немедленного подключения CDX
ФайлDBF = Новый XBase;
ФайлDBF.Поля.Добавить("TYPE", "S", 50);
ФайлDBF.Поля.Добавить("ID77", "S", 50);
ФайлDBF.Поля.Добавить("UID8", "S", 36);
ФайлDBF.СоздатьФайл(ПутьКФайлуDBF);

// Заполняем таблицу записями БЕЗ открытых индексов
Для Каждого СтрДанных Из МассивОбъектов Цикл
    ФайлDBF.Добавить();
    ФайлDBF.TYPE = СтрДанных.Тип;
    ФайлDBF.ID77 = СтрДанных.Идентификатор77;
    ФайлDBF.UID8 = СтрДанных.Ссылка8Гуид;
    ФайлDBF.Записать();
КонецЦикла;

ФайлDBF.ЗакрытьФайл();

// Создаем индекс по уже готовому монолитному файлу
ФайлDBF.ОткрытьФайл(ПутьКФайлуDBF);
ФайлDBF.Индексы.Добавить("MAINKEY", "TYPE + ID77", Ложь, Ложь);
ФайлDBF.СоздатьИндексныйФайл(ПутьКФайлуCDX);
ФайлDBF.ЗакрытьФайл();

При «холодном» построении индекса формируется идеально сбалансированное плотное дерево без пустых узлов и фрагментации. Это сокращает конечный размер файла tempref.cdx в 2–4 раза, что часто позволяет уложиться в 500–800 МБ вместо критических 2 ГБ.

Решение 2. Периодическое сжатие и реиндексация пачками

Если алгоритм переноса устроен так, что доступ к уже добавленным записям через поиск по индексу требуется непосредственно в процессе работы, применим порционную обработку. Через каждые 50 000 – 100 000 добавленных строк выполним программную очистку индексного пространства:


Если СчетчикЗаписей % 50000 = 0 Тогда
    // Сбрасываем изменения, перестраиваем индекс начисто
    ФайлDBF.Сжать();
    ФайлDBF.Переиндексировать();
КонецЕсли;

Вызов метода Переиндексировать() пересобирает дерево заново, полностью удаляя фрагментированные страницы и сбрасывая неиспользуемые байты.

Решение 3. Шардирование таблицы соответствий по видам метаданных

В типовых правилах конвертации файл tempref.dbf обычно выступает глобальной свалкой соответствий: туда пишутся одновременно номенклатура, контрагенты, документы, партии и регистры. Разделим один гигантский файл на несколько независимых таблиц:

  1. Отдельный файл для справочников высокой плотности (например, tempref_nomen.dbf).
  2. Отдельный файл для контрагентов и договоров.
  3. Отдельный файл для документов и их табличных частей.

Разделение на изолированные хранилища гарантирует, что ни один из подчинённых файлов индексов физически не приблизится к лимиту в 2 ГБ.

Решение 4. Отказ от XBase в пользу структур оперативной памяти 1С

Использование DBF в современных конфигурациях является устаревшим техническим решением. Если база-приемник работает на современной 64-битной платформе 1С:Предприятие 8.3, временные соответствия гораздо быстрее и безопаснее хранить напрямую в оперативной памяти с помощью объектов Соответствие или индексированной ТаблицыЗначений.

Рассмотрим, как заменить дисковый поиск по CDX на встроенное индексирование в оперативной памяти:


// Инициализация таблицы соответствий в памяти
ТаблицаСоответствий = Новый ТаблицаЗначений;
ТаблицаСоответствий.Колонки.Добавить("ТипОбъекта", Новый ОписаниеТипов("Строка", Новый КвалификаторыСтроки(50)));
ТаблицаСоответствий.Колонки.Добавить("ИД77", Новый ОписаниеТипов("Строка", Новый КвалификаторыСтроки(50)));
ТаблицаСоответствий.Колонки.Добавить("Ссылка8", Новый ОписаниеТипов("ЛюбаяСсылка"));

// Строим системный индекс платформы по ключевым колонкам
ТаблицаСоответствий.Индексы.Добавить("ТипОбъекта, ИД77");

// Быстрый поиск в цикле загрузки без ограничений по размеру
ПараметрыОтбора = Новый Структура;
ПараметрыОтбора.Вставить("ТипОбъекта", "Номенклатура");
ПараметрыОтбора.Вставить("ИД77", ТекИдентификатор77);

НайденныеСтроки = ТаблицаСоответствий.НайтиСтроки(ПараметрыОтбора);
Если НайденныеСтроки.Количество() > 0 Тогда
    НайденнаяСсылка = НайденныеСтроки[0].Ссылка8;
Иначе
    // Логика создания и добавления нового сопоставления
    НоваяСтрока = ТаблицаСоответствий.Добавить();
    НоваяСтрока.ТипОбъекта = "Номенклатура";
    НоваяСтрока.ИД77 = ТекИдентификатор77;
    НоваяСтрока.Ссылка8 = СозданнаяСсылка;
КонецЕсли;

Такой подход не только полностью устраняет ошибку Error#:-30 Determining File Length, но и ускоряет поиск связей в десятки раз за счет исключения дисковых операций ввода-вывода.

← На главную