Исправляем проблемы производительности в конфигурации ERP - 7 примеров

23.05.22

База данных - HighLoad оптимизация

Злободневные примеры поиска и исправления проблемных мест в конфигурациях ERP/УТ/КА на СУБД Postgres.

Продолжаем набор статей по вопросам проблем производительности. Это следующая часть разборов ситуаций после перевода большой базы ERP на Postgres. Помните мы писали в предыдущей статье "нагрузочное тестирования в 5000+ онлайн" что вы будьте заниматься вопросами оптимизации? Это то что мы делали и Вам предстоит, если соберетесь выполнять подобную процедуру.

Рассматриваемые вопросы и примеры решения ситуаций живые, интересные и злободневные, и до сих пор живут в актуальных конфигурациях ERP/УТ/КА. Мы рассмотрели 7 разных показательных ситуаций и соответствующие им пути решения, но по факту таких доработок было значительно больше. Скорее всего приведем еще подборку, но позже. 

 

Структура статьи:

Вступление
1. Проблема быстродействия АРМ "Управление поступлением".
2. Опять 25. Получаем данные через две точки.
3. Плохой план Postgre SQL.
4. JIT оптимизация - это такая оптимизация!
5. Через две точки + плохие поля выбора и ужасные СКД отборы по ним.
6. Бесполезный индекс скан и отключенные итоги.
7. Нет отборов внутри виртуальных таблиц. Как так получилось?
Выводы

 

Вступление

 

О чем мы хотим рассказать?

 

Мы расскажем и покажем последовательность действий по выявлению проблемных мест производительности, а также реализацию исправлений на актуальных конфигурациях ERP/УТ/КА. Рассмотрим некоторые отличия в поведении MS SQL Server и PostgreSQL. Увидим, что если писать код в соответствии с рекомендациями, то система сможет относительно хорошо работать в любой СУБД.

Где-то мы будем более подробно рассказывать, где-то чуть более сжато. Но от этого ценность этого поста не снижается. Очень подробно про расследование проблемы производительности мы писали ранее в статье "Пример пошагового решения проблемы производительности на базе Postgres SQL с картинками", попробуйте ознакомиться с ней прежде.

Подробное описание рассматриваемых проблем приведено в статье "Распространенные ошибки разработчиков, приводящие к проблемам производительности", т.ч. для кого будет непонятно, почему, открывайте и ищите пример. А в статье Смотрим запросы и планы 1С по следам ошибок разработчиков, приводящих к проблемам производительности приведены картинки планов.

 

Краткое техническое вступление

 

Прежде чем вы начнете работать над вопросами мониторинга производительности, вам необходимо будет определиться с инструментами. Существует не так много инструментов, а хороших и удобных инструментов совсем мало. В нашем случае мы будем использовать конфигурацию (про которую я уже писал)  "Мониторинг производительности" с широким набором плагинов. Нам понадобятся следующие плагины и функционал:

  • Парсер технологического журнала (встроен в конфигурацию);
  • Парсер метрик производительности performance monitor для windows (встроен в конфигурацию);
  • Классификатор ошибок технологического журнала;
  • Обработка получения данных RAS 1C (с возможностью получения данных через com соединение).
  • И др.

На что смотрим и ищем в журнале мониторинга (кратко):

  • Долгие запросы, те, которые сильно отклоняются от общей картины в 2,3 и больше раз. Если у вас в среднем 10 с, то запросы 20,30, 60, 200, 1000 с точки оптимизации. Находим наиболее критичные и начинаем с них.
  • Частые запросы середнячки и трудяги. Те, которых очень много. Отбираем и смотрим, можно ли их оптимизировать.
  • Запросы с большим количеством возвращаемых данных. Отчеты, которые возвращают десятки тысяч строк.  Если на выходе пакета 10-15 записей, а промежуточные данные миллионы, то, возможно, не хватает фильтров.

На что смотрим в планах:

  • Сканы таблиц, вместо них должны скорее всего быть индексы.
  • Большие потоки данных, а на выходе 1-2 строки.
  • Плохие отборы/фильтры.
  • Множественные связи.
  • Большое количество источников данных. 

Что смотрим в запросах:

  • Большой текст,
  • Большое количество таблиц (тоже может быть через две точки от поля составного типа),
  • Длинные строки содержащие CASE … THEN CASE …. (это оператор через две точки от поля составного типа)
  • Операторы LIKE,  ORDER по каким полям, большое количество полей у GROUP
  • Большое количество данных.

Про то, как отлавливать план запроса, я рассказывал в предыдущей статье, к тому же на этом ресурсе полно других подобных статей.

Как улучшить понятность планов запросов в Postgres и MS SQL?

Для этого воспользуйтесь обработкой «Конвертация SQL в 1С». Она ищет таблицы и реквизиты, преобразует их в значения конфигурации. Запускать обработку необходимо в исследуемой конфигурации. Является частью Фреймворка «Мониторинг производительности».

Начинаем...5.4.3.2.1... поехали

 

1. Проблема быстродействия АРМ «Управление поступлением»

 

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

На решение проблемы отводилось довольно ограниченное время — пару часов. Получив фамилии пользователей мы погрузились в решение проблемы. Обратите внимание, что длительность в микро секундах, а если отбросить 6 нулей, то получим секунды. Время отработки запроса динамического списка на рисунке ниже от 100 до 4 000 с.

 

 

Теперь сделаем фильтр по одному пользователю:

 

 

Мы видим, что что-то произошло с 13 на 14 число. Первым делом посмотрим тексты запросов и сравним их. Это поле SQL для двух замеров до и после. В результате сравнения мы определили, что они одинаковые, т.е. это один и тот же запрос. Поэтому работаем дальше и пытаемся понять, что тут не так.

Определяем и смотрим позицию возникновения проблемы по Context.

 

 

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

 

 

Обращаем внимание на относительно большое количество таблиц и наличие нескольких операторов "ВЫБОР" в поле "Комментарий" (выделено красной рамкой). Это выглядит как получение данных через две точки. Странно! Давайте смотреть дальше.

По тексту мы видим основную таблицу - "ЖурналДокументов.СкладскиеОрдера". Это второй динамический список, который расположен внизу формы. 

 

 

Открываем форму и смотрим свойство поля "Комментарий" формы.

 

 

Тут мы с вами видим, что путь к данным ведет не к ожидаемому реквизиту «Комментарий» журнала «СкладскиеОрдера», а к реквизиту «Ссылка.Комментарий» - обращение через две точки. Вот такая мина подложена в одном из самых востребованных АРМ конфигурации. В итоге вместо как минимум одной RLS  по журналу, будут добавляться платформой 1С еще три ограничения по всем ордерам. А может и больше, зависит от того, как группы доступа настроены администратором базы данных. 

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

Итоги: Мы обнаружили и исправили одну грубейшую ошибку рабочего места «Управление поступлением», приводящую к непозволительным и чудовищным падениям производительности в некоторых случаях. Пользователи довольны, а мы продолжили далее расследовать причину резкого снижения производительности в рамках зависимостей данных и RLS.

P.S. Данная ошибка на момент решения проблемы не исправлена в типовых конфигурациях ERP/КА/УТ. 

 

2. Опять 25. Получаем данные через две точки.

 

Решили посмотреть почему такое большое количество маленьких запросов 6-12 секунд. К тому же при выполнении анализа на глаза попался кластер содержащий вхождения слов «файл», «управление доступом» - мы воспользовались функционалом обработки из статьи "Автоматическая классификация технологического журнала" (подобный подход также применим к контекстам запросов, т.е. можем получить суждение, к какому кластеру относится подобный длительный запрос). 

 

 

Ого. Достаточно много позиций. Иногда достигает 10-15 позиций в минуту при самом пике нагрузки. Давайте посмотрим на частоту появления данной проблемы по всей базе данных, сгруппированную по дням:

 


Как мы видим, это довольно частая операция. Из графика видно, что до 16 часов суммарно процессор на сервере занимается непонятно чем - фактически минус одно ядро!

 

 

Позиция выполнения запроса. Что же тут не так? Давайте глянем текст запроса в SQL. Он очень большой.

 

 

Код совсем небольшой, и в нем нет такой кучи таблиц! Но зато есть выбор через две точки. Который и выдает нам соединения. В запросе мы находим в самом низу таблицу со следующим индексом «T258». 258 соединений. Вот зачем так делать?

 

 

Решение:

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

 

 

В результате время с 13 с на тесте снизилось до 1 мс. Отличное решение!

Итоги оптимизации: Мы фактически устранили ошибку в типовой конфигурации, которая при достаточно больших объемах базы данных приводила к неоптимальной работы системы на обоих базах СУБД. 

 

3. Плохой план PostgreSQL

 

Смотрим дальше и находим следующую операцию, которая небольшая, но вызывает проблемы 6-12 с в зависимости от пользователя:

 

 

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

 

 

Давайте посмотрим контекст:

 

 

Как мы видим, общий модуль управления доступом и функция «Установить параметры сеанса». Идем в конфигурацию и ищем позицию в исходном коде.

Запрос представлен пакетом, нам нужен первая часть:

 
 Код запроса

 

ВЫБРАТЬ РАЗЛИЧНЫЕ
	ЗначенияПоУмолчанию.ТипЗначенийДоступа КАК ТипЗначений,
	ЗначенияПоУмолчанию.ВсеРазрешеныБезИсключений КАК ВсеРазрешеныБезИсключений
ПОМЕСТИТЬ ЗначенияПоУмолчаниюДляПользователя
ИЗ
	РегистрСведений.ЗначенияГруппДоступаПоУмолчанию КАК ЗначенияПоУмолчанию
ГДЕ
	ИСТИНА В
			(ВЫБРАТЬ ПЕРВЫЕ 1
				ИСТИНА
			ИЗ
				Справочник.ГруппыДоступа.Пользователи КАК ГруппыДоступаПользователи
					ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.СоставыГруппПользователей КАК СоставыГруппПользователей
					ПО
						ГруппыДоступаПользователи.Ссылка = ЗначенияПоУмолчанию.ГруппаДоступа
							И ГруппыДоступаПользователи.Пользователь = СоставыГруппПользователей.ГруппаПользователей
							И СоставыГруппПользователей.Пользователь = &ТекущийПользователь)
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ РАЗЛИЧНЫЕ
	ЗначенияПоУмолчанию.ТипЗначений КАК ТипЗначений
ИЗ
	ЗначенияПоУмолчаниюДляПользователя КАК ЗначенияПоУмолчанию

СГРУППИРОВАТЬ ПО
	ЗначенияПоУмолчанию.ТипЗначений

ИМЕЮЩИЕ
	МИНИМУМ(ЗначенияПоУмолчанию.ВсеРазрешеныБезИсключений) = ИСТИНА

 

Как вы помните, проблемный кусок начинался с оператора «Insert», значит проблему вызывает первая часть. Запрос очень похож на использующиеся в RLS системы БСП.

Он не выглядит сложным, давайте взглянем на план запроса:

https://explain.tensor.ru/archive/explain/533c226e2a0008b2641ac0eb8ba205a2:0:2022-02-03#explain

 

 

Как мы видим самый тяжелый блок — начальный. Используется оператор соединения циклом, выполняется сканирование более 3 млн раз (циферки на верху кружка).

 

 

Давайте посмотрим как выглядит тот же план в базе СУБД MS SQL

 

 

Планы довольно похожи, за исключением времени выполнения (около 10 мс) и количества считанных и обработанных данных.

Теперь выполним рефакторинг-оптимизацию. Вынесем соединение из оператора «В» в соединение таблиц следующим образом:

 
 Оптимизированный запрос

 

ВЫБРАТЬ РАЗЛИЧНЫЕ
	ЗначенияПоУмолчанию.ТипЗначенийДоступа КАК ТипЗначений,
	ЗначенияПоУмолчанию.ВсеРазрешеныБезИсключений КАК ВсеРазрешеныБезИсключений
ПОМЕСТИТЬ ЗначенияПоУмолчаниюДляПользователя
ИЗ
	РегистрСведений.ЗначенияГруппДоступаПоУмолчанию КАК ЗначенияПоУмолчанию
		ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.ГруппыДоступа.Пользователи КАК ГруппыДоступаПользователи
		ПО (ГруппыДоступаПользователи.Ссылка = ЗначенияПоУмолчанию.ГруппаДоступа)
		ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.СоставыГруппПользователей КАК СоставыГруппПользователей
		ПО (ГруппыДоступаПользователи.Пользователь = СоставыГруппПользователей.ГруппаПользователей)
			И (СоставыГруппПользователей.Пользователь = &ТекущийПользователь)

 

Запрос выполняется в консоли запросов менее чем за 10 мс. Время работы стало приемлемо. Посмотрим план выполнения:

https://explain.tensor.ru/archive/explain/947e585afc8fd23b64a7f9331d406320:0:2022-02-03#explain

 

 

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

Получим план исправленного запроса в базе MS SQL. Он практически не изменился. По времени выполнения и количеству считываемых и обрабатываемых записей остался на том же уровне.

 

 

Итоги оптимизации: Успех! Мы фактически вернули быстродействие процедуры в целевые незначительные по времени рамки, избавив пользователей от проблемы ожидания лишних 5-12 с при запуске внешних обработок и отчетов. Быстродействие увеличилось в 5000 раз. Фактически мы выполнили оптимизацию под требования базы постгре, но также поведение базы MS SQL не изменилось. Данная процедура рефакторинга может быть применена в изначальной конфигурации вендора 1С для конфигураций ERP/УТ/КА.

P.S. Проверили на Postgres 13, тот же запрос выполняется гораздо лучше 500 мс (в 20 раз лучше),  план более правильный, но все равно немного хуже оптимизированного:

https://explain.tensor.ru/archive/explain/613554aae7064b7e3d010e3b34f68d00:0:2022-04-01#visio

 

 

4. JIT оптимизация — это такая оптимизация!

 

В данном примере речь пойдет об оптимизаторе Postgres. Мне давно эти всякие штучки-дрючки казались подозрительными, но все времени не было детально посмотреть. Ситуация проявилась после обновления платформы. Этот шаг был необходим в связи с ситуацией рассмотренной в примере «Пример расследования проблемы производительности по шагам с картинками». Согласно рекомендациям мы обновили платформу у заказчика и со спокойной совестью отправились спать, чтобы утром прикинуть, насколько эффективно работает новая платформа, но… Утро не было таким радостным, а скорее облило словно холодный душ, а к вечеру ситуация стала довольно очевидна — это выглядело как провал, а не успех. На рисунке красным показана дата обновления платформы с 8.3.16 на 8.3.19. Мы видим резкий рост проблемных запросов.

Контрмеры были предприняты — показано синим цветом.

 

 

Что могло пойти не так? Это была проблема, которую необходимо было срочно решать. Со стороны службы сопровождения пользователей пришли сообщения о проблемных ситуациях, основное недовольство пользователей было связано с поведением списка задач. Мы построили отчет и «бинго», таблица по задачам показывала именно то увеличение времени.

 

 

Начали смотреть по пользователям. Сложилась следующая картинка, время выполнения по пользователям не изменилось и осталось средним, но появилось много новых пользователей с временем более 5 с. Знаете почему такое критичное количество? Ответ довольно прост — форма мои задачи обычно находится на начальной странице и стоит авто обновление раз в минуту. Отключать авто обновление нельзя, т.к. новые задачи должны появляться без принудительного обновления списка пользователем. Удобно было бы, если бы появлялось уведомление о новых задачах, но, думаю, это будет в следующих релизах.

План запроса

https://explain.tensor.ru/archive/explain/50e1baada886a76571761e2d06468b05:0:2022-03-06#visio

 

 

Удивила выделенная операция — оператор «Limit», которая занимает до 93.3%. Это довольно непонятное состояние. Общее время получилось в районе 25 с.

 

 

Обратите внимание, сколько времени занимает выполнение оптимизации оператором JIT.

Для того чтобы оценить, какую выгоду вносит сам jit, нам необходимо выполнить запрос с отключенным оптимизатором.

 

 

Для этого берем запрос и выполняем команду с отключенным оптимизатором. План выглядит следующим образом:

https://explain.tensor.ru/archive/explain/85975ea000377da98ffea641e1672d88:0:2022-03-07#visio

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

Решение:

Для дальнейших действий можно выделить несколько вариантов:

  • отключить в настройках конфигурации JIT = off. Полностью отключаем.
  • увеличить параметры срабатывания  jit_optimize_above_cost, по умолчанию стоит 500 000.
  • отключить использование оптимизатора  jit_optimize_above_cost=-1.

Первоначально было принято решение увеличить порог срабатывания до  значения 1 500 000. Эффект получился практически мгновенным - более 70% запросов ушло. В таблице данное событие отражает пятница.

Хочу отметить тот факт, что сами разработчики постгрез и jit отмечают следующее:  включать этот механизм для всех ситуаций не стоит, настраивайте под свою систему.

Но все равно остались некоторые запросы и был выполнен следующий шаг:  jit_optimize_above_cost=-1.
Показатель рабочая суббота. Как видно из рисунка, снизилось не только количество событий, но и среднее время выполнения. Фактически исчезло большое добавочное время работы JIT.

https://explain.tensor.ru/archive/explain/9f366440c300acdd2ad6a83185761eaf:0:2022-03-07#visio

 

 

Время сократилось до 4 с. Но оптимизатор продолжает вносить лишнюю задержку.

 

 

Смотрим далее. Количество проблемных запросов уменьшилось, но не исчезло совсем. 

 

 

Проблема, которая осталась — это время на обработку RLS. Из картинки видно, что акцент на операторе сканирования таблицы «Группы доступа». 

https://explain.tensor.ru/archive/explain/a41092a7a08caad8a7d7afc4393fab30:0:2022-03-07#visio

 

 

Решение данного вопроса может состоять в двух следующих частях:

  • анализ текущих настроек прав доступа и удаление излишних
  • использование другой модели RLS, к этому варианту мы вернемся в следующих статьях.

 

5. Через две точки + плохие поля выбора и ужасные СКД отборы по ним

 

Открыли список и поставили отбор на 1000 с. И немножко ужаснулись тому, что увидели. Но самое странное — это запросы длительностью в 20 000 -34 000 с.

 

 

План запроса искать бессмысленно, обычно на такую длительность они просто не сохраняются. Открываем контекст и смотрим где это.

 

 

Тут мы столкнулись с проблемой медленного открытия формы динамического списка в базе при подборе товаров в коммерческое предложение. Давайте посмотрим, что там такое, судя по всему ничего хорошего!

 

 

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

 
 Изначальный запрос

 

Через пару минут, может кто-то быстрее, мы замечаем довольно очевидную и грубую ошибку - получение данных через две точки от составного типа.

 

 

Хотел выполнить запрос и посмотреть его план, но у меня не хватило терпения. Поэтому план запроса не привожу, если кто-то соберется духом, то пусть скинет ссылку.

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

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

 
 Запрос оптимизированный, первая итерация

 

Выполняем запрос и смотрим его план:

https://explain.tensor.ru/archive/explain/bfb5890f29681376b39e60668b20b169:0:2022-03-29#visio

 

\

 

Вроде все здорово. Вносим код в конфигурацию. Но на форме есть флажок фильтра "Только заказы по организации ...", и если мы его активируем, то быстродействие у нас опять приземляется. Не так сильно, но 60 с тоже много. Что за дела? Должно же быть еще быстрее, мы же добавили фильтр?

Смотрим план запроса для этой ситуации:

https://explain.tensor.ru/archive/explain/f1a39936379c9917f0f27972b3df5c5d:0:2022-03-29#visio

 

 

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

Возьмем тексты запросов первого и второго случая, и их сравним. В результате увидим отличие в секции «ГДЕ» - вылезает наш отбор по организации. Но выглядит он странно, ужасно и непонятно, что это и как такое получилось? 

 


Объясняем. Поле «Организация» у нас находится в выбранных на вывод полях.

 

 

Далее в коде есть функция, которая добавляет по нему отбор СКД. А СКД накладывает отбор, взяв представление вывода поля и добавив его в условие примерно так:

 

 

И далее у планировщика нет такого индекса и он не понимает, как сделать правильно, и мы видим снижение быстродействия.

Зачем так сделано? Видимо, этот код пришел из глубокой древности, когда ничего про оформление динамических списков разработчику не было известно и такой возможности не было, возможно, с обычных форм. Цель - для того случая, когда отсутствует заказ, чтобы вместо демонстрации пустой ссылки выводился текст «не используется».

Убираем эту конструкцию для поля организация и переписываем запрос еще раз:

 
 Еще одна итерация оптимизации

 

Наконец! Наш запрос начал летать:

https://explain.tensor.ru/archive/explain/1c2ce18da2cf7dc94e08fbe5bc0ecf45:0:2022-03-29#visio

 

 

Резюме: время выполнения мы снизили на просто космическую разницу с 34 000 с до 100 мс, это ускорение более чем в 340 000 раз. И запомните, что используем везде условное оформление для динамических списков, а всю ересь и подобные выкрутасы выше надо запрещать и бить по рукам!

P.S. Данная ошибка на момент решения проблемы не исправлена в типовых конфигурациях ERP/КА/УТ. 

 

6. Бесполезный индекс скан и отключенные итоги

 

Продолжаем смотреть, что  у нас еще "плохого" в базе данных. Ставим отбор в 300 сек и немного «ужасаемся» от такого количества проблемных ситуаций. Мы видим — ультра долгие запросы. Среди очень больших времен выполнения запросов в глаза сразу бросается проблема с пиком в 1400 секунд (это чуть больше 23 минут). Можно уже мысленно провести параллель с «эстонской» оперативной базой учета.

 

 

Посмотрим где это встречается.

 

 

Давайте посмотрим картинку с отбором по вхождению в контекст условия «РабочееМестоМенеджераПоДоставке». Что тут у нас единичный или закономерность?

 

 

Как мы видим, то прослеживается закономерность. И подсказка нам сообщает, что это все происходит в динамических списках.

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

Посмотрим план запроса, конечно, предварительно его преобразуем через конвертер SQL в 1С.

https://explain.tensor.ru/archive/explain/d676eebfb35e11e0379ae298b7d7607c:0:2022-01-22#visio

 

 

Как мы видим (два жирных красных кружка на рисунке выше), все основные ресурсы сжирает процедура Index Only Scan и группировка. Давайте глянем, что там такое. Суммарное время 1369 с (728 с + 641 с). Еще 111 с расходуются на соединение, идущее следом.

 

 

Наш Index Only Scan на самом деле выполняет лишнюю и ненужную работу, т.к. условие поиска по индексу «Index Cond» - «Область данных основные данные»=0 - фактически все записи таблицы - это бесполезные итоги! Много говорили про область данных и еще много скажут, но, на мой взгляд, это один из самых коварных элементов любой конфигурации БСП. По факту  результат работы этого оператора похож на последовательное чтение (Seq Scan), но только по таблице индекса. Слишком много данных читается, чтобы потом пользователю вывести 45 записей.

Открываем целевую конфигурацию, открываем форму «РабочееМестоМенеджераПоДоставке» и ищем запрос. Смотрим и ….


 

 
 Проблемный запрос

 

Как видим, то запрос выглядит совсем ужасно. Но давайте взглянем на SQL код postgres, чтобы понять, что там еще есть такое. Он нам понадобится для дальнейшего анализа проблем.

 
 SQL представление проблемного запроса

 

И наш блок — это вот такой код:

 

 

Система выполняет создание таблицы срез последних прямо в коде. Зачем она так делает?

Давайте проверим настройки этого регистра сведений. Открываем в конфигурации этот регистр сведений, переходим на вкладку прочее и смотрим позиции "Разрешить итоги".

 

 

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

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

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

https://explain.tensor.ru/archive/explain/bc39a18fd4de4601576b122332ffb35b:0:2022-01-22#visio

 

 

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

 

 

Тут уже сканирование таблицы точек погрузки и разгрузки. Давайте поищем в запросе таблицу с именем «T41».

 

 

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

 

7. Нет отборов внутри виртуальных таблиц. Как так получилось?

 

Анализируем журнал длительных запросов и замечаем еще один запрос длительностью более 1000 секунд. 

 

 

Контекст запроса выглядит следующим образом:

 

 

Давайте посмотрим, насколько часто встречается этот запрос. Для этого в фильтр списка добавим словосочетание из контекста. К примеру, вот так "ТоварыРазмещение.Загрузить(Запрос.Выполнить().Выгрузить())".

 

 

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

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

 

 
 Исходный запрос

 

ВЫБРАТЬ
	ТоварыВЯчейкахОбороты.Номенклатура КАК Номенклатура,
	ТоварыВЯчейкахОбороты.Характеристика КАК Характеристика,
	ТоварыВЯчейкахОбороты.Назначение КАК Назначение,
	ТоварыВЯчейкахОбороты.Упаковка КАК Упаковка,
	ТоварыВЯчейкахОбороты.Ячейка КАК Ячейка,
	ТоварыВЯчейкахОбороты.Серия КАК Серия,
	СУММА(ТоварыВЯчейкахОбороты.ВНаличииОборот) КАК ВНаличииОборот
ПОМЕСТИТЬ вт_КорректировкиНазначений
ИЗ
	РегистрНакопления.ТоварыВЯчейках.Обороты(,,Авто,) КАК ТоварыВЯчейкахОбороты
		ВНУТРЕННЕЕ СОЕДИНЕНИЕ вт_ДвиженияПоНазначению КАК вт_ДвиженияПоНазначению
		ПО ТоварыВЯчейкахОбороты.Номенклатура = вт_ДвиженияПоНазначению.Номенклатура
			И ТоварыВЯчейкахОбороты.Характеристика = вт_ДвиженияПоНазначению.Характеристика
			И ТоварыВЯчейкахОбороты.Ячейка = вт_ДвиженияПоНазначению.Ячейка
			И ТоварыВЯчейкахОбороты.Серия = вт_ДвиженияПоНазначению.Серия
			И ТоварыВЯчейкахОбороты.Упаковка = вт_ДвиженияПоНазначению.Упаковка
			И ТоварыВЯчейкахОбороты.Регистратор = вт_ДвиженияПоНазначению.Регистратор

СГРУППИРОВАТЬ ПО
	ТоварыВЯчейкахОбороты.Номенклатура,
	ТоварыВЯчейкахОбороты.Характеристика,
	ТоварыВЯчейкахОбороты.Назначение,
	ТоварыВЯчейкахОбороты.Упаковка,
	ТоварыВЯчейкахОбороты.Ячейка,
	ТоварыВЯчейкахОбороты.Серия

 

 

На рисунке ниже мы описали, как на плане запросов выглядит процедура формирования виртуальной таблицы "Обороты" и чуть ниже примерная последовательность выполнения.

https://explain.tensor.ru/archive/explain/1e16f485e815a9b4fcab27daf6e4db9e:0:2022-03-05#visio

 

 

Давайте рассмотрим, что тут происходит и в какой последовательности. 

  1. Первым делом выбираются записи из физической таблицы "Товары в ячейках". Обратите внимание на стрелку "товары в ячейках".
  2. Далее на каждую строку таблицы выше накладывается ограничение RLS. Позиция от левого края рисунка до стрелки "RLS фильтр".
  3. Следующим шагом происходит агрегация. Стрелочка "обороты".
  4. И в конечном шаге накладывается фильтр/отбор по тем позициям, которые нужны. Это позиция со стрелочкой "фильтр по нужным товарным позициям 'временная таблица'"

Согласитесь, агрегировать все записи (около 300 тыс.), чтобы потом взять 10-20 штук, это немного перебор? Вспоминаем рекомендации, которые написаны в желтой книжке: "Отборы делать необходимо внутри виртуальных таблиц".

Но почему же такую ошибку никто не заметил ранее? Все дело в том, что MS SQL выполняет данный запрос более умно и поднимает автоматически фильтр на уровень выше. Как мы увидим дальше, то план исходного запроса MS SQL похож на план оптимизированного запроса Postgres. Давайте посмотрим на картинку, как выглядит план запроса там:

  1. Из таблицы индекса "Товары в ячейках" выбираются сразу необходимые записи. Отражено красным текстом - "поиск по индексу в ячейках";
  2. Этот фильтр из временной таблицы применяется через Nested Loop. Позиция по тексту "фильтр по набору данных";
  3. Далее фильтруется по ограничению RLS - "отбор по RLS";
  4. И наконец формируется виртуальная таблица обороты в районе оператора "Stream Aggregate" - "обороты".

 

 

Решение: Оптимизируем запрос в соответствии с рекомендациями - добавляем отбор внутрь виртуальной таблицы "Обороты". Итоговый запрос приведен ниже:

 
 Оптимизированный запрос

 

ВЫБРАТЬ
	ТоварыВЯчейкахОбороты.Номенклатура КАК Номенклатура,
	ТоварыВЯчейкахОбороты.Характеристика КАК Характеристика,
	ТоварыВЯчейкахОбороты.Назначение КАК Назначение,
	ТоварыВЯчейкахОбороты.Упаковка КАК Упаковка,
	ТоварыВЯчейкахОбороты.Ячейка КАК Ячейка,
	ТоварыВЯчейкахОбороты.Серия КАК Серия,
	СУММА(ТоварыВЯчейкахОбороты.ВНаличииОборот) КАК ВНаличииОборот
ПОМЕСТИТЬ вт_КорректировкиНазначений
ИЗ
	РегистрНакопления.ТоварыВЯчейках.Обороты(
			,
			,
			Авто,
			(Номенклатура, Характеристика, Ячейка, Серия, Упаковка) В
				(ВЫБРАТЬ
					вт_ДвиженияПоНазначению.Номенклатура,
					вт_ДвиженияПоНазначению.Характеристика,
					вт_ДвиженияПоНазначению.Ячейка,
					вт_ДвиженияПоНазначению.Серия,
					вт_ДвиженияПоНазначению.Упаковка
				ИЗ
					вт_ДвиженияПоНазначению)) КАК ТоварыВЯчейкахОбороты
		ВНУТРЕННЕЕ СОЕДИНЕНИЕ вт_ДвиженияПоНазначению КАК вт_ДвиженияПоНазначению
		ПО ТоварыВЯчейкахОбороты.Номенклатура = вт_ДвиженияПоНазначению.Номенклатура
			И ТоварыВЯчейкахОбороты.Характеристика = вт_ДвиженияПоНазначению.Характеристика
			И ТоварыВЯчейкахОбороты.Ячейка = вт_ДвиженияПоНазначению.Ячейка
			И ТоварыВЯчейкахОбороты.Серия = вт_ДвиженияПоНазначению.Серия
			И ТоварыВЯчейкахОбороты.Упаковка = вт_ДвиженияПоНазначению.Упаковка
			И ТоварыВЯчейкахОбороты.Регистратор = вт_ДвиженияПоНазначению.Регистратор

СГРУППИРОВАТЬ ПО
	ТоварыВЯчейкахОбороты.Номенклатура,
	ТоварыВЯчейкахОбороты.Характеристика,
	ТоварыВЯчейкахОбороты.Назначение,
	ТоварыВЯчейкахОбороты.Упаковка,
	ТоварыВЯчейкахОбороты.Ячейка,
	ТоварыВЯчейкахОбороты.Серия

 

 

Давайте посмотрим, что изменилось в плане, а изменилось многое:

https://explain.tensor.ru/archive/explain/3e0fa834b97e79002b47f3376ea812c2:0:2022-05-22#visio

 

 

Порядок операций изменился, и время выполнения сократилось до 145 мс. Порядок действий примерно можно описать так:

  1. Теперь отбираются необходимые записи из индекса таблицы "Товары в ячейках". На рисунке отмечено "индекс по таблице 'товары в ячейках'";
  2. Фильтр для пункта 1 используется из временной таблицы. Отмечено "фильтр по набору данных";
  3. Следующим шагом выполняется проверка RLS - это "RLS ограничения";
  4. Последним шагом выполняется агрегация этих десятков записей, на рисунке "обороты" .

Резюме: В результате наших мероприятий время выполнения снизилось почти в ~7 000 раз, мы перестали обрабатывать лишние данные и сразу стало быстро. Поэтому - Пишите запросы согласно рекомендациям и не полагайтесь на умный планировщик.

 

Послесловие:

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


Выводы:

 

  1. В текущих типовых конфигурациях достаточно проблемных мест, которые серьезно снижают производительность высоконагруженных продуктовых баз.
  2. При переходе на СУБД Postgres с MS SQL будьте готовы вложить ресурсы (время, деньги, разработчиков) на доведение работы системы до приемлемого уровня, особенно в критичных местах для бизнеса. Будьте готовы к тому, что потребуется оптимизация запросов под целевую СУБД и в некоторых случаях под версию СУБД.
  3. Если вы задумаетесь над переходом на другую СУБД, то необходимо выявить ключевые точки в системе и выполнить нагрузочное тестирование. Про проведения нами такого тестирования мы рассказали в статье "Нагрузочное тестирование 5000+ онлайн".
  4. Ошибки, которые встречаются в конфигурации, фактически одни и те же (мы рассматривали тут - Распространенные ошибки разработчиков, приводящие к проблемам производительности):
    • выбор полей через точку в составных типах;
    • неоптимальные отборы;
    • отсутствие индексов;
    • сложные многоуровневые запросы;
    • невнимательность;
    • использование подхода - да и так сойдет;
    • квалификация разработчиков;
    • отсутствие нормального нагрузочного тестирования на больших базах под различными сложными RLS;
    • встречающиеся ошибки платформы 1С;
    • и другие.
  5. Следование стандартам разработки позволит избежать большинства проблем. 
  6. Используйте подходящие инструменты мониторинга, держите руку на пульсе, чтобы по возможности заранее выявлять и устранять проблемные узкие места.

 

См. также

Оптимизация нагрузки на ЦП сервера СУБД используя типовые индексы

HighLoad оптимизация Платформа 1С v8.3 Конфигурации 1cv8 Бесплатно (free)

Анализ простого плана запроса. Оптимизация нагрузки на ЦП сервера СУБД используя типовые индексы.

13.03.2024    2380    spyke    25    

38

Анализируем SQL сервер глазами 1С-ника

HighLoad оптимизация Инструменты администратора БД Платформа 1С v8.3 Конфигурации 1cv8 Абонемент ($m)

Обработка для простого и удобного анализа настроек, нагрузки и проблем с SQL сервером с упором на использование оного для 1С. Анализ текущих зааросов на sql, ожиданий, конвертация запроса в 1с и рекомендации где может тормозить

1 стартмани

15.02.2024    7353    149    ZAOSTG    66    

95

Удаление строк из таблицы значений различными способами с замером производительности

HighLoad оптимизация Платформа 1С v8.3 Конфигурации 1cv8 Абонемент ($m)

Встал вопрос: как быстро удалить строки из ТЗ? Рассмотрел пять вариантов реализации этой задачи. Сравнил их друг с другом на разных объёмах данных с разным процентом удаляемых строк. Также сравнил с выгрузкой с отбором по структуре.

09.01.2024    5765    doom2good    48    

63

Опыт оптимизации 1С на PostgreSQL

HighLoad оптимизация Бесплатно (free)

При переводе типовой конфигурации 1C ERP/УТ/КА на PostgreSQL придется вложить ресурсы в доработку и оптимизацию запросов. Расскажем, на что обратить внимание при потерях производительности и какие инструменты/подходы помогут расследовать проблемы после перехода.

20.11.2023    8586    ivanov660    6    

75

ТОП проблем/задач у владельцев КОРП лицензий 1С на основе опыта РКЛ

HighLoad оптимизация Бесплатно (free)

Казалось бы, КОРП-системы должны быть устойчивы, быстры и надёжны. Но, работая в рамках РКЛ, мы видим немного другую картину. Об основных болевых точках КОРП-систем и подходах к их решению пойдет речь в статье.

15.11.2023    4999    a.doroshkevich    20    

72

Начните уже использовать хранилище запросов

HighLoad оптимизация Запросы

Очень немногие из тех, кто занимается поддержкой MS SQL, работают с хранилищем запросов. А ведь хранилище запросов – это очень удобный, мощный и, главное, бесплатный инструмент, позволяющий быстро найти и локализовать проблему производительности и потребления ресурсов запросами. В статье расскажем о том, как использовать хранилище запросов в MS SQL и какие плюсы и минусы у него есть.

11.10.2023    15958    skovpin_sa    14    

98

Как эффективно настроить autovacuum в Postgres для 1С

HighLoad оптимизация Администрирование СУБД Платформа 1С v8.3 Бесплатно (free)

Кто не любит убирать мусор? Думаю, практически все, а вот в Postgres это обязательный ритуал для эффективной работы. Как эффективно настроить уборку за 1С в Postgres, можно прочитать в этой статье и еще раз задуматься о бесплатности Postgres.

05.08.2023    4974    1CUnlimited    5    

51
Вознаграждение за ответ
Показать полностью
Комментарии
В избранное Подписаться на ответы Сортировка: Древо развёрнутое
Свернуть все
1. sapervodichka 6690 23.05.22 14:38 Сейчас в теме
Спасибо, что делишься опытом!
m1_1976; Ingraf; Artem-B; akR00b; ivanov660; +5 Ответить
2. quazare 3574 23.05.22 15:02 Сейчас в теме
титанический труд
Ingraf; Yashazz; Drivingblind; ivanov660; pavlov_dv; +5 Ответить
3. CheBurator 3119 23.05.22 18:09 Сейчас в теме
"Недоделанные отчеты - это не беда. Главное - доделывайте детей. А то они вырастают и приносячт недоделанные отчеты. И никак не разорвать этот порочный круг"
Yashazz; sapervodichka; +2 Ответить
4. sapervodichka 6690 23.05.22 21:29 Сейчас в теме
(3)
Прикрепленные файлы:
m1_1976; user1286487; +2 Ответить
5. PerlAmutor 129 24.05.22 05:24 Сейчас в теме
Ну вроде бы не так много в 2.5.8 через двойную точку путей

Хотелось бы увидеть как можно оптимизировать текст запроса динамического списка формы Диспетчирование документа ЭтапПроизводства2_2:

ВЫБРАТЬ
	Этапы.Статус КАК Статус,
	&ПредставлениеЭтапа КАК ПредставлениеЭтапа,
	Этапы.Распоряжение КАК Распоряжение,
	Этапы.Распоряжение.Подразделение КАК ПодразделениеДиспетчер,
	Этапы.НаименованиеЭтапа КАК НаименованиеЭтапа,
	Этапы.ПроизводствоНаСтороне КАК ПроизводствоНаСтороне,
	Этапы.Подразделение КАК Подразделение,
	Этапы.Спецификация КАК Спецификация,
	Этапы.ПартияПроизводства КАК ПартияПроизводства,
	ВЫБОР
		КОГДА &ПланируетсяГрафикПроизводства
				И ИСТИНА В
					(ВЫБРАТЬ ПЕРВЫЕ 1
						ИСТИНА
					ИЗ
						РегистрСведений.ЗаданияКРасчетуГрафикаПроизводства КАК Т
					ГДЕ
						Т.Распоряжение = Этапы.Распоряжение
						И Т.ЭтапПроизводства = Этапы.Ссылка)
			ТОГДА ИСТИНА
		ИНАЧЕ ЛОЖЬ
	КОНЕЦ КАК ТребуетсяПланироватьГрафик,
	ЕСТЬNULL(СостоянияЭтаповПроизводства.ТребуетОбеспечения, ЛОЖЬ) КАК ТребуетсяОбеспечение,
	ВЫБОР
		КОГДА &ПланируетсяГрафикПроизводства
			ТОГДА ВЫБОР
					КОГДА ЕСТЬNULL(ГрафикПроизводства.ОграничиваетСрокВыпуска, ЛОЖЬ) = ИСТИНА
						ТОГДА 3
					КОГДА ЕСТЬNULL(ГрафикПроизводства.НаКритическомПути, ЛОЖЬ) = ИСТИНА
						ТОГДА 2
					ИНАЧЕ -1
				КОНЕЦ
		ИНАЧЕ -1
	КОНЕЦ КАК СостояниеОграниченийВГрафике,
	ЕСТЬNULL(СостоянияЭтаповПроизводства.СостояниеНаМежцеховомУровне, &ПустаяСсылкаСостояние) КАК СостояниеЭтапа,
	ЕСТЬNULL(СостоянияЭтаповПроизводства.СостояниеОпераций, &ПустаяСсылкаСостояниеОпераций) КАК СостояниеОпераций,
	ВЫБОР
		КОГДА Этапы.ТребуетсяЗаполнитьПоОперациям
			ТОГДА 3
		КОГДА СостоянияЭтаповПроизводства.СостояниеОпераций = ЗНАЧЕНИЕ(Перечисление.СостоянияОперацийЭтапаПроизводства.ОжидаетНазначения)
			ТОГДА 0
		КОГДА СостоянияЭтаповПроизводства.СостояниеОпераций = ЗНАЧЕНИЕ(Перечисление.СостоянияОперацийЭтапаПроизводства.ОжидаетЗавершения)
			ТОГДА 1
		КОГДА СостоянияЭтаповПроизводства.СостояниеОпераций = ЗНАЧЕНИЕ(Перечисление.СостоянияОперацийЭтапаПроизводства.Выполнено)
			ТОГДА 2
		ИНАЧЕ -1
	КОНЕЦ КАК КодСостоянияОпераций,
	Этапы.ТребуетсяЗаполнитьПоОперациям КАК ТребуетсяЗаполнитьПоОперациям,
	НЕ Этапы.ТребуетсяЗаполнитьПоОперациям
		И СостоянияЭтаповПроизводства.СостояниеОпераций = ЗНАЧЕНИЕ(Перечисление.СостоянияОперацийЭтапаПроизводства.ОжидаетНазначения) КАК ТребуетсяНазначитьОперации,
	ВЫБОР
		КОГДА &ПланируетсяГрафикПроизводства
			ТОГДА НЕ ГрафикПроизводства.ЭтапПроизводства ЕСТЬ NULL
		ИНАЧЕ НЕ НормативныйГрафикПроизводства.ЭтапПроизводства ЕСТЬ NULL
	КОНЕЦ КАК ГрафикРассчитан,
	ВЫБОР
		КОГДА &ПланируетсяГрафикПроизводства
				И НЕ ГрафикПроизводства.ЭтапПроизводства ЕСТЬ NULL
			ТОГДА НАЧАЛОПЕРИОДА(ГрафикПроизводства.НачалоЭтапа, ДЕНЬ)
		ИНАЧЕ НАЧАЛОПЕРИОДА(ДОБАВИТЬКДАТЕ(Этапы.Распоряжение.НачатьНеРанее, СЕКУНДА, ЕСТЬNULL(НормативныйГрафикПроизводства.ДлительностьДоЗапуска, 0)), ДЕНЬ)
	КОНЕЦ КАК ДатаСобытия,
	ВЫБОР
		КОГДА &ДатаАктуальности > ВЫБОР
					КОГДА &ПланируетсяГрафикПроизводства
							И НЕ ГрафикПроизводства.ЭтапПроизводства ЕСТЬ NULL
						ТОГДА ГрафикПроизводства.НачалоЭтапа
					ИНАЧЕ ДОБАВИТЬКДАТЕ(Этапы.Распоряжение.НачатьНеРанее, СЕКУНДА, ЕСТЬNULL(НормативныйГрафикПроизводства.ДлительностьДоЗапуска, 0))
				КОНЕЦ
				И Этапы.Статус В (&СтатусФормируется, &СтатусСформирован, &СтатусКВыполнению)
			ТОГДА ИСТИНА
		ИНАЧЕ ЛОЖЬ
	КОНЕЦ КАК Просрочен,
	ВЫБОР
		КОГДА &ПланируетсяГрафикПроизводства
				И НЕ ГрафикПроизводства.ЭтапПроизводства ЕСТЬ NULL
			ТОГДА ГрафикПроизводства.НачалоЭтапа
		ИНАЧЕ ДОБАВИТЬКДАТЕ(Этапы.Распоряжение.НачатьНеРанее, СЕКУНДА, ЕСТЬNULL(НормативныйГрафикПроизводства.ДлительностьДоЗапуска, 0))
	КОНЕЦ КАК ДатаНачала,
	ВЫБОР
		КОГДА &ПланируетсяГрафикПроизводства
				И НЕ ГрафикПроизводства.ЭтапПроизводства ЕСТЬ NULL
			ТОГДА ГрафикПроизводства.ОкончаниеЭтапа
		ИНАЧЕ ДОБАВИТЬКДАТЕ(Этапы.Распоряжение.НачатьНеРанее, СЕКУНДА, ЕСТЬNULL(НормативныйГрафикПроизводства.ДлительностьДоЗапуска + НормативныйГрафикПроизводства.Ресурсоемкость, 0))
	КОНЕЦ КАК ДатаЗавершения,
	ВЫБОР
		КОГДА &ПланируетсяГрафикПроизводства
				И НЕ ГрафикПроизводства.ЭтапПроизводства ЕСТЬ NULL
			ТОГДА ВЫБОР
					КОГДА Этапы.Статус В (&СтатусФормируется, &СтатусСформирован, &СтатусКВыполнению)
							И РАЗНОСТЬДАТ(ГрафикПроизводства.НачалоЭтапа, &ДатаАктуальности, ДЕНЬ) > 0
						ТОГДА РАЗНОСТЬДАТ(ГрафикПроизводства.НачалоЭтапа, &ДатаАктуальности, ДЕНЬ)
					КОГДА Этапы.Статус = &СтатусНачат
							И РАЗНОСТЬДАТ(ГрафикПроизводства.ОкончаниеЭтапа, &ДатаАктуальности, ДЕНЬ) > 0
						ТОГДА РАЗНОСТЬДАТ(ГрафикПроизводства.ОкончаниеЭтапа, &ДатаАктуальности, ДЕНЬ)
					КОГДА Этапы.Статус = &СтатусЗавершен
							И РАЗНОСТЬДАТ(ГрафикПроизводства.ОкончаниеЭтапа, Этапы.ФактическоеОкончаниеЭтапа, ДЕНЬ) > 0
						ТОГДА РАЗНОСТЬДАТ(ГрафикПроизводства.ОкончаниеЭтапа, Этапы.ФактическоеОкончаниеЭтапа, ДЕНЬ)
					ИНАЧЕ 0
				КОНЕЦ
		ИНАЧЕ 0
	КОНЕЦ КАК Задержка,
	ВЫБОР
		КОГДА &ПланируетсяГрафикПроизводства
			ТОГДА ВЫБОР
					КОГДА НЕ ГрафикПроизводства.ЭтапПроизводства ЕСТЬ NULL
							И (&ДатаАктуальности > ГрафикПроизводства.НачалоЭтапа
									И Этапы.Статус В (&СтатусФормируется, &СтатусСформирован, &СтатусКВыполнению)
								ИЛИ &ДатаАктуальности > ГрафикПроизводства.ОкончаниеЭтапа
									И Этапы.Статус <> &СтатусЗавершен)
						ТОГДА ИСТИНА
					ИНАЧЕ ЛОЖЬ
				КОНЕЦ
		ИНАЧЕ ВЫБОР
				КОГДА НЕ НормативныйГрафикПроизводства.ЭтапПроизводства ЕСТЬ NULL
						И (&ДатаАктуальности > ДОБАВИТЬКДАТЕ(Этапы.Распоряжение.НачатьНеРанее, СЕКУНДА, ЕСТЬNULL(НормативныйГрафикПроизводства.ДлительностьДоЗапуска, 0))
								И Этапы.Статус В (&СтатусФормируется, &СтатусСформирован, &СтатусКВыполнению)
							ИЛИ &ДатаАктуальности > ДОБАВИТЬКДАТЕ(Этапы.Распоряжение.НачатьНеРанее, СЕКУНДА, ЕСТЬNULL(НормативныйГрафикПроизводства.ДлительностьДоЗапуска + НормативныйГрафикПроизводства.Ресурсоемкость, 0))
								И Этапы.Статус <> &СтатусЗавершен)
					ТОГДА ИСТИНА
				ИНАЧЕ ЛОЖЬ
			КОНЕЦ
	КОНЕЦ КАК ЕстьОтставаниеОтГрафика,
	ВЫБОР
		КОГДА Этапы.РучноеРазмещениеВГрафике
			ТОГДА 1
		КОГДА &ПланируетсяГрафикПроизводства
				И НЕ ГрафикПроизводства.ЭтапПроизводства ЕСТЬ NULL
				И (ТИПЗНАЧЕНИЯ(Этапы.ПланироватьНеРанее) = ТИП(ДАТА)
						И Этапы.ПланироватьНеРанее <> ДАТАВРЕМЯ(1, 1, 1)
					ИЛИ ТИПЗНАЧЕНИЯ(Этапы.ПланироватьНеРанее) = ТИП(Документ.ЭтапПроизводства2_2)
						И Этапы.ПланироватьНеРанее <> ЗНАЧЕНИЕ(Документ.ЭтапПроизводства2_2.ПустаяСсылка))
			ТОГДА 0
		ИНАЧЕ -1
	КОНЕЦ КАК ДатаНачалаИндексКартинки,
	ВЫБОР
		КОГДА Этапы.РучноеРазмещениеВГрафике
			ТОГДА 1
		ИНАЧЕ -1
	КОНЕЦ КАК ДатаОкончанияИндексКартинки,
	Этапы.ЖелаемаяДатаОбеспечения КАК ЖелаемаяДатаОбеспечения,
	ВЫБОР
		КОГДА &ПланируетсяГрафикПроизводства
			ТОГДА ВЫБОР
					КОГДА Этапы.ЖелаемаяДатаОбеспечения <> ДАТАВРЕМЯ(1, 1, 1)
							И ЕСТЬNULL(ГрафикПроизводства.НачалоЭтапа, ДАТАВРЕМЯ(1, 1, 1)) <> ДАТАВРЕМЯ(1, 1, 1)
							И Этапы.ЖелаемаяДатаОбеспечения > ГрафикПроизводства.НачалоЭтапа
						ТОГДА ИСТИНА
					ИНАЧЕ ЛОЖЬ
				КОНЕЦ
		ИНАЧЕ ВЫБОР
				КОГДА Этапы.ЖелаемаяДатаОбеспечения <> ДАТАВРЕМЯ(1, 1, 1)
						И НЕ НормативныйГрафикПроизводства.ЭтапПроизводства ЕСТЬ NULL
						И Этапы.ЖелаемаяДатаОбеспечения > ДОБАВИТЬКДАТЕ(Этапы.Распоряжение.НачатьНеРанее, СЕКУНДА, ЕСТЬNULL(НормативныйГрафикПроизводства.ДлительностьДоЗапуска, 0))
					ТОГДА ИСТИНА
				ИНАЧЕ ЛОЖЬ
			КОНЕЦ
	КОНЕЦ КАК ЖелаемаяДатаОбеспеченияПросрочена,
	Этапы.Ссылка КАК Ссылка
ИЗ
	Документ.ЭтапПроизводства2_2 КАК Этапы
		ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.НормативныйГрафикЭтаповПроизводства КАК НормативныйГрафикПроизводства
		ПО Этапы.Ссылка = НормативныйГрафикПроизводства.ЭтапПроизводства
		ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.СостоянияЭтаповПроизводства КАК СостоянияЭтаповПроизводства
		ПО Этапы.Распоряжение = СостоянияЭтаповПроизводства.Распоряжение
			И Этапы.Ссылка = СостоянияЭтаповПроизводства.Этап
		{ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ГрафикЭтаповПроизводства2_2 КАК ГрафикПроизводства
		ПО Этапы.Распоряжение = ГрафикПроизводства.Распоряжение
			И Этапы.Ссылка = ГрафикПроизводства.ЭтапПроизводства
			И (ГрафикПроизводства.СтатусГрафика = &СтатусРабочийГрафик)}
ГДЕ
	НЕ Этапы.ПометкаУдаления
Показать


Суть проблемы. В динамическом списке есть настройка упорядочивания по полю "ДатаНачала". Но это поле вычисляемое, как следствие не индексируется. Если в БД огромное количество этапов (десятки миллионов), то со списком становится невозможно работать, т.к. любая прокрутка подвешивает пользователя не минуты. Хуже того, этот список имеет настройку автообновления, что приводит к тем же результатам.
Прикрепленные файлы:
sapervodichka; +1 Ответить
6. ivanov660 4325 24.05.22 08:35 Сейчас в теме
(5)
1. Проблема через две точки существенна только для составных типов.
2. Сортировка по расчетному полю не очень хорошо, но почему вы считаете что проблема в нем? Вы план запроса смотрели? Прикладывайте посмотрим дадим совет.
В базах бывают и миллиарды записей и вполне себе работает.
15. PerlAmutor 129 24.05.22 18:20 Сейчас в теме
(6)
2. Сортировка по расчетному полю не очень хорошо, но почему вы считаете что проблема в нем? Вы план запроса смотрели? Прикладывайте посмотрим дадим совет.

Этой проблеме уже не первый год, в 2019 году план запроса смотрел. Если немного разгружусь с работой, то выложу. У себя сделали просто - отключили настройку сортировки и запретили пользователям упорядочивать все поля в динамическом списке. После этого жалобы от пользователей ушли. А так постоянно приходилось чистить настройки в хранилище пользовательских настроек, т.к. в форму попасть было невозможно. Думаю, что такое решение лучше, чем городить регистр, пересчитывать все даты по каждому этапу, которых у одного заказа может быть десятки тысяч, делать новые роли, писать регламентные задания и т.д. и т.п.
16. ivanov660 4325 24.05.22 18:30 Сейчас в теме
(15)
1. Вы поставили вопрос - "как сделать чтобы сортировка работала быстро?" как требование, мы на него ответили.
2. Вариант с отключением возможности сортировки - это отличный вариант.
3. Смысла выкладывать план в текущей ситуации не вижу. Решение есть, вопрос закрыт. Возможно кому-то пригодится идея.

Резюмирую варианты решения (у них есть свои плюсы и минусы):
1. Оставляем как есть.
2. Отключаем возможность сортировки. Нет возможности - нет проблемы
3. Добавить регистр для хранения расчетных дат.
18. PerlAmutor 129 24.05.22 18:37 Сейчас в теме
(16) Вопрос я так не ставил ) Лишь спросил что с этим можно сделать. Была надежда, что запрос можно как-то переписать, чтобы эта сортировка выполнялась уже после TOP 40 например, а не ДО. Может решение с временными таблицами есть, которые можно использовать в динамических списках не так давно? Например отобрать туда результат, а потом уже сортировать? Или во временную таблицу попадет вся таблица, а не результат динамического списка?
20. ivanov660 4325 24.05.22 19:42 Сейчас в теме
(18)
Во временную таблицу попадет весь список. И это похоже будет очень большая таблица, что не очень здорово. Но потом сортировка будет идти веселее.
8. s22 19 24.05.22 09:44 Сейчас в теме
Двойная точка не проблема.
проблема составные типы.
19. PerlAmutor 129 24.05.22 18:38 Сейчас в теме
(8) Я и не говорил, что проблема в двойной точке как таковой. Просто это звоночки. Сегодня "Ссылка" это не составной тип, а завтра уже составной например, а путь от этого не изменится на форме.
9. s22 19 24.05.22 14:32 Сейчас в теме
(5)
Суть проблемы. В динамическом списке есть настройка упорядочивания по полю "ДатаНачала". Но это поле вычисляемое, как следствие не индексируется. Если в БД огромное количество этапов (десятки миллионов), то со списком становится невозможно работать, т.к. любая прокрутка подвешивает пользователя не минуты. Хуже того, этот список имеет настройку автообновления, что приводит к тем же результатам.


добавите реквизит в документ с индексацией?
11. ivanov660 4325 24.05.22 15:34 Сейчас в теме
(9) Каждое решение имеет свои плюсы и минусы.

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

Думаю, что самый быстрый вариант - создать таблицу-регистр сведений, где будут отражены все рассчитанные данные и выводить ее на экран. Но в замен мы будем вынуждены ее пересчитывать ее часть регламентом или при проведении каждого документа.
13. s22 19 24.05.22 17:58 Сейчас в теме
(11)
Думаю, что самый быстрый вариант - создать таблицу-регистр сведений, где будут отражены все рассчитанные данные и выводить ее на экран. Но в замен мы будем вынуждены ее пересчитывать ее часть регламентом или при проведении каждого документа.


тут динамический список с десятками миллионов записей, отборами и сортировкой по полю из регистра сведений.
не уверен, что план будет разумным со стороны планировщика
14. ivanov660 4325 24.05.22 18:13 Сейчас в теме
(13) Будет. Вы в своем утверждении смешали два понятия новый регистр и существующий запрос. Приведу пример для наглядности:
ВЫБРАТЬ
    Новый.Статус КАК Статус,
    Новый.Распоряжение КАК Распоряжение,
    Новый.Подразделение КАК ПодразделениеДиспетчер,
    Новый.НаименованиеЭтапа КАК НаименованиеЭтапа,
    Новый.ПроизводствоНаСтороне КАК ПроизводствоНаСтороне,
    Новый.Подразделение КАК Подразделение,
    Новый.Спецификация КАК Спецификация,
    Новый.ПартияПроизводства КАК ПартияПроизводства,
    Новый.ТребуетсяПланироватьГрафик КАК ТребуетсяПланироватьГрафик,
    Новый.ТребуетсяОбеспечение КАК ТребуетсяОбеспечение,
    //....
    Новый.ДатаНачала КАК ДатаНачала,
    Новый.ДатаЗавершения КАК ДатаЗавершения,
    //....
ИЗ
    РегистрСведений.НовыйРегистр как Новый
Показать
21. s22 19 26.05.22 10:29 Сейчас в теме
(14)
(13) Будет. Вы в своем утверждении смешали два понятия новый регистр и существующий запрос. Приведу пример для наглядности:


Понял. В такой постановке я был не прав. Спасибо за разъяснение
7. ivanov660 4325 24.05.22 08:38 Сейчас в теме
Было бы интересно услышать мнение вендора. А проблемных мест в топовой конфигурации и ее сателлитах еще достаточно.
Gilev.Vyacheslav; +1 Ответить
10. Dach 371 24.05.22 14:45 Сейчас в теме
(0) О, эта дурацкая форма "Мои задачи", тоже с ней намучался. Она тормозит во многих конфигурациях и не только на Pg, но и на Ms sql
unknown181538; Дмитрий74Чел; +2 Ответить
12. ivanov660 4325 24.05.22 15:37 Сейчас в теме
(10) Да, в ней RSL серьезно кривит и размер таблицы тоже быстро становиться большим. Наверное самый оптимальный вариант - это выборочно включить для данного объекта новый механизм ограничения прав доступа.
Gilev.Vyacheslav; +1 Ответить
17. PerlAmutor 129 24.05.22 18:31 Сейчас в теме
Вот сегодняшний кейс, если кому интересно. В документах начал долго выполняться подбор Назначений.
У нас этот справочник огромный за счет количества этапов производства, которые прописываются в назначения.

На скрине проблемное место (Справочник.Назначения.МодульМенеджера.ЗаполнитьДанныеВыбора()). Идет поиск подстроки по шаблону "%%", т.е. подстрока никогда не попадет в индекс. Пришлось поменять на:

Запрос.УстановитьПараметр("СтрокаПоиска", Параметры.СтрокаПоиска + "%");


По началу строки ищется миллисекунды, а не минуты.
Прикрепленные файлы:
Drivingblind; Dach; ivanov660; +3 Ответить
22. Jimbo 9 26.05.22 20:27 Сейчас в теме
Интересно и познавательно как всегда у автора
23. m1_1976 13 30.05.22 16:32 Сейчас в теме
Даже если процентов 10 из выложеннного автором понять, разобрать и внедрить у себя. Уже низкий поклон ему.
Спасибо большое.
24. ivanov660 4325 30.05.22 16:57 Сейчас в теме
(23)
1. Пожалуйста)
2. В примерах указаны точки возникновения и вариант исправления, т.ч. если у вас такая ошибка присутствует, то можете исправить согласно рекомендации. Может разработчики компании 1С ERP конфигурации снизойдут до нас и поправят в последних версиях.
3. Осознать написанное, конечно, это лучше всего. Поэтому рекомендую сначала ознакомиться с тремя предыдущими статьями (в порядке следования):
- Распространенные ошибки разработчиков, приводящие к проблемам производительности
- Смотрим запросы 1С через Microsoft SQL Profiler по следам ошибок разработчиков, приводящих к проблемам производительности
- Пример пошагового решения проблемы производительности на базе Postgres SQL с картинками

и другие статьи на текущем ресурсе.
25. Serg O. 224 01.06.22 08:51 Сейчас в теме
Спасибо за статью, как всегда очень круто, масса примеров и объяснений, прямо пример для всех.
Оставьте свое сообщение