При работе в конфигурациях на обычных формах, таких как «1С:Управление торговлей 10.3» или «1С:Управление производственным предприятием 1.3», многие разработчики и системные администраторы сталкиваются со значительным замедлением системы при проведении документов. Особенно остро эта проблема проявляется в документах массового спроса — например, таких как Отчет о розничных продажах или Реализация товаров и услуг.
Основным «пожирателем» времени при проведении этих документов выступает общий модуль УправлениеЗапасамиПартионныйУчет и его экспортируемая процедура ДвижениеПартийТоваров. Время проведения одного документа может возрастать до 15–20 секунд, а при попытке группового перепроведения за длительный период система практически полностью парализуется. В этой статье мы подробно разберем причины этой проблемы и рассмотрим три эффективных способа ее решения.
Если мы запустим замер производительности в конфигураторе 1С во время проведения «тяжелого» документа, то увидим, что более 90% времени выполнения транзакции уходит на получение остатков партий. Проанализируем, где именно происходят задержки. Внутри общего модуля УправлениеЗапасамиПартионныйУчет находятся ключевые функции получения остатков:
ПолучитьДеревоПартийНаСкладахУпр()ПолучитьДеревоПартийНаСкладахБух()ПолучитьДеревоПартийНаСкладахНал()Внутри этих функций выполняется тяжелый типовой запрос. В нем виртуальная таблица остатков регистра накопления РегистрНакопления.ПартииТоваровНаСкладах.Остатки соединяется с регистром сведений РегистрСведений.СписанныеТовары. Фильтрация по номенклатуре в параметрах виртуальной таблицы остатков реализована через вложенный подзапрос следующего вида:
Номенклатура В (
ВЫБРАТЬ Номенклатура
ИЗ РегистрСведений.СписанныеТовары
ГДЕ Регистратор = &Ссылка
)
Почему это работает медленно на SQL Server и PostgreSQL? При таком синтаксисе СУБД часто не может построить оптимальный план запроса. Вместо быстрого поиска по индексу (Index Seek) оптимизатор СУБД выбирает сканирование всей таблицы остатков партий (Table Scan / Index Scan) либо использует неоптимальные соединения типа Nested Loops. Когда база данных содержит миллионы записей за несколько лет работы, этот запрос начинает выполняться критически долго. Это также приводит к массовым взаимоблокировкам (deadlocks) и ошибкам транзакций у пользователей.
---
Самый правильный технический способ решить проблему раз и навсегда — переписать типовой запрос в общем модуле, избавившись от вложенного подзапроса в параметрах виртуальной таблицы. Мы заменим его использованием индексированной временной таблицы.
Разберем этот процесс по шагам:
ПолучитьДеревоПартийНаСкладахУпр (и аналогичные для бухгалтерского и налогового учета) в модуле УправлениеЗапасамиПартионныйУчет.СписанныеТовары по текущему документу-регистратору.Номенклатура, Склад, ХарактеристикаНоменклатуры (если используется учет по характеристикам).Посмотрим на пример того, как должен выглядеть оптимизированный пакет запросов:
// Шаг 1. Помещаем списанные товары во временную таблицу
ВЫБРАТЬ
СписанныеТовары.Номенклатура КАК Номенклатура,
СписанныеТовары.ХарактеристикаНоменклатуры КАК ХарактеристикаНоменклатуры,
СписанныеТовары.Склад КАК Склад
ПОМЕСТИТЬ ВТ_СписанныеТовары
ИЗ
РегистрСведений.СписанныеТовары КАК СписанныеТовары
ГДЕ
СписанныеТовары.Регистратор = &Ссылка
ИНДЕКСИРОВАТЬ ПО
Номенклатура,
ХарактеристикаНоменклатуры,
Склад
;
// Шаг 2. Основной запрос получения остатков партий
ВЫБРАТЬ
ПартииОстатки.Номенклатура КАК Номенклатура,
ПартииОстатки.ДокументОприходования КАК ДокументОприходования,
ПартииОстатки.КоличествоОстаток КАК КоличествоОстаток,
ПартииОстатки.СтоимостьОстаток КАК СтоимостьОстаток
ИЗ
РегистрНакопления.ПартииТоваровНаСкладах.Остатки(
&МоментВремени,
Номенклатура В (ВЫБРАТЬ Номенклатура ИЗ ВТ_СписанныеТовары)
) КАК ПартииОстатки
Благодаря явному использованию индексированной временной таблицы ВТ_СписанныеТовары, СУБД мгновенно определяет точный список номенклатуры и строит идеальный план запроса. На практике этот шаг сокращает время проведения одного документа с 15–20 секунд до десятых долей секунды.
---
Если конфигурация сильно изменена и вы опасаетесь вносить правки в программный код общего модуля, можно воспользоваться штатным функционалом отложенного проведения партий.
Проанализируем этот механизм подробнее. По умолчанию в 1С:УТ 10.3 настроено списание партий в режиме реального времени («онлайн»). Это значит, что при проведении каждого документа система пытается сразу рассчитать себестоимость списания. Если снять эту нагрузку с пользователей, документы будут проводиться мгновенно (за 1–2 секунды), так как они не будут обращаться к регистру партий.
Разберем, как настроить отложенное проведение по шагам:
ПартииТоваровНаСкладах формироваться не будут, а граница последовательности партионного учета будет сдвигаться назад.Проведение по партиям в ночное время, когда нагрузка на систему минимальна.Важный нюанс: Многие пользователи переживают, что если изменить документ «задним числом», то данные не актуализируются. Это не так. При изменении документа в прошлом периоде граница последовательности партионного учета автоматически сдвинется назад. При очередном ночном запуске регламентной обработки система последовательно перепроведет и пересчитает партии со дня изменения по текущую дату.
---
Иногда даже после переписывания запросов скорость проведения может периодически падать. Выясним причину этого поведения на уровне СУБД. В процессе активной работы пользователей таблицы партионного учета и регистр СписанныеТовары подвергаются колоссальной нагрузке — за день туда записываются, изменяются и удаляются сотни тысяч строк. Это приводит к быстрому устареванию статистических данных СУБД.
Когда статистика СУБД по этим таблицам «устаревает», оптимизатор SQL-сервера начинает строить неверные планы выполнения даже для оптимизированных запросов. Для решения этой проблемы необходимо настроить регулярное обслуживание базы данных:
СписанныеТовары и регистр накопления ПартииТоваровНаСкладах) каждые несколько часов или даже каждый час в течение рабочего дня.Rebuild, для слабо фрагментированных — Reorganize. Это позволит держать поисковые индексы таблиц партионного учета в максимально производительном состоянии.Для достижения максимального эффекта мы рекомендуем использовать комплексный подход. В первую очередь следует переписать функции общего модуля УправлениеЗапасамиПартионныйУчет с использованием индексированных временных таблиц — это даст кратный прирост скорости «здесь и сейчас». Во вторую очередь необходимо настроить регулярные регламентные процедуры обслуживания индексов и сверхчастотного обновления статистики на сервере СУБД. Сочетание этих методов позволит документам проводиться мгновенно и навсегда решит проблему транзакционных блокировок при партионном учете.