| Типичные причины неоптимальной работы запросов и методы оптимизации в 1С |
|
Значительная часть проблем, приводящих к не оптимальной работе запросов, может быть обнаружена путем анализа кода конфигурации и структуры метаданных. Имеется перечень типичных ошибок в коде и структуре данных, последствия которых достаточно хорошо изучены и легко предсказуемы. Анализ кода с использованием этого перечня позволяет решить большую часть проблем с производительностью запросов, не углубляясь в детальную техническую информацию (текст запроса на языке SQL, план запроса и т.д.). Основные причины не оптимальной работы запросов, диагностируемые на уровне кода конфигурации и структуры метаданных:
В настоящей статье рассматриваются перечисленные причины неоптимальной работы запросов и даются рекомендации по их оптимизации. Cоединения с подзапросамиРекомендацииПри написании запросов не следует использовать соединения с подзапросами. Следует соединять друг с другом только объекты метаданных или временные таблицы. Если запрос использует соединения с подзапросами, то его следует переписать с использованием временных таблиц. Если запрос содержит соединения с подзапросами, то это может привести к следующим негативным последствиям:
Пример потенциально опасного запроса, использующего соединение с подзапросом: ВЫБРАТЬ ... В данном примере в правой части соединения используется подзапрос, а не объект метаданных. Обратите внимание на то, что в какой части соединения (правой или левой) используется подзапрос - не важно. Точно так же не важно, какого типа соединение указано (ЛЕВОЕ, ПРАВОЕ и т.д.). Во всех случаях такая конструкция является потенциально опасной и должна быть исправлена при помощи временных таблиц. Обратите внимание на то, что возможность использования временных таблиц появилась в 1С:Предприятии начиная с версии 8.1. Если вы используете версию 8.0, то для решения проблемы производительности такого запроса следует перейти на 8.1. Для оптимизации запроса следует разбить его на несколько отдельных запросов (по числу подзапросов, используемых в соединениях). Эти запросы рекомендуется поместить в один пакетный запрос. Внимание! Не забудьте проиндексировать созданную временную таблицу. В качестве индексных полей следует указать все поля, которые используются в условии соединения. Для вышеприведенного примера получится следующий пакетный запрос: // Создать менеджер временных таблиц ПоясненияВо встроенном языке запросов 1С:Предприятия версии 8.0 отсутствовала возможность использовать временные таблицы и писать пакетные запросы. При этом часто было необходимо выполнять сложные вычисления в рамках одного запроса (то есть, одного цикла взаимодействия клиент - сервер 1С:Предприятия - сервер СУБД). Для решения таких задач использовались подзапросы - обращения не к объектам метаданных, а к выборкам из этих объектов. Как правило, подзапросы выполнялись с группировкой и часто использовались в соедениниях. Оптимизатор сервера СУБД (независимо от того, какую СУБД вы используете) не всегда может правильно оптимизировать подобный запрос. В данном случае, проблемой для оптимизатора является выбор правильного способа соединения. Существуют несколько алгоритмов соединения двух выборок. Выбор того или иного алгоритма зависит от того, сколько записей будет содержаться в одной и в другой выборке. В том случае, если вы соединяете две физические таблицы, СУБД может легко определить объем обоих выборок на основании имеющейся статистики. Если же одна из соединямых выборок представляет собой подзапрос, то понять, какое количество записей она вернет, становится очень сложно. В этом случае СУБД может ошибиться с выбором плана, что приведет к катастрофическому падению производительности запроса. Переписывание запроса по приведенной выше методике имеет своей целью упростить работу оптимизатору СУБД. В переписанном запросе все выборки, участвующие в соединениях будут представлять собой физические таблицы, и СУБД сможет легко определить размер каждой выборки. Это позволит СУБД гарантированно выбрать самый быстрый из всех возможных планов. Причем, СУБД будет делать правильный выбор независимо ни от каких условий. Переписанный подобным образом запрос будет работать одинаково хорошо на любых СУБД, что особенно важно при разработке тиражных решений. Кроме того, переписанный подобным образом запрос лучше читается, проще для понимания и отладки. Следует понимать, что переписав запрос таким образом, мы, возможно, внесли в него некоторое замедление за счет дополнительных накладных расходов - создания временных таблиц. Если СУБД не ошибется с выбором плана, то она, возможно, выполнит старый запрос быстрее, чем новый. Однако, это замедление всегда будет крайне незначительным. Размер замедления зависит от используемой СУБД и производительности оборудования. В типичном случае на создание одной временной таблицы может уйти несколько миллисекунд. То есть, эти замедления не могут оказать заметного влияния на производительность системы и как правило ими можно пренебречь. Cоединения с виртуальными таблицамиРекомендацииЕсли в запросе используется соединение с виртуальной таблицей языка запросов 1С:Предприятия (например, "РегистрНакопления.Товары.Остатки()") и запрос работает с неудовлетворительной производительностью, то рекомендуется вынести обращение к виртуальной таблице в отдельный запрос с сохранением результатов во временной таблице. То есть, следует использовать ту же рекомендацию, что и в случае соединения с подзапросом. ПоясненияВиртуальные таблицы, используемые в языке запросов 1С:Предприятия, могут разворачиваться в подзапросы при трансляции в язык SQL. Это связано с тем, что виртуальная таблица часто (но не всегда) получает данные из нескольких физических таблиц СУБД. Если вы используете соединение с виртуальной таблицей, то на уровне SQL оно может быть в некоторых случаях реализовано, как соединение с подзапросом. В этом случае оптимизатор СУБД может точно так же выбрать неоптимальный план, как при работе с подзапросом, использованным в языке 1С:Предприятия в явном виде. Несоответствие индексов и условий запросаРекомендацииУбедитесь в том, что для всех условий, использованных в запросе, имеются подходящие индексы. Условия используются в следующих секциях запроса:
Для каждого условия должен существовать подходящий индекс. Подходящим является индекс, удовлетворяющий следующим требованиям:
При создании объекта метаданных 1С:Предприятие автоматически создает индексы, которые должны подходить для работы большинства запросов. Основные идексы, создаваемые 1С:Предприятием:
Детальная информация по индексам, автоматически создаваемым 1С:Предприятием содержится в статье «Индексы таблиц базы данных 1С:Предприятия 8.1». В тех случаях, когда автоматически созданных индексов недостаточно, можно дополнительно проиндексировать реквизиты объекта метаданных. Информация о том, какие индексы при этом будут созданы, содержится в этой же статье. Следует иметь в виду, что создание индекса ускоряет процесс поиска информации, но может несколько замедлить процесс ее изменения (добавления, редактирования и удаления). Поэтому индексы следует создавать осознанно и только в том случае, если точно известен запрос, для которого такой индекс необходим. Не следует создавать индексы "на всякий случай" или заведомо избыточные индексы. Например никогда не следует дополнительно индексировать первое измерение регистра, поскольку для поиска по значению первого измерения подходит основной индекс таблицы итогов, который автоматически создаст платформа. Пояснения Если в структуре базы данных отсутствует индекс, удовлетворяющий всем перечисленным условиям, то для получения результата СУБД будет вынуждена сканировать таблицу или один из ее индексов. Это приведет к увеличению времени выполнения запроса, а так же к возможному снижению параллельности системы, поскольку возрастет количество установленных блокировок. Требования к индексу, перечисленные в рекомендациях, связаны с физической структурой индекса в СУБД. Эта структура представляет собой дерево значений проиндексированных полей. На первом уровне дерева находятся значения первого поля индекса, на втором - второго и так далее. Такая структура позволяет достичь высокой эффективности при поиске по индексу. Кроме того, она гарантирует отсутствие деградации производительности индекса с ростом количества данных. Однако, индекс такой структуры, очевидно, может быть использован только строго определенным образом. Сначала необходимо провести поиск по значению первого поля индекса, затем - второго и так далее. Если, например, условие по первому полю индекса не указано, то индекс уже не сможет обеспечить быстрый поиск. Если указано условие по нескольким первым полям индекса, а затем одно или несколько полей индекса не задано, то индекс может быть использован только частично. ПримерыВ конфигурации описан регистр накопления ТоварыНаСкладах:
Платформа 1С:Предприятие автоматически создаст для таблицы остатков данного регистра индекс по периоду и всем измерениям в том порядке, в котором они перечислены в конфигураторе. Рассмотрим несколько примеров запросов и проанализируем, смогут ли они оптимально выполняться при такой структуре данных. Запрос 1Запрос.Текст = "ВЫБРАТЬ В данном случае нарушено требование 2. В условии отсутствует отбор по первому полю индекса (Склад). Такой запрос не сможет выполниться оптимально. Для его выполнения серверу СУБД придется перебирать (сканировать) все записи таблицы. Время выполнения этой операции напрямую зависит от количества записей в таблице остатков регистра и может быть очень большим (и будет увеличиваться с ростом количества данных). Варианты оптимизации:
Запрос 2Запрос.Текст = "ВЫБРАТЬ В данном случае нарушено требование 3. Между измерениями «Склад» и «Качество» в структуре регистра находится измерение «Номенклатура», которое не задано в условии запроса. Этот запрос так же не сможет выполняться оптимально. При его выполнении СУБД выполнит поиск по первому полю индекса, но затем вынужденно просканирует некоторую его часть. Сканирование приведет к увеличению времени выполнения запроса и к блокировке избыточных записей в таблице, то есть к снижению общей пропускной способности системы. Варианты оптимизации:
Запрос 3Запрос.Текст = "ВЫБРАТЬ В этом случае требования соответствия индекса и запроса не нарушены. Данный запрос будет выполнен СУБД оптимальным способом. Обратите внимание на то, что порядок следования условий в запросе не обязан совпадать с порядком следования полей в индексе. Это не является проблемой и будет нормально обработано СУБД. Использование подзапросов в условии соединенияРекомендацииНе следует использовать подзапросы в условии соединения. Это может привести к значительному замедлению запроса и (в отдельных случаях) к его полной неработоспособности на некоторых СУБД. Пример запроса с использованием подзапроса в условии соединения: Запрос.Текст = "ВЫБРАТЬ В данном случае подзапрос в условии соединения используется для получения как бы "среза последних" на конец предыдущего периода. Причем, для каждой номенклатуры период может быть разным. Подобный запрос рекомендуется переписать с использованием временных таблиц. Например, это можно сделать следующим образом: Запрос.Текст = " Получение данных через точку от полей составного типаРекомендацииЕсли в запросе используется получение значения через точку от поля составного ссылочного типа, то при выполнении этого запроса будет выполняться соединение со всеми таблицами объектов, входящими в этот составной тип. В результате SQL текст запроса чрезвычайно усложняется, и при его выполнении оптимизатор СУБД может выбрать неоптимальный план. Это может привести к серьезным проблемам производительности и даже к неработоспособности запроса в отдельных случаях. В частности, не рекомендуется обращаться к реквизитам регистратора регистра (например, "ТоварыНаСкладах.Регистратор.Дата") и т.п. При этом не важно в какой части запроса вы используете реквизит, полученный через точку от поля составного типа - в списке возвращаемых полей, в условии и т.п. Во всех случаях такое обращение может привести к проблемам производительности. Общая рекомендация заключается в том, чтобы по возможности ограничить количество соединений в таких запросах. Для этого можно использовать следующие приемы:
ПримерВ данном запросе используется обращение к реквизитам регистратора. Регистратор является полем составного типа, которое может принимать значения ссылки на один из 56 видов документов. Запрос.Текст = "ВЫБРАТЬ SQL-текст этого запроса будет включать 56 левых соединений с таблицами документов. Это может привести к серьезным проблемам производительности при выполнении запроса. Однако, для решения данной конкретной задачи нет необходимости соединяться со всеми 56 видами документов. Условия запроса таковы, что при его выполнении будут выбраны только движения документов "РеализацияТоваровУслуг" и "ЗаказыПокупателя". В этом случае мы можем значительно ускорить работу запроса, ограничив количество соединений при помощи функции ВЫРАЗИТЬ(). Запрос.Текст = "ВЫБРАТЬ Этот запрос является более громоздким и, возможно, менее универсальным (он не будет правильно работать для других ситуаций - когда возможны другие значения типов регистратора). Однако, при его выполнении будет сформирован SQL запрос, который будет содержать всего два соединения с таблицами документов. Такой запрос будет работать значительно быстрее и стабильнее, чем запрос в его первоначальном виде. Фильтрация виртуальных таблиц без использования параметровПри использовании виртуальных таблиц в запросах, следует передавать в параметры таблиц все условия, относящиеся к данной виртуальной таблице. Не рекомендуется фильтровать виртуальные таблицы при помощи условий в секции ГДЕ и т.п. Такой запрос будет возвращать правильный (с точки зрения функциональности) результат, но СУБД будет намного сложнее выбрать оптимальный план для его выполнения. В некоторых случаях это может привести к ошибкам оптимизатора СУБД и значительному замедлению работы запроса. Например, следующий запрос использует секцию ГДЕ запроса для выборки из виртуальной таблицы. Запрос.Текст = "ВЫБРАТЬ Возможно, что в результате выполнения этого запроса сначала будут выбраны все записи виртуальной таблицы, а затем из них будет отобрана часть, соответствующая заданному условию. Было бы оптимальным вариантом ограничивать количество выбираемых записей на самом раннем этапе обработки запроса. Для этого следует передать условия в параметры виртуальной таблицы. Запрос.Текст = "ВЫБРАТЬ
|
Типичные причины неоптимальной работы запросов и методы оптимизации в 1С
Комментарии
Самое страшное, что это лжезнание и лженаука.
Доверяй, но проверяй! Не стоит всё, что здесь написано, сразу брать за Аксиому. Было бы неплохо в статье отобразить для указанных примеров данные о производительно сти "правильных" и "не правильных" вариантов запросов.
Синтетический пример:
ВЫБРАТЬ
НН.Номер КАК НомерНН,
ПТУ.Номер КАК НомерПТУ,
РТУ.Номер КАК НомерРТУ
ИЗ
Документ.НалоговаяНаклад ная КАК НН
ЛЕВОЕ СОЕДИНЕНИЕ Документ.ПоступлениеТова ровУслуг КАК ПТУ
ПО НН.Сделка = ПТУ.Ссылка
ЛЕВОЕ СОЕДИНЕНИЕ Документ.РеализацияТовар овУслуг КАК РТУ
ПО НН.Сделка = РТУ.Ссылка
поле "сделка" НН не индексировано. /quote]
Забавно, что пример к статье никакого отношения не имеет. В примере соединение трех реальных таблиц. В статье на этот случай, по-моему, никаких рекомендаций по применению временных таблиц и близко не было.
В 1С тоже наверное не дураки что б придумывать то что медленее работает. Возможно вы чего-то не то делаете
Во всех случаях такая конструкция является потенциально опасной и должна быть исправлена при помощи временных таблиц.
никаких аргументов за.
Производительно сть их на СУБД никто похоже не мерил.
Никому ни пришло в голову что временные таблы сервер пишет на диск. на это уходит времени на порядки больше чем бы выполнилось обычное соединение с подзапросом.(проверял на довольно шустрой дисковой подсистеме много рейдSSD)
Реально, на всех возможных задачах протестировал где у меня были подзапросы попробовал сделать времянки, производительно сть падает в разы!!!
Дорогие читатели этого мануала, внимательно смотрите на то что вы делаете, внимательно следите за промежуточными результатами подзапросов и как вы их соединяете, да прибудет вам счастье. Про Пакеты забудьте как класс, бредятина жуткая!
Как SQL Server-ом строится статистика можно прочитать например здесь:
http://www.sql.ru/articles/mssql/2005/081301StatisticsInSQLServer2005.shtml
По поводу индекса. Конечно ни одна СУБД "самовольно" не строит индексы. Для этого есть программисты и администраторы. В конфигураторе 1С в свойствах реквизита необходимо установить свойство "индексировать" - при этом будет построен индекс.
Вопрос - как найти поля которые надо индексировать.
Способ 1: Пользователи жалуются на медленную работу обработки/отчета - делаем замер производительно сти, находим проблемный запрос, анализируем объединение и строим индекс.
Способ 2: Вообще не разговариваем с пользователями и не запускаем конфигуратор, а смотрим вот эту штуку:
http://msdn.microsoft.com/ru-ru/library/cc280469.aspx
Как именуются объекты 1С в таблицах БД:
http://help1c.com/faq/view/531.html
Находим проблемные запросы и решаем проблему.
Синтетический пример:
ВЫБРАТЬ
НН.Номер КАК НомерНН,
ПТУ.Номер КАК НомерПТУ,
РТУ.Номер КАК НомерРТУ
ИЗ
Документ.НалоговаяНаклад ная КАК НН
ЛЕВОЕ СОЕДИНЕНИЕ Документ.ПоступлениеТова ровУслуг КАК ПТУ
ПО НН.Сделка = ПТУ.Ссылка
ЛЕВОЕ СОЕДИНЕНИЕ Документ.РеализацияТовар овУслуг КАК РТУ
ПО НН.Сделка = РТУ.Ссылка
поле "сделка" НН не индексировано. Попробуйте выполнить запрос в трех вариантах:
без индекса по полю "сделка"
есть индекс по полю "сделка" и собрана статистика
с помощью способа который описан в статье - временных таблиц
ПоступлениеТова ровУслуг - 40000 шт.
РеализацияТовар овУслуг - 40000 шт.
Я конечно наверное барзею..., но хотелось бы пример того как sql-сервер построить индекс и собрать статитстику, если вас не затруднит
http://www.kodges.ru/11519-oracle-dlja-professionalov.html
http://www.ozon.ru/context/detail/id/3647294/
Приведите плиз пример какой-нибудь и ссылку на соответсвующую инфу, если вас не затруднит
Синтетический пример:
ВЫБРАТЬ
НН.Номер КАК НомерНН,
ПТУ.Номер КАК НомерПТУ,
РТУ.Номер КАК НомерРТУ
ИЗ
Документ.НалоговаяНаклад ная КАК НН
ЛЕВОЕ СОЕДИНЕНИЕ Документ.ПоступлениеТова ровУслуг КАК ПТУ
ПО НН.Сделка = ПТУ.Ссылка
ЛЕВОЕ СОЕДИНЕНИЕ Документ.РеализацияТовар овУслуг КАК РТУ
ПО НН.Сделка = РТУ.Ссылка
поле "сделка" НН не индексировано. Попробуйте выполнить запрос в трех вариантах:
без индекса по полю "сделка"
есть индекс по полю "сделка" и собрана статистика
с помощью способа который описан в статье - временных таблиц
ПоступлениеТова ровУслуг - 40000 шт.
РеализацияТовар овУслуг - 40000 шт.
Приведите плиз пример какой-нибудь и ссылку на соответсвующую инфу, если вас не затруднит