При загрузке больших массивов данных в 1С 7.7 (например, из файлов Excel) программисты часто сталкиваются с эффектом «прогрессирующего торможения». Если первые 1000 элементов создаются мгновенно, то после 5000 или 10 000 скорость падает в десятки раз, а время записи начинает расти по экспоненте. В этой статье мы подробно разберем, почему штатный метод Записать() начинает давать сбои, и какие технические приемы помогут вернуть былую скорость загрузки.
Рассмотрим типовую ситуацию: выполняется загрузка справочника Номенклатура с подчиненными справочниками Цены и ЕдиницыИзмерения. В коде используются транзакции по 100–1000 элементов. Однако замер производительности показывает, что львиную долю времени (до 99%) занимает именно строка Спр.Записать() при создании нового элемента. Основная причина кроется не в самом программном коде, а в особенностях работы движка DBF (CodeBase) и разрастании индексных файлов (.CDX).
Когда мы добавляем новый элемент, система должна обновить все индексы, связанные с этим справочником. Если индексный файл становится слишком тяжелым или фрагментированным, каждая новая запись требует перестройки дерева индексов, что и вызывает лавинообразное замедление.
Одной из главных причин деградации производительности в 1С 7.7 является длина строковых реквизитов, по которым установлены флаги «Сортировка» или «Отбор». Разберем механизм: для каждого такого реквизита создается индексное выражение. В 1С 7.7 существует негласный лимит на длину ключа индекса (около 120 байт). Если длина Наименования установлена в 100 символов, то индекс по нему становится крайне неэффективным при больших объемах данных.
Проанализируем результаты эксперимента: при уменьшении длины наименования со 100 до 50–80 символов скорость загрузки 45 000 позиций сокращается до нескольких минут. Важный вывод: старайтесь не использовать слишком длинные индексируемые поля. Если вам необходимо хранить длинное наименование (например, для печати), создайте два реквизита: Наименование (короткое, индексируемое, 50-60 символов) и ПолноеНаименование (длинное, неиндексируемое).
Каждый включенный флаг «Сортировка» или «Отбор по реквизиту» в Конфигураторе добавляет новый индексный тег в файл .CDX. Рассмотрим ситуацию, когда у справочника включено 5–10 таких флагов. При вызове метода Записать() система вынуждена обновлять 5–10 веток индекса. Ситуация усугубляется, если реквизит имеет тип «Число» с флагом отбора (например, флаг НеВключатьВПрайс).
Для ускорения разовых массовых загрузок рекомендуется:
Это значительно быстрее, чем заставлять систему пересчитывать индексы при каждой записи каждого элемента.
Выясним причину, почему индексы в DBF-версии растут до гигантских размеров (в некоторых случаях файл индекса может вырасти с 20 МБ до 2 ГБ за одну загрузку). Это происходит из-за того, что данные записываются в «хаотичном» порядке относительно алфавита. Дерево индекса постоянно «расщепляется», создавая пустые страницы.
Практический совет: перед началом загрузки отсортируйте ваши данные в исходном файле (Excel или ТЗ) по Наименованию (или по тому полю, которое является основным индексом). Когда данные поступают в систему в алфавитном порядке, новые записи добавляются в «хвост» индексных страниц, что минимизирует фрагментацию файла .CDX и предотвращает его взрывной рост.
Использование транзакций — обязательное условие для быстрой записи. Рассмотрим оптимальный алгоритм работы с транзакциями:
// Пример правильной организации транзакции
Счетчик = 0;
НачатьТранзакцию();
Пока НоменклатураИзФайла.ПолучитьСтроку() = 1 Цикл
Спр.Новый();
Спр.Наименование = НоменклатураИзФайла.Наименование;
// ... заполнение других реквизитов
Спр.Записать();
Счетчик = Счетчик + 1;
Если Счетчик % 200 = 0 Тогда // Оптимальный размер — 100-500 элементов
ЗафиксироватьТранзакцию();
НачатьТранзакцию();
КонецЕсли;
КонецЦикла;
ЗафиксироватьТранзакцию();
Если даже с транзакциями скорость падает, применим стратегию «Разрыв и переиндексация». Если вы заметили, что после 10 000 элементов скорость упала, прервите загрузку, выгоните пользователей и выполните штатную переиндексацию (или просто удалите файлы .CDX, чтобы 1С их пересоздала). После этого индексный файл сожмется, и следующие 10 000 элементов запишутся так же быстро, как первые.
Обратите внимание на замеры производительности в начале статьи. Часто программисты совершают лишние действия, вызывая Записать() несколько раз для одного объекта. Рассмотрим стандартную ошибку:
Номенклатура, вызываем Записать() (нужно, чтобы получить ссылку/ID).ЕдиницуИзмерения, устанавливаем владельца, вызываем Записать().Номенклатуру, чтобы обновить реквизит ОсновнаяЕдиница.Каждая такая запись инициирует обновление индексов. Постарайтесь минимизировать количество вызовов Записать(). Например, записывать основную единицу в номенклатуру можно один раз в самом конце, или использовать прямые запросы (через внешние компоненты типа 1C++), если объем данных исчисляется сотнями тысяч строк.
Подведем итог. Чтобы массовая загрузка в 1С 7.7 не «тормозила», необходимо соблюдать три главных правила: минимизировать длину индексируемых строк, сортировать входные данные по алфавиту и не злоупотреблять количеством индексов (отборов и сортировок) в Конфигураторе. Если же база данных находится в сетевом доступе, крайне рекомендуется выполнять такие операции в терминальном режиме на сервере, где база расположена локально, чтобы исключить влияние сетевых задержек и протоколов блокировок файлов (SMB/OpLocks).