Механизм расширений в платформе 1С:Предприятие стал стандартом де-факто для доработки типовых конфигураций. Он позволяет кастомизировать систему, сохраняя основную конфигурацию на полной поддержке. Однако среди разработчиков часто возникают споры о том, как расширения влияют на производительность системы. Некоторые специалисты отмечают субъективное ощущение замедления интерфейса и процессов, в то время как другие утверждают, что разница несущественна.
Проанализируем этот вопрос детально. Разберем внутренние механизмы работы платформы, рассмотрим результаты официальных тестов фирмы 1С, выясним физические причины снижения скорости работы на уровне СУБД и определим правила оптимизации, которые помогут минимизировать негативное влияние расширений на высоконагруженные системы.
Для объективной оценки обратимся к официальным исследованиям производительности, проведенным разработчиками платформы 1С. В ходе нагрузочного тестирования на 100 одновременно работающих пользователях сравнивалось время выполнения одной и той же тяжелой обработки, реализованной тремя разными способами. Тестовый модуль обработки содержал около 32 000 строк кода и 120 интерфейсных форм. Результаты замеров распределились следующим образом:
Проанализируем эти цифры. Расширения работают значительно быстрее внешних обработок, однако они стабильно уступают встроенному в основную конфигурацию коду. Выясним, почему возникает эта разница и какие внутренние процессы платформы её вызывают.
Разберем механизм компиляции модулей в 1С. Платформа является интерпретируемой средой, где исходный код компилируется в промежуточный байт-код, который затем исполняется виртуальной машиной платформы.
Когда мы запускаем код, встроенный в основную конфигурацию, он компилируется один раз при первом обращении (или при старте сервера) и кэшируется в оперативной памяти. В случае с внешними обработками компиляция происходит заново при каждом открытии файла или вызове метода из хранилища двоичных данных. Это объясняет, почему внешние файлы создают колоссальную нагрузку на процессор.
Расширения занимают промежуточное положение. Платформа компилирует модули расширений при старте сеанса пользователя. Но при каждом обращении к методам расширения виртуальной машине приходится выполнять сопоставление контекстов основной конфигурации и расширения. Наличие дополнительных уровней абстракции неизбежно требует дополнительных тактов процессора для адресации вызовов.
Одним из самых критичных факторов замедления работы баз данных при использовании расширений является изменение структуры метаданных. Рассмотрим, как платформа 1С проецирует новые реквизиты объектов из расширения на таблицы СУБД (MS SQL Server, PostgreSQL и др.).
Если мы добавляем новый реквизит в существующий справочник или документ непосредственно в основной конфигурации, платформа выполняет команду ALTER TABLE и добавляет колонку прямо в физическую таблицу этого объекта. Чтение данных происходит максимально быстро за один проход.
Когда мы добавляем новый реквизит к заимствованному объекту через расширение, физическая структура основной таблицы в СУБД не меняется. Вместо этого платформа создает в базе данных отдельную вспомогательную таблицу для хранения данных этого расширения. При попытке прочитать объект, открыть форму списка или выполнить запрос к этому документу, платформа на уровне СУБД генерирует запрос с оператором соединения таблиц LEFT JOIN.
Посмотрим на пример схемы запроса, который формируется автоматически под капотом платформы:
SELECT
TableMain.ID,
TableMain.Number,
TableExtension.NewAttribute_Field
FROM
_Document150 TableMain
LEFT JOIN
_Document150_Ext TableExtension ON TableMain.ID = TableExtension.ID
Если в базе данных хранятся миллионы записей документов, и к этому документу добавлено несколько реквизитов через расширения, СУБД тратит огромные ресурсы на постоянные соединения таблиц. Это приводит к росту загрузки дисковой подсистемы, неэффективному использованию индексов и, как следствие, к существенному замедлению работы интерфейса у конечных пользователей. Для анализа сгенерированных СУБД запросов и их сопоставления с объектами конфигурации можно использовать инструмент для перевода SQL-запросов профайлера в термины метаданных 1С.
Еще один источник снижения производительности — использование аннотаций перехвата методов. Рассмотрим ситуацию, когда разработчики активно модифицируют логику проведения документов или расчета цен с помощью аннотаций &Перед, &После и &Вместо.
При вызове любой стандартной процедуры платформа должна проверить стек расширений. Если в системе подключено несколько расширений (например, три разных расширения перехватывают метод проведения документа), платформа выстраивает цепочку вызовов. Выделение памяти под стек вызовов, передача параметров "по ссылке" и "по значению" между контекстами расширений и основной конфигурации создают накладные расходы.
Особую опасность представляет злоупотребление аннотацией &Вместо. Если разработчик полностью замещает метод основной конфигурации и не использует вызов оригинального метода через функцию ПродолжитьВызов(), платформа лишается возможности использовать внутренние оптимизации компилятора.
Проанализируем правильный шаблон перехвата метода, минимизирующий издержки:
&Вместо("РассчитатьСкидку")
Функция Расш1_РассчитатьСкидку(ПараметрыСкидки)
// Выполняем быструю подготовку данных
ПараметрыСкидки.Вставить("ДополнительныйКонтекст", Истина);
// Обязательный вызов оригинального метода для сохранения цепочки оптимизации
Результат = ПродолжитьВызов(ПараметрыСкидки);
// Выполняем постобработку результата
Возврат Результат;
КонецФункции
Если цепочка перехватов построена неоптимально, или внутри перехватчиков написан тяжелый код, время открытия форм документов и выполнения транзакций может вырасти в разы. Особенно критично это проявляется при обновлении интерфейсных элементов формы при каждом изменении реквизитов.
В высоконагруженных корпоративных системах (например, в крупных внедрениях 1С:ERP на несколько сотен активных пользователей) влияние расширений на общую производительность может стать критическим фактором.
Часто в расширения выносят интеграционные модули (модули работы с маркетплейсами, системы маркировки, EDI). Такие модули активно используют подписки на события для регистрации изменений объектов. Подписки, реализованные в расширении, срабатывают в рамках одной транзакции с проведением документов основной конфигурации.
Поскольку вызов подписок из расширения требует дополнительного времени на инициализацию контекста, время нахождения транзакции в открытом состоянии увеличивается. Это напрямую ведет к возникновению взаимных блокировок в СУБД (Deadlocks) и превышению времени ожидания блокировок у других пользователей. В результате показатель производительности APDEX резко снижается, а пользователи начинают жаловаться на зависания системы при проведении документов. Для удобного сбора технологического журнала и выявления причин зависаний подойдёт инструмент для замера производительности и поиска узких мест контура 1С.
Полностью отказываться от расширений не стоит — это отличный инструмент быстрой кастомизации. Однако необходимо соблюдать строгие архитектурные правила, чтобы не допустить деградации производительности базы данных.
Сформулируем ключевые правила для разработчиков:
LEFT JOIN при каждом запросе.&Перед и &После. Если без полного замещения кода не обойтись, обязательно вызывайте метод ПродолжитьВызов().Применяя данные подходы, мы сможем сохранить баланс между удобством поддержки типового решения и высокой скоростью работы информационной системы.