Борьба с медленными запросами в 1С. Типичные проблемы
Рубрика: 1С:Предприятие -> Программирование
Метки: 1С:Предприятие | инструменты | полезности | программирование | советы
Просмотров: 7538
На работе часто приходится проводить ревью кода и разбираться с проблемами в стиле «почему же тормозит запрос». Проблемы подобных тормозов чаще всего вызваны не оптимальными запросами к базе. Так уж получилось, что код нашей конфигурации разрабатывается почти 9 лет и за этот внушительный промежуток времени накопились запросы, которые нужно переписать. Они превосходно решают задачи, но при нынешнем объеме базы данных работают не так быстро, как хотелось бы.
Причин появления медленных запросов много. Где-то сами недоглядели, а где-то просто нельзя было сделать по-другому. Язык запросов 1С за это время совершенствовался, а наш код был написан в то время, когда, например, в нем отсутствовала возможность применять временные таблицы (как пример). Вот и получилось, что в свое время написали так, а сегодня этот код требует доработок. Для таких долгоиграющих проектов это нормально. Код стареет, появляются новые требования и без рефакторинга не обойтись.
В этой заметке я решил собрать список типичных проблем, которые заставляют запросы выполняться медленно. Возможно, узнаете для себя что-то новенькое и сможете во время принять должные меры по устранению будущих проблем.
Проблема №1. Соединение основного запроса с подзапросами
Проблема была особенно актуально до версии 8.1. Напомню, именно в ней впервые появились временные таблицы. Без них часто приходилось соединять основной запрос с результатом подзапроса. Например, есть основной запрос, а нам требуется прикрутить к нему результат специальной выборки, сделанной в подзапросе. В коде выглядит примерно так:
Выбрать Поле1, Поле2, Из Источник1 Левое соединение ( Выбрать поле1, поле2 ИЗ Источник )
На первый взгляд ничего страшного, но работать такой запрос начнет медленно даже не на самых больших выборках. Причем внешне тормоза будут незаметны – на сервере процессор загружен сильно не будет, память тоже не будет активно жраться, а запрос будет выполняться долго. Причем подобные тормоза будут воспроизводиться не с 100% вероятностью. В некоторых случаях, один и тот же запрос будет выполнен с разной скоростью (тут вопрос кэширования данных).
Вывод: в актуальных версиях платформы не стоит использовать соединения с подзапросами. Вместо этого применяйте индексируемые временные таблицы и делайте отдельные запросы на каждый подзапрос. Работать будет значительно быстрей и предсказуемо. Обратите внимание, при указании индексов у временных таблиц. Индексируйте все поля, участвующие в условиях соединения.
Проблема №2. Старайтесь не применять ключевое слово «В ИЕРАРХИИ»
Если требуется поставить условия с учетом иерархичности, то будьте осторожны, возможно, использование оператора «В ИЕРАРХИИ» создаст большие тормоза при применении на большом количестве данных. Причем проявится это только в момент, когда данных в источнике станет сильно много.
А много это сколько? В моей практике тормоза появились уже на 300000 записей. Эта цифра условная и на разном оборудовании (а также архитектуре БД) будет отличаться. Чтобы не попасть в нехорошую ситуацию, просто откажитесь от использования конструкции «В ИЕРАРХИИ». Как же обойтись без нее? Зависит от ситуации, но чаще всего проще написать отдельный запрос.
Проблема №3. Виртуальные таблицы
В некоторых ситуациях начинают появляться существенные тормоза при большом количестве соединений основного запроса с виртуальными таблицами. Все знают, что при возможности стоит применять виртуальные таблицы (Остатки(), СрезПоследних() и т.д.), но при использовании их в соединениях могут появляться потери производительности.
Причина столь странного поведения заключается в природе виртуальных таблиц. Виртуальная таблица, по сути, аккумулирует данные из нескольких источников. При трансляции запроса в SQL, в финальном запросе к базе данных могут появиться подзапросы (вспоминаем проблему №1) и в некоторых случаях может получиться соединение с основным запросом. Дальше – больше, если оптимизатор базы данных выберет неоптимальный план, то появятся тормоза, которых никто не ожидал увидеть. В итоге мы возвращаемся к проблеме №1. Решение опять же простое – разбивать запросы и использовать временные таблицы.
Проблема №4. Излишние индексы
Неоднократно видел, как разработчики на ранних стадиях пытаются «предвидеть» различные узкие места и создают кучу индексов. Вроде как в будущем это должно окупиться и данные будут выбираться особенно быстро. Это большое заблуждение. Индексы должны создаваться рационально и осознанно. Чрезмерное количество индексом в первую очередь будет влиять не на скорость выборки данных, а на их модификацию. Следовательно, если чувствуете, что при записи данных в базу стали ощущаться заметные тормоза, то проверьте, а не переборщили ли вы с индексами.
В одной из коммерческих конфигураций я видел объекты метаданных, где для каждого реквизита был добавлен индекс. Все было хорошо и незаметно, пока данных в объекте не стало слишком много.
Применение индексов 1С:Предпряитие имеет ряд особенностей, поэтому на эту тему поговорим как-нибудь отдельно.
Проблема №5. Коварное «ИЛИ»
Все мы знаем про ключевое слово «ИЛИ», но не всегда догадываемся, какие оно может принести проблемы при построении запросов к таблицам, содержащим большое количество данных. Если мы применяем ключевое слово «ИЛИ» в секции запроса «ГДЕ», то рискуем напороться на ситуацию, когда СУБД не сможет воспользоваться индексами таблиц, и вместо этого будет выполнять сканирование. Таким образом, выборка из больших таблиц будет идти значительно дольше и есть все шансы столкнуться с блокировкой данных.
На мой взгляд, это одна из самых частых недоработок во многих конфигурациях. Я специально не стал употреблять слово «ошибка», т.к. проблема возникает только при выборке данных из больших таблиц. Пока данных немного, никаких тормозов вы скорей всего не заметите.
Что делать и как быть? Не лениться, а применять вместо «ИЛИ» объединения запросов. Например:
Выбрать поле1 из источник где поле1 = «тест» Объединить все Выбрать поле1 из источник где поле1= «тест2»
Аналогичные требования применяются и при использовании "ИЛИ" в соединениях. Здесь ситуация несколько сложней. Далеко не всегда возможно переписать соединение без «ИЛИ». В таких случаях действовать надо по ситуации. Сначала внимательно проанализировать решение и попробовать найти другое. Вариантов не нашлось? Тогда применяйте, но не забывайте про временные таблицы. Вполне возможно, что подготовка временной таблицы из второго источника скажется куда положительней на производительности.
На сегодня хватит, но это далеко не все проблемы, из-за которых могут возникать «тормоза» при получении данных из базы. В одной из следующих заметок разберем еще пять типичных ситуаций. За время программирования на 1С накопилось достаточно много заметок на тему «как не надо делать», буду постепенно превращать их в полноценные записи для блога.