Тяжёлые запросы в Битрикс: разбор инцидента
Каталог на 1С-Битрикс начинает отдавать 502 после запуска новой витрины, а в базе растёт очередь запросов. Разбираем, как найти виновника, отличить медленный SQL от N+1 и исправить проблему без слепого увеличения кеша.

Каталог на 1С-Битрикс может начать отдавать 502 не из-за одного «медленного запроса», а из-за сотен почти одинаковых обращений к базе на одной странице. Разберём типичный инцидент для редакции 1С-Битрикс: Управление сайтом 24.x: после изменения фильтра время ответа выросло, PHP-воркеры занялись, а база данных стала узким местом. Замеры здесь не выдумываем: покажем, какие цифры снять до исправления и как доказать, что стало лучше.
Сценарий инцидента: карточка товара заняла весь пул PHP
Представьте витрину с каталогом, остатками и персональными ценами. После добавления нового блока страница товара стала выполнять не один запрос к базе, а запрос к товару, несколько обращений к свойствам, отдельные проверки цен и повторные выборки связанных элементов. На тестовом стенде это незаметно. Под нагрузкой каждый PHP-процесс ждёт базу, новые запросы встают в очередь, а веб-сервер начинает возвращать 502 или 504.
Первое правило расследования — не исправлять симптом до фиксации исходного состояния. Запишите для одного и того же URL:
- время полного ответа и время выполнения PHP;
- количество SQL-запросов и суммарное время работы с базой;
- число активных PHP-воркеров и долю запросов с ошибками 502/504;
- нагрузку на базу: CPU, диск, память, число соединений и ожидания;
- идентификатор товара или параметры фильтра, на которых проблема воспроизводится.
Снимайте показатели на прогретом и холодном кеше отдельно. Иначе результат смешает работу приложения с эффектом кеширования.
Кеш скрывает тяжёлый запрос только до первого промаха. Если причина не устранена, сброс кеша, новый товар или другой фильтр вернут инцидент.
Где искать запрос, который ломает страницу
1. Профилировщик и журнал медленных запросов
Начните с инструментов разработки 1С-Битрикс и журнала медленных запросов СУБД. Профилировщик нужен, чтобы связать SQL с PHP-кодом и компонентом. Журнал базы показывает, какие запросы действительно долго выполняются на сервере, включая обращения, которые приложение не считает проблемными.
В рабочей системе отладку включают временно и только с ограничением доступа. Подробный SQL-лог может содержать параметры запроса, идентификаторы товаров и другие данные. После сбора материала режим отладки отключают, а журнал ротируют.
2. Разделите три разных проблемы
Один тяжёлый SQL. Запрос читает большой объём строк, сортирует результат или соединяет несколько таблиц. Признак — высокое время выполнения конкретного SQL.
N+1. Код сначала получает список из N элементов, а затем делает отдельный запрос для каждого. Один запрос может занимать 5 миллисекунд, но 500 таких обращений дадут секунды задержки.
Ожидание базы. Сам запрос не обязательно плохо спроектирован: он ждёт блокировку, диск или свободное соединение. В профайлере это выглядит как длинное время, а в плане выполнения причина может быть неочевидна.
N+1-запросы — шаблон, при котором одна выборка порождает отдельный запрос для каждого элемента результата. В интернет-магазине это часто проявляется при загрузке свойств товаров, цен, остатков или связанных разделов.
Почему запрос становится тяжёлым именно в Битрикс
Слой ORM скрывает реальный SQL
Компонент или код на ORM может выглядеть компактно, но приводить к большим JOIN, дополнительным выборкам и сортировкам. Проверять нужно не только PHP-вызов, но и итоговый SQL в профилировщике. Особенно внимательно смотрите на выборку всех полей, постраничную навигацию и сортировку по полю, для которого нет подходящего индекса.
В старом коде похожий эффект дают классы работы с инфоблоками и циклы по результатам выборки. Сам факт использования старого API не доказывает наличие проблемы, но цикл, внутри которого выполняется обращение к базе, — обязательное место для проверки.
Фильтр по свойствам инфоблока
Фильтрация по одному простому полю и фильтрация по нескольким свойствам — разные по цене операции. Если условие заставляет базу просматривать большую таблицу свойств, индекс существовать может, но план всё равно выберет полное сканирование. Это нужно подтверждать планом выполнения, а не догадкой.
Не добавляйте индекс вслепую. Индекс ускоряет чтение только для подходящего условия, но увеличивает стоимость INSERT и UPDATE, расходует место и может не использоваться из-за функции над колонкой, несовместимого порядка полей или низкой избирательности.
Сортировка и постраничная навигация
Сортировка по цене, наличию или пользовательскому свойству часто становится дорогой после роста каталога. Особенно если сначала нужно собрать большой набор строк, затем отсортировать его, а только потом вернуть несколько элементов страницы. Проверьте, сколько строк база читает до применения LIMIT, а не только сколько строк получает приложение.
Как подтвердить причину, а не угадать
- Зафиксируйте воспроизводимый запрос. Используйте один URL, товар и набор параметров. Проверка: два-три повторных запуска дают сопоставимые значения, а не случайный разброс.
- Сопоставьте SQL с кодом. Найдите компонент, обработчик или пользовательский класс, который породил запрос. Проверка: вы понимаете, какой блок страницы исчезнет, если временно отключить этот участок на тестовом стенде.
- Посмотрите план выполнения. Проверьте тип доступа к таблицам, предполагаемое число строк, сортировки и соединения. Проверка: понятно, где база читает больше всего данных и какой шаг занимает время.
- Проверьте повторения. Сгруппируйте одинаковые или отличающиеся только идентификатором запросы. Проверка: видно, растёт ли число SQL вместе с количеством товаров на странице.
- Повторите тест после изменения. Сравните одинаковые условия, размер базы и состояние кеша. Проверка: улучшилось не только среднее время, но и медленный хвост — например, 95-й или 99-й перцентиль.
Какие исправления выбирать
| Подход | Что меняется | Цена и риск | Когда применять |
|---|---|---|---|
| Убрать N+1 | Связанные данные загружаются пакетно или заранее подготовленной выборкой | Нужно перепроверить память и состав результата | Число запросов растёт вместе с количеством элементов |
| Сузить выборку | Возвращаются только нужные поля и строки | Компонент может ожидать поле, которое больше не выбирается | В SQL есть выборка всех колонок или лишние связи |
| Добавить индекс | СУБД быстрее находит строки по конкретному условию | Увеличиваются размер базы и стоимость записи | План показывает полное сканирование и индекс соответствует фильтру |
| Кешировать результат | Повторный запрос обслуживается без обращения к базе | Риск устаревших цен, остатков и персональных данных | Данные допустимо хранить ограниченное время |
| Предрасчитать данные | Тяжёлая агрегация выполняется заранее, а не на запросе пользователя | Появляется задача обновления и контроль рассинхронизации | Отчёт или фильтр строится по большим объёмам данных |
Если проблема в N+1, индекс на каждую отдельную выборку не заменит пакетную загрузку. Если проблема в полном сканировании, кеш может снизить частоту ошибки, но не исправит план. Выбор должен следовать из наблюдаемого узкого места.
Где подход ломается
Кеширование скрывает свежесть данных
Для каталога нельзя одинаково кешировать описание товара, остаток и персональную цену. У каждого блока свой срок жизни и событие сброса. Если после изменения цены страница продолжает показывать старое значение, ускорение превращается в функциональную ошибку.
Индекс не гарантирует ускорение
После добавления индекса план нужно проверить заново на данных, близких к боевым. Оптимизатор может выбрать прежний путь, если условие возвращает большую долю таблицы. Несколько индексов также не всегда объединяются так, как ожидает разработчик.
Пакетная выборка упирается в память
Устранение N+1 через загрузку всех связанных данных одним запросом уменьшает количество обращений к базе, но увеличивает размер результата. При большом каталоге это может поднять потребление памяти PHP и время сериализации. Пакет нужно ограничивать страницей, курсором или порциями — конкретный вариант зависит от используемого API и версии.
Отладка в проде сама создаёт нагрузку
Подробное логирование SQL и профилирование нельзя оставлять постоянно включёнными на публичном проекте. Сначала ограничьте сбор одним тестовым запросом или коротким окном, затем проверьте размер логов и права доступа.
Что проверять на ревью
- Есть ли обращение к базе внутри цикла по товарам, разделам или пользователям.
- Выбираются ли только поля, которые реально используются на странице.
- Не выполняется ли один и тот же запрос повторно в компоненте и его шаблоне.
- Совпадают ли фильтр и сортировка с существующим индексом.
- Что произойдёт после сброса кеша и при пустом результате.
- Не попадают ли персональные цены, остатки и права доступа в общий кеш.
Формулировка замечания должна быть проверяемой: «На странице N элементов выполняется N одинаковых запросов к свойствам; получить данные одной выборкой и сравнить количество SQL до и после». Это полезнее, чем комментарий «оптимизировать базу».
Что делать дальше
Соберите короткий регрессионный тест для проблемной страницы: холодный кеш, прогретый кеш, один товар, большая выборка и пустой результат. Для каждого сценария сохраняйте время ответа, число SQL и суммарное время базы. После исправления смотрите не только среднее значение, но и долю запросов, которые превышают ваш рабочий порог.
Следующим шагом проверьте тот же путь после импорта товаров и массового изменения цен. Именно фоновые операции часто возвращают исчезнувший N+1 или создают блокировки. Инвалидация кеша и выбор конкретных методов ORM — отдельные темы; здесь важнее сначала связать пользовательскую задержку с конкретным SQL и измерить эффект изменения.
IT-архитектор / SQL-оптимизатор: Поиск N+1, оптимизация запросов, code review, поиск багов.
Спросить за 20 ₽Источники: 1С-Битрикс: документация по производительности, 1С-Битрикс: официальная документация разработчика
Частые вопросы
Как понять, что проблема именно в SQL, а не в PHP?
Нужно ли сразу добавлять индекс на проблемное поле?
Что делать, если после исправления запросов страница всё ещё отдаёт 502?
А если у меня другая версия Битрикс или другая СУБД?
Можно ли просто увеличить кеш или время ожидания?
Какие риски остаются после устранения N+1?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.