Как оптимизировать производительность при получении представления объекта в 1С и избежать тяжелых запросов?

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

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

В этой статье мы подробно разберем, почему запросы в обработчиках получения представления категорически запрещены стандартами разработки «1С», к каким последствиям они приводят под нагрузкой, и как решить задачу вывода динамических данных архитектурно правильно и максимально быстро.

В чем заключается проблема производительности?

Проанализируем ситуацию, описанную в исходном обсуждении. Автор пытается динамически получать сегмент клиента из периодического регистра сведений РегистрСведений.СегментыКлиентов.СрезПоследних непосредственно в обработчике получения представления справочника Клиенты. На небольших базах это может работать незаметно. Но как только объем данных вырастает, а пользователи начинают активно открывать формы списков документов, строить отчеты или выполнять поиск, система начинает «зависать» на 0.5–0.8 секунд при каждом выводе строки.

Давайте разберемся, почему так происходит:

  1. Частота вызовов обработчика. Платформа «1С» вызывает обработчик ОбработкаПолученияПредставления колоссальное количество раз. Он срабатывает при визуализации каждой ссылки на форме, при генерации отчетов (если они строятся не через СКД или выводятся вручную), при записи и проведении документов, а также во время фоновых процессов обмена данными (например, при формировании журнала регистрации).
  2. Проблема N+1 запроса. Если в динамическом списке отображается 50 строк, и для каждой строки выполняется индивидуальный запрос СрезПоследних к СУБД, платформа выполнит 50 отдельных микрозапросов к базе данных вместо одного пакетного. Это лавинообразно нагружает процессор сервера приложений и СУБД.
  3. Блокировка фоновых процессов. Поскольку представления запрашиваются платформой и в неинтерактивных режимах (например, при выгрузке данных в другие системы), «тяжелый» обработчик может парализовать интеграционные шлюзы и регламентные задания.

Жесткие стандарты разработки «1С» (требования ИТС)

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

Эти обработчики должны работать исключительно «в памяти», оперируя уже готовыми данными, которые им передает платформа.

Штатный двухэтапный механизм работы платформы

Для оптимизации получения представлений «под капотом» платформы реализован эффективный двухэтапный механизм. Разберем его по шагам:

Шаг 1. Определение необходимых данных. В обработчике ОбработкаПолученияПолейПредставления мы сообщаем платформе, какие именно реквизиты объекта нам понадобятся для формирования представления. Платформа собирает эти требования и автоматически модифицирует свои внутренние запросы к СУБД, выбирая нужные поля одним пакетом вместе с основными данными списка.

Шаг 2. Формирование строки представления. В обработчик ОбработкаПолученияПредставления в параметр Данные прилетает готовая структура, содержащая значения запрошенных на первом шаге реквизитов. Нам остается лишь «склеить» их в нужную строку без единого обращения к базе данных.

Но как быть, если нужные данные (сегмент клиента) лежат во внешнем регистре сведений, а не внутри самого справочника? Напрямую пробросить поля регистра через этот механизм невозможно. Здесь нам на помощь приходит архитектурный паттерн кэширования данных.

Архитектурное решение: Кэширование данных в реквизит объекта

Чтобы обеспечить максимальную скорость работы интерфейса и соблюсти стандарты разработки, мы применим стратегию кэширования. Мы добавим в справочник Клиенты вспомогательный реквизит, куда будем записывать актуальный сегмент клиента. Получение представления будет происходить мгновенно из этого реквизита, а его актуализацию мы настроим в фоновом режиме.

Выполним реализацию этого подхода по шагам.

Шаг 1. Модификация метаданных

Добавим в справочник Клиенты новый реквизит:

Шаг 2. Реализация двухэтапного получения представления

Откроем модуль менеджера справочника Клиенты и пропишем стандартные обработчики платформы. Сначала укажем платформе, что нам нужен наш новый реквизит:


Процедура ОбработкаПолученияПолейПредставления(Поля, СтандартнаяОбработка)
	
	// Добавляем наш кэширующий реквизит в массив полей, которые платформа прочитает из БД
	Поля.Добавить("СегментКэш");
	
	// Отключаем стандартную обработку, чтобы платформа знала, что мы сами управляем составом полей
	СтандартнаяОбработка = Ложь;
	
КонецПроцедуры

Теперь напишем сам алгоритм формирования представления в памяти без использования запросов:


Процедура ОбработкаПолученияПредставления(Данные, Представление, СтандартнаяОбработка)
	
	// Проверяем, что платформа передала нам структуру с нужным полем
	Если Данные.Свойство("СегментКэш") Тогда
		
		НаименованиеКлиента = ?(Данные.Свойство("Наименование"), Данные.Наименование, "");
		
		Если ЗначениеЗаполнено(Данные.СегментКэш) Тогда
			// Формируем красивое представление: "Иван Иванов (VIP)"
			Представление = Строка(НаименованиеКлиента) + " (" + Строка(Данные.СегментКэш) + ")";
		Иначе
			Представление = Строка(НаименованиеКлиента);
		КонецЕсли;
		
		// Сигнализируем платформе, что представление успешно сформировано нами
		СтандартнаяОбработка = Ложь;
		
	КонецЕсли;
	
КонецПроцедуры

Шаг 3. Обеспечение актуальности данных (Подписка на события)

Данные в периодическом регистре сведений СегментыКлиентов могут меняться. Нам необходимо своевременно обновлять реквизит СегментКэш в справочнике Клиенты. Использовать регламентное задание, запускаемое раз в сутки — рабочий вариант, но пользователи могут жаловаться на неактуальность информации в течение дня.

Наиболее надежный и оперативный способ — использование механизма подписок на события. Создадим новую подписку на событие для регистра сведений.

Настройки подписки на событие:

Проанализируем и напишем код обработчика в общем модуле:


Процедура ОбновитьКэшСегментовПриЗаписиРегистраПриЗаписи(Источник, Отказ, Замещение) Экспорт
	
	// Если это обмен данными, пропускаем обработку во избежание зацикливания или замедления импорта
	Если Источник.ОбменДанными.Загрузка Тогда
		Возврат;
	КонецЕсли;
	
	// Собираем список клиентов, по которым изменились записи в регистре
	ТаблицаИзменений = Источник.Выгрузить();
	СписокКлиентов = Новый Массив;
	
	Для Каждого СтрокаТаблицы Из ТаблицаИзменений Цикл
		Если СписокКлиентов.Найти(СтрокаТаблицы.Клиент) = Неопределено Тогда
			СписокКлиентов.Добавить(СтрокаТаблицы.Клиент);
		КонецЕсли;
	КонецЦикла;
	
	Если СписокКлиентов.Количество() = 0 Тогда
		Возврат;
	КонецЕсли;
	
	// Запросом получаем актуальные срезы последних для измененных клиентов
	Запрос = Новый Запрос;
	Запрос.Текст = 
		"ВЫБРАТЬ
		|	СегментыКлиентовСрезПоследних.Клиент КАК Клиент,
		|	СегментыКлиентовСрезПоследних.Сегмент КАК Сегмент
		|ИЗ
		|	РегистрСведений.СегментыКлиентов.СрезПоследних(, Клиент В (&СписокКлиентов)) КАК СегментыКлиентовСрезПоследних";
	
	Запрос.УстановитьПараметр("СписокКлиентов", СписокКлиентов);
	РезультатЗапроса = Запрос.Выполнить();
	
	Если Не РезультатЗапроса.Пустой() Тогда
		Выборка = РезультатЗапроса.Выбрать();
		Пока Выборка.Следующий() Цикл
			
			// Блокируем и обновляем реквизит в справочнике Клиенты
			ОбъектКлиент = Выборка.Клиент.ПолучитьОбъект();
			Если ОбъектКлиент <> Неопределено Тогда
				// Записываем новое значение сегмента в кэш
				ОбъектКлиент.СегментКэш = Выборка.Segment;
				
				// Отключаем проверки при записи для ускорения процесса
				ОбъектКлиент.ОбменДанными.Загрузка = Истина;
				ОбъектКлиент.Записать();
				
				// Оповещаем платформу о необходимости сбросить клиентский кэш представлений
				ОповеститьОбИзменении(Выборка.Клиент);
			КонецЕсли;
			
		КонецЦикла;
	КонецЕсли;
	
КонецПроцедуры

Важный нюанс: Кэширование представлений на клиенте

Платформа «1С» в целях экономии трафика кэширует представления ссылок на стороне клиента (особенно в Тонком клиенте). Если вы обновили реквизит СегментКэш в базе данных на сервере, клиентское приложение может еще какое-то время отображать старое представление ссылки.

Чтобы принудительно заставить платформу обновить визуальное представление конкретного элемента на клиенте, используется встроенный метод ОповеститьОбИзменении(Ссылка). Вызов этого метода мгновенно очищает кэш представлений для переданного объекта, гарантируя, что пользователи увидят актуальные данные без перезапуска сеанса.

Особенности работы Системы Компоновки Данных (СКД)

В процессе обсуждения на форуме прозвучал важный вопрос: почему в типовых отчетах производительность представлений не страдает так сильно?

Дело в том, что СКД — это высокооптимизированный движок. При компоновке макета и выводе данных СКД самостоятельно анализирует структуру схемы компоновки и получает все необходимые представления скопом, упаковывая их в единый пакетный запрос к СУБД.

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

Подведем итоги

Мы спроектировали надежную архитектуру вывода связанных данных в представление объектов «1С»:

  1. Отказались от тяжелых запросов в обработчиках модуля менеджера, полностью обезопасив систему от фризов и зависаний интерфейса.
  2. Организовали надежное хранение (кэш) динамических данных непосредственно внутри целевого справочника.
  3. Настроили автоматическое обновление кэша в фоновом режиме через механизм подписок на события СУБД, минимизировав нагрузку на систему во время пиковой работы пользователей.

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

← На главную