Parameter sniffing и генерация планов для разработчиков 1С

01.06.21

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

Особенности генерации планов запросов. Статья написана по мотивам вебинара Виктора Богачева.

Введение

В документации находим определение термина: Parameter sensitivity, also known as "parameter sniffing", refers to a process whereby SQL Server "sniffs" the current parameter values during compilation or recompilation, and passes it along to the Query Optimizer so that they can be used to generate potentially more efficient query execution plans.

Генерация SQL сервером плана выполнения запроса требует ресурсов. Поэтому при первом выполнении запроса, план сохраняется и используется для одинаковых запросов повторно. Параметры запроса не сохраняются, однажды составленный план применяется независимо от параметров. Но при неравномерном распределении данных, выбор оптимального плана запроса может зависеть от параметров (чувствительность к параметрам, parameter sensitivity).

Рассмотрим практический пример

непериодический регистр сведений «НеравномерноеРаспределениеДанных»  (InfoRg48221)
незримо присутствует разделитель учета (Fld11560)
первое измерение «Счетчик» (Fld48223)
второе измерение «Организация», индексировано (Fld48222RRef)
ресурс – «Значение» (Fld48224)
Имена полей СУБД получены функцией ПолучитьСтруктуруДанных().

В данном случае, с регистром связано два индекса:

(1) Кластерный в порядке измерений регистра, который содержит все поля, в том числе значения ресурсов регистра (Fld48224).

(2) Индекс по организации не содержит значения ресурсов (Fld48224). Для запросов по ресурсам индекс  будет заведомо непокрывающим, поскольку не содержит всех необходимых полей.

Внесем в регистр 99.9% записей ООО «Ромашка», 0.1% записей ООО «Рога и копыта»

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
НаборСчетчикиВыгрузок = РегистрыСведений.НеравномерноеРаспределениеДанных.СоздатьНаборЗаписей();

Для Счетчик = 0 По 100000 Цикл

	ЗаписьСчетчикиВыгрузок = НаборСчетчикиВыгрузок.Добавить();

	Если цел(Счетчик/1000) = Счетчик/1000 Тогда
		ЗаписьСчетчикиВыгрузок.Организация = Организация1;
	Иначе
		ЗаписьСчетчикиВыгрузок.Организация = Организация99;
	КонецЕсли;

	ЗаписьСчетчикиВыгрузок.Счетчик = Счетчик;
	ЗаписьСчетчикиВыгрузок.Значение = 100000-Счетчик;

КонецЦикла;

НаборСчетчикиВыгрузок.Записать();
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

В некоторых запросах значения ресурсов по ООО «Ромашка» (99.9%) выгоднее получить - просмотреть всю таблицу, использовать кластерный индекс (1) (Clustered index scan). Значения ресурсов по ООО «Рога и копыта» (0.1%) выгоднее получить - выбрать по индексу (2) значения организации и присоединить значения ресурсов (Index seek + Key Lookup).

Но в обоих случаях будет использоваться план, помещенный в кеш при первом выполнении, который соответствует только одному из вариантов.

Просматривать выполненные запросы на сервере СУБД будем с помощью динамических представлений (3).

-----------------------------------------------------------------------------------------------
SELECT
eqs.last_execution_time,
eqs.creation_time,
eqs.sql_handle,
eqs.plan_handle,
eqp.query_plan,
est.text
FROM sys.dm_exec_query_stats AS eqs
CROSS APPLY sys.dm_exec_sql_text(eqs.sql_handle) AS est
CROSS APPLY sys.dm_exec_query_plan(eqs.plan_handle) AS eqp
WHERE eqs. execution _time > '2021-26-05 13:40:00'
AND est.text like '%Rg48221%'
ORDER BY eqs.last_execution_time
-----------------------------------------------------------------------------------------------

Используем ограничение по времени выполнения и имени таблицы. Подробности в документации

Выполним запрос по ООО «Рога и копыта» (0.1%)

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

"ВЫБРАТЬ
|	НеравномерноеРаспределениеДанных.Значение КАК Значение,
|	НеравномерноеРаспределениеДанных.Организация КАК Организация
|ИЗ
|	РегистрСведений.НеравномерноеРаспределениеДанных КАК НеравномерноеРаспределениеДанных
|ГДЕ
|	НеравномерноеРаспределениеДанных.Организация = &Организация";

Запрос.УстановитьПараметр("Организация", Организация);
РезультатЗапроса = Запрос.Выполнить();
ВыборкаДетальная = РезультатЗапроса.Выбрать();

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

Смотрим СУБД. Обратите внимание, информация в плане содержит только предполагаемые значения. С помощью dmv можно получить данные предварительного плана запроса. Данные из фактического плана можно увидеть например в трассировке.

Поиск в некластерном индексе (2), список столбцов совпадает с колонками индекса (2). Предполагаемое количество строк совпадает с фактическим (по способу заполнения).

Поиск по ключу в кластерном индексе (1), список столбцов содержит только значение ресурсов Fld48224.

creation_time            2021-05-27 11:22:35.810
last_execution_time   2021-05-27 11:22:35.810
sql_handle                0x02000000BC7EF3109836D5B073B04A84CBF3C7B5E3CA55F2
plan_handle              0x06001700BC7EF310404163F2050000000000000000000000

После выполнения запроса по ООО «Рога и Копыта» (0.1%) и заполнения кеша планов, выполним запрос по ООО «Ромашка» (99.9%). В dmv-запросе (3) добавляется строка, изменяется время последнего выполнения,  визуальный план запроса не изменился.

creation_time            2021-05-27 11:22:35.810
last_execution_time   2021-05-27 11:58:29.600
sql_handle                0x02000000BC7EF3109836D5B073B04A84CBF3C7B5E3CA55F2
plan_handle              0x06001700BC7EF310404163F2050000000000000000000000

Очистим кеш планов командой с параметром plan_handle

DBCC FREEPROCCACHE(0x06001700BC7EF310404163F2050000000000000000000000)

Результат dmv-запроса (3) становится пустым. После очистки кешированных планов запросов, выполним запрос по ООО «Ромашка» (99.9%), sql_handle без изменения, время создания и время исполнения совпадают между собой, plan_handle изменился.

creation_time            2021-05-27 12:07:13.597
last_execution_time   2021-05-27 12:07:13.597
sql_handle                0x02000000BC7EF3109836D5B073B04A84CBF3C7B5E3CA55F2
plan_handle              0x06001700BC7EF3104041A0982F0000000000000000000000

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

Обратите внимание, в среде выполнения запросов (3), выше плана запроса есть ремарка:

/*
Отсутствуют сведения об индексе из ExecutionPlan5.sqlplan
Обработчик запросов считает, что реализация следующего индекса может сократить стоимость запроса на 73.7991%.
*/
/*
USE [***********]
GO
CREATE NONCLUSTERED INDEX [<Name of Missing Index, sysname,>]
ON [dbo].[_InfoRg48221] ([_Fld48222RRef],[_Fld11560])
INCLUDE ([_Fld48224])
GO
*/

Средствами 1С не удастся создать индекс, который включает разделитель учета (Fld11560), организацию (Fld48222RRef) и значение (Fld48224). Ремарка появилась, поскольку произошло сканирование кластерного индекса (1) (Cluster Index Seek с предикатом – разделитель учета выбирает все записи). Для ООО "Ромашка" (99.9%), это оправданно, поскольку необходимы почти все записи регистра, а полей в регистре немного. Для ООО "Рога и Копыта" (0.1%) это приведет к избыточному чтению.

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

Что делают разработчики СУБД?

Стандартные решения:

  •     добавить хинт/опцию в текст запроса Optimize @Pxx for unknown (использовать Plan Guide)
  •     отключить Parameter sniffing флагом 4136 или в настройках MS SQL 2016 и позже
  •     использовать в запросе локальные переменные
  •     использовать динамический текст запроса

упоминание о проблемах parameter sniffing можно встретить часто в вопросах и ответах специалистов.

 
 инструменты Plan Guide (Структуры планов) подставляют  хинт по маске запроса.

Что доступно разработчикам 1С?

Если поле типа булево, то зачастую данные по нему распределены неравномерно. Например, флажок «Особенный товар» отсутствует у большинства товаров. Любопытно, что параметры запроса типа булево включаются в текст запроса на уровне платформы, один запрос 1С соответствует разным запросам СУБД.

Мы можем пойти по тому же пути, изменять текст запроса, чтобы СУБД выбирала для него уникальный план. Выполним код 1С:

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| НеравномерноеРаспределениеДанных.Счетчик КАК Счетчик,
|&МеткаЗапроса
|ИЗ
|РегистрСведений. НеравномерноеРаспределениеДанных КАК НеравномерноеРаспределениеДанных
|ГДЕ
| НеравномерноеРаспределениеДанных.Организация = &Организация";

Запрос.Текст = СтрЗаменить(Запрос.Текст, "&МеткаЗапроса", "Истина, Ложь, ""abc"", 123");
Запрос.УстановитьПараметр("Организация", Организация);
РезультатЗапроса = Запрос.Выполнить();

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

Получим запрос в СУБД:

-----------------------------------------------------------------------------------------------
(@P1 nvarchar(4000),@P2 numeric(10),@P3 numeric(10),@P4 varbinary(16))SELECT T1._Fld48223, 0x01, 0x00, @P1, @P2 FROM dbo._InfoRg48221 T1 WHERE ((T1._Fld11560 = @P3)) AND ((T1._Fld48222RRef = @P4))
-----------------------------------------------------------------------------------------------

Этот пример показывает, что использование в качестве метки запроса числа или строки не приносит желаемого результата: они переходят в параметры. Поведение компилятора может изменяться, здесь платформа 1С:Предприятие 8.3 (8.3.17.1851). Для каждой организации метка запроса должна содержать уникальную последовательность логических значений, тогда запросы будут уникальны по каждой организации.

И еще одна близкая тема, связанная с генерацией планов, на которую есть намек на ИТС.

Не использовать в запросе функции от параметров.

Выполним код 1С в разные моменты времени

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

Запрос = Новый Запрос ("ВЫБРАТЬ МИНИМУМ(&Период) КАК Период");

Запрос.УстановитьПараметр( "Период", ТекущаяДата() );

РезультатЗапроса = Запрос.Выполнить();

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

Как Вы догадались, на стороне СУБД запросы разные.

-----------------------------------------------------------------------------------------------
sql_handle 0x020000005B8E5C00E3A31CE9CFD7C5776CCC32A71AA1E71D
text SELECT MIN({ts '4021-05-20 11:42:01'})


sql_handle 0x0200000054E6421B88E9D5DD77378ECB0B09EDD9C62AAECE
text SELECT MIN({ts '4021-05-20 11:42:09'})
-----------------------------------------------------------------------------------------------

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

Если запрос содержит условие вхождения в список, например "ГДЕ НеравномерноеРаспределениеДанных.Организация В ( &ИзбранныеОрганизации )" и элементов списка меньше одной тысячи, то компилятор 1С преобразует список в логическое выражение, вставит в текст запроса все элементы списка через ИЛИ. Для разной длины списка, оптимизатор SQL Server генерирует разные планы запроса.

Заключение

Мне было интересно написать эту статью - посмотрим, насколько Вам будет интересно ее прочитать. Всем удачи !

 
 P.S. Разрешение от Виктора Богачева на публикацию прилагается.

 

См. также

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

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

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

13.03.2024    3563    spyke    28    

47

Быстродействие типовой 1С

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

Оказывается, в типовых конфигурациях 1С есть, что улучшить!

13.03.2024    5515    vasilev2015    19    

38

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

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

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

1 стартмани

15.02.2024    8213    167    ZAOSTG    71    

101

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

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

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

09.01.2024    6503    doom2good    48    

64

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

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

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

20.11.2023    9327    ivanov660    6    

76

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

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

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

15.11.2023    5324    a.doroshkevich    20    

72

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

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

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

11.10.2023    16555    skovpin_sa    14    

101
Комментарии
Подписаться на ответы Сортировка: Древо развёрнутое
Свернуть все
1. ildary 21 02.06.21 14:16 Сейчас в теме
Читать было очень интересно, ждём новых статей!
8. vasilev2015 2696 03.06.21 13:44 Сейчас в теме
(1) Спасибо, буду стараться.
2. PerlAmutor 129 03.06.21 06:22 Сейчас в теме
Спасибо за статью. Есть пара замечаний.

В заголовке блока:
Что делают разработчики СУБД?

Видимо имелись ввиду все-таки разработчики 1С?

В блоке:
Что доступно разработчикам 1С?

Как-то странно подан материал. Написано "Мы можем пойти по тому же пути, изменять текст запроса, чтобы СУБД выбирала для него уникальный план"... а затем тут же:

"Этот пример показывает, что использование в качестве метки запроса числа или строки не приносит желаемого результата: они переходят в параметры." и "Для каждой организации метка запроса должна содержать уникальную последовательность логических значений, тогда запросы будут уникальны по каждой организации.". При этом читатель видит комбинацию "SELECT T1._Fld48223, 0x01, 0x00" и понимает, что на этом месте должно быть "булево", а тут число, а число вроде как должно было перейти в параметры. Хорошо бы разделить пример на правильный и неправильный запрос и внести ремарку того, что на стороне СУБД в запросах нет типа Булево в том виде, в котором оно есть в запросах 1С.

Еще вот так вроде бы уже почти никто не пишет:

Запрос.УстановитьПараметр( "Период", ТекущаяДата() );

ТекущаяДатаСеанса() напрашивается. Хотя с точки зрения логики система ведет остатки в разрезе своего часового пояса, а не сеанса. Возможно в этом случае это оправдано.
grumagargler; +1 Ответить
5. vasilev2015 2696 03.06.21 12:44 Сейчас в теме
(2) Спасибо на добром слове.

Мне представлялось, что разработчикам 1С достаточно изменять текст запроса, не нужно пользоваться хинтом "Optimize" и прочее, поэтому отделил разработчиков СУБД. Возможно в тексте есть шероховатости, поэтому спасибо за уточняющий комментарий, с ним будет понятнее.
3. user662404_itlexusss 28 03.06.21 06:46 Сейчас в теме
Для разных планов надо использовать скобки. Их платформа транслирует в скл не изменяя, получая разные планы. Вроде бы баян, но в статье не написано.
6. vasilev2015 2696 03.06.21 12:44 Сейчас в теме
(3) Хорошая идея, спасибо.
11. tolyan_ekb 104 04.06.21 10:08 Сейчас в теме
(3) приведите, пожалуйста, пример
4. sisdrou 23 03.06.21 12:28 Сейчас в теме
Очень большая проблема 1С на сегодняшний день - это ее все большее внедрение в системы с высокой нагрузкой, где нужны очень узкие настройки СУБД и отсутствие такого инструментария у 1С. Все эти моменты они оставляют на свое усмотрение. И когда мы смело добавляли индексы и вносили правки в СУБД, прекрасно понимая, что это нарушения соглашений 1С и прописанной методологии. Что нам остается ? А эта статья замечательная, но возможно она актуальна только до следующей платформы ). Пора уже дать возможность более гибкой настройки объектов метаданных и их поведению при построении запросов.
7. vasilev2015 2696 03.06.21 12:46 Сейчас в теме
9. Yashazz 4722 03.06.21 17:14 Сейчас в теме
А можно ссылку на сам вебинар Богачёва?
10. vasilev2015 2696 03.06.21 17:23 Сейчас в теме
(9) Здравствуйте, Яков !

Этой части вебинара нет в свободном доступе, статью написал по памяти.

Предыдущие статьи писал на основе опубликованных на youtube частей вебинара.
12. пользователь 05.06.21 14:52
Сообщение было скрыто модератором.
...
13. СергейК 51 05.06.21 20:51 Сейчас в теме
Спасибо за статью!
Вот только что писал запрос, понадобилось в сравнении параметр привести к началу дня. Подумал, лучше же конечно параметр в запросе один раз привести, так же легче будет СУБД обрабатывать, чем применять функцию к меняющемуся полю. Теперь получается что у меня каждый раз СУБД будет думать что это новый запрос, и рассчитывать план (( . Придется передавать ещё один параметр, приведенный к началу дня )). Век живи век учись.

Не знаете, на Postgres поведение похожее?
14. vasilev2015 2696 06.06.21 09:41 Сейчас в теме
(13) Здравствуйте, Сергей !

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

Когда я писал статью, функция "ПолучитьНачалоПериода" в запросе обработала корректно,
поэтому я использовал функцию "Минимум". Но поведение платформы может отличаться.
Передавать в запрос обработанный параметр выглядит надежнее.
15. Hatson 529 25.06.21 11:57 Сейчас в теме
Не используйте запросы, ведь они тратят ваше время, а жизнь-то проходит.

Производительность более не важна
16. vasilev2015 2696 12.07.21 21:05 Сейчас в теме
(15) Есть разные уровни развития бизнеса.
Чебуречная (уставный капитал 10 тысяч) - пицца Додо (250 разработчиков).
С определенного уровня производительность имеет значение.
Выбирайте своего работодателя, ориентируйтесь на его потребности.
17. life-wayfarer 09.10.21 13:05 Сейчас в теме
"отключить Parameter sniffing флагом 4136 или в настройках MS SQL 2016 и позже" это не решаете проблемы Parameter sniffing.
Оставьте свое сообщение