Как ускорить операцию закрытия месяца и оптимизировать корректировку стоимости в 1С

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

Операция закрытия месяца, а именно процедура корректировки стоимости, часто становится «бутылочным горлышком» в высоконагруженных системах на базе 1С (особенно в конфигурациях 1С:УПП 1.3 и 1С:КА 1.1). Ситуации, когда расчет себестоимости длится более 5 часов, создают серьезные простои для бизнеса. В этой статье мы детально разберем, как проанализировать замеры производительности и какие изменения в программном коде позволяют сократить время выполнения этой операции в 5 раз и более.

Анализ проблемы: почему корректировка стоимости работает медленно

Рассмотрим ситуацию, когда замер производительности указывает на модуль ОбщийМодуль.КорректировкаСтоимости.Модуль, а именно на процедуру РассчитатьСписаниеПоСредней. Основная проблема часто кроется в неэффективном алгоритме поиска состояний товара. В типовом коде часто встречается конструкция «Для Каждого Элемент Из Соответствие», где внутри цикла происходит сравнение свойств структур. Если количество перемещений товара велико, система вынуждена выполнять миллиарды операций сравнения, что приводит к квадратичной сложности алгоритма.

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


Для Каждого Строка Из Таб Цикл
    // поиск выполняется полным перебором
    НайденоСостояние=Ложь;
    Для Каждого ЭлементСостояние Из СоотвПараметровСостояний Цикл
        НайденоСостояние=Истина;
        Для Каждого Элемент Из СтруктураСостояния Цикл
            НайденоСостояние = НайденоСостояние И (ЭлементСостояние.Значение[Элемент.Ключ] = Строка[Элемент.Ключ]);
        КонецЦикла;
        Если НайденоСостояние Тогда
            Прервать;
        КонецЕсли;
    КонецЦикла;
КонецЦикла;

В данном примере СоотвПараметровСостояний — это объект типа Соответствие, но поиск по нему идет не по ключу (что было бы быстро), а путем перебора всех его элементов. Это сводит на нет все преимущества использования хеш-таблиц.

Решение №1: Использование индексированных таблиц значений

Для оптимизации этого узкого места мы заменим перебор в Соответствие на поиск в объекте ТаблицаЗначений с использованием встроенных индексов платформы 1С. Это позволит системе использовать быстрый поиск вместо последовательного сканирования.

Разберем по шагам, как изменить код:

  1. Создадим вспомогательную таблицу значений ТаблицаПоиска.
  2. Добавим в неё колонки, соответствующие полям поиска из структуры состояния.
  3. Установим индекс по всем ключевым полям поиска с помощью метода Индексы.Добавить().
  4. Заменим вложенный цикл на метод НайтиСтроки().

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


// Создаем таблицу для быстрого поиска
ТаблицаПоиска = Новый ТаблицаЗначений;
СтрокаПолейПоиска = "";

// Наполняем колонки исходя из структуры состояния
Для Каждого Колонка Из Таб.Колонки Цикл
    // ... логика определения колонок состояния ...
    ТаблицаПоиска.Колонки.Добавить(Колонка.Имя, Колонка.ТипЗначения);
    СтрокаПолейПоиска = СтрокаПолейПоиска + ?(ЗначениеЗаполнено(СтрокаПолейПоиска), ",", "") + Колонка.Имя;
КонецЦикла;

ТаблицаПоиска.Колонки.Добавить("Индекс");
// Критически важный момент: добавляем индекс по полям поиска
ТаблицаПоиска.Индексы.Добавить(СтрокаПолейПоиска);

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

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

Решение №2: Оптимизация поиска встречных перемещений

После устранения первой проблемы замер производительности часто смещается на следующий блок — поиск и сворачивание встречных перемещений (например, когда товар перемещался со Склада А на Склад Б и обратно в рамках одного периода). В типовом коде это также реализовано через вложенный цикл, что дает сложность $O(N^2)$.

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

Вместо вложенного цикла Пока Инд2 < КолВо Цикл мы рекомендуем использовать временное Соответствие, где ключом будет выступать комбинация Источник + "_" + Приемник. Это позволит найти встречное движение за одну операцию поиска.

Пример логики для ускорения этого блока:


ХешПеремещений = Новый Соответствие;
// Сначала заполняем хеш-карту всех перемещений
Для Каждого Строка Из Таб Цикл
    Ключ = Строка.Источник + "_" + Строка.Приемник;
    ХешПеремещений.Вставить(Ключ, Строка);
КонецЦикла;

// Затем ищем встречные одним проходом
Для Каждого Строка Из Таб Цикл
    ОбратныйКлюч = Строка.Приемник + "_" + Строка.Источник;
    Строка2 = ХешПеремещений[ОбратныйКлюч];
    
    Если Строка2 <> Неопределено Тогда
        // Выполняем взаимозачет количеств и стоимостей
        // ...
    КонецЕсли;
КонецЦикла;

Решение №3: Технические настройки и инфраструктура

Если оптимизация кода выполнена, но закрытие месяца всё еще занимает значительное время, проанализируем влияние серверной части и СУБД. Процесс корректировки стоимости генерирует огромную нагрузку на временные таблицы (TempDB).

Рассмотрим важные рекомендации по настройке железа и SQL-сервера:

Дополнительные советы по работе с данными

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

1. Отрицательные остатки: Если в системе есть списания «в минус», алгоритм корректировки стоимости может работать некорректно или совершать лишние итерации по «развязыванию» стоимости. Предварительное исправление отрицательных остатков значительно ускоряет закрытие месяца.

2. Зацикленные перемещения: Встречаются ситуации, когда документы создают петли: Склад 1 -> Склад 2 -> Склад 1. Алгоритм пытается исключить такой «холостой ход», и чем больше таких петель, тем сложнее расчет графа перемещений. Рекомендуется минимизировать такие операции в рамках одного месяца.

3. Свертка характеристик: Если в учете используются сотни характеристик, которые фактически не влияют на расчет стоимости, стоит рассмотреть возможность их упрощения или настройки разрезов учета, так как каждая аналитика добавляет размерности в таблицу перемещений.

Подводя итог, можно сказать, что основное ускорение (в 5 раз, как в рассмотренном нами случае) достигается за счет перевода алгоритмов с линейного поиска O(N) на поиск по индексам O(log N) или хеш-таблицам O(1). Совместное использование программной оптимизации и правильной настройки SQL-сервера позволяет превратить мучительное многочасовое ожидание в быстрый и предсказуемый процесс.

← На главную