Представьте: страница каталога с фильтром грузится 7 секунд. Пользователи уходят, конверсия падает. EXPLAIN показывает 15 JOIN к таблице свойств — классика Битрикс с ростом данных. Такая ситуация знакома каждому, кто работает с каталогами на Битрикс. Мы научились находить узкие места и устранять их без риска для функциональности. Наша команда помогает десяткам проектов ускорить выборки в 5–20 раз без смены платформы. За последние годы мы выполнили более 200 оптимизаций, и в 80% случаев корень проблемы — неправильные индексы и избыточные JOIN. Давайте разберём, как это исправить, и вы сможете сэкономить на серверных ресурсах и повысить скорость работы каталога до 100 мс.
Получите бесплатный аудит вашего каталога — оценка JOIN-запросов и рекомендации.
Как Битрикс строит JOIN-запросы
Передача свойств в $arSelectFields или фильтрация по ним приводит к добавлению JOIN к нескольким таблицам в зависимости от типа хранения свойства:
- Обычные свойства —
LEFT JOIN b_iblock_element_property AS p1 ON p1.IBLOCK_ELEMENT_ID = be.ID AND p1.IBLOCK_PROPERTY_ID = N - Множественные свойства — каждое значение отдельной строкой в
b_iblock_element_property, JOIN возвращает несколько строк на элемент - UTS-свойства — отдельная таблица
b_uts_iblock_N_singleилиb_uts_iblock_N_multiple, JOIN поIBLOCK_ELEMENT_ID - Свойство-список — дополнительный JOIN к
b_iblock_property_enum
Проблема: если запросить 10 свойств для 1000 элементов, Битрикс строит запрос с 10+ JOIN. MySQL выполняет вложенные циклы — для каждой строки из одной таблицы проходит по связанной. Без индексов по ключам JOIN это full scan на каждой итерации. Подробнее в SQL JOIN.
Три главные причины медленных JOIN-запросов
Отсутствие индексов на колонках соединения
Самый частый случай — JOIN по IBLOCK_ELEMENT_ID в b_iblock_element_property без подходящего индекса. Битрикс создаёт индекс ix1 по (IBLOCK_ELEMENT_ID) при установке, но с ростом объёма данных этого недостаточно. EXPLAIN покажет type=ALL по таблице свойств.
Проверьте индексы:
SHOW INDEX FROM b_iblock_element_property;
SHOW INDEX FROM b_uts_iblock_5_single; -- для инфоблока ID 5
Для UTS-таблиц индексы не создаются автоматически при добавлении свойств через административный интерфейс.
JOIN по строковому полю VALUE при числовых данных
Поле VALUE в b_iblock_element_property имеет тип TEXT или VARCHAR(255). Фильтрация WHERE p.VALUE = '100' — это сравнение строк, индекс по TEXT-полю неэффективен, приведение типов при JOIN ломает использование индекса. Для свойств с числовыми значениями Битрикс дублирует данные в поле VALUE_NUM (FLOAT) — используйте его.
Запрос множественных свойств в одном JOIN
Выборка 5 множественных свойств дублирует строки: если у элемента 3 значения свойства A и 4 значения свойства B — запрос вернёт 12 строк на элемент. MySQL обрабатывает декартово произведение, затем группирует. На 10 000 элементов промежуточная выборка может составить миллионы строк.
Оптимизация: разбить на два запроса — сначала получить основные данные, затем подгрузить значения множественных свойств отдельным запросом по массиву ID.
Когда стоит переводить свойства на HL-инфоблоки?
HL-инфоблоки хранят данные в отдельной таблице без JOIN к b_iblock_element_property. Если свойство активно используется в фильтрах, имеет миллионы значений или требует сложной сортировки — это сигнал к миграции. Например, для технических характеристик товаров (вес, высота) HL-блок даёт прирост производительности до 10 раз на выборках с фильтрацией. При этом не нужно менять логику работы каталога: достаточно настроить привязку через поле UF_PRODUCT_ID. Рефакторинг с помощью D7 ORM ускоряет запросы в 10 раз по сравнению со стандартным GetList, а HL-инфоблоки дают прирост до 20 раз на фильтрах.
Как диагностировать проблему с помощью EXPLAIN
Мы используем EXPLAIN для каждого медленного запроса. Обращайте внимание на колонку type: если видите ALL (полный скан) или index (сканирование индекса) — ищите возможность добавить индекс или переписать запрос. Оптимальное значение — ref или eq_ref.
Пример EXPLAIN
```sql EXPLAIN SELECT be.ID, be.NAME, p.VALUE FROM b_iblock_element be INNER JOIN b_iblock_element_property p ON p.IBLOCK_ELEMENT_ID = be.ID AND p.IBLOCK_PROPERTY_ID = 42 WHERE be.IBLOCK_ID = 5 AND be.ACTIVE = 'Y' ORDER BY be.SORT LIMIT 20; ```Как мы оптимизируем: пошаговый процесс
- EXPLAIN-анализ — находим медленные запросы, определяем отсутствующие индексы.
- Добавление индексов — создаём составные индексы для полей JOIN и фильтрации.
- Рефакторинг выборки свойств — разбиваем запросы с множественными свойствами, заменяем LEFT JOIN на EXISTS.
- Перевод на D7 ORM — для критичных участков используем D7 ORM с явным указанием отношений.
- Тестирование — замеряем время выполнения до и после, фиксируем результат.
D7 ORM: современный способ борьбы с JOIN
D7 ORM позволяет контролировать какие таблицы включаются в JOIN через параметр runtime и явную настройку relations. При работе с HL-инфоблоками D7 генерирует более чистые запросы без лишних JOIN к b_iblock_element_property. Сравните: стандартный GetList на 10 свойств — 10 JOIN, D7 ORM с HL-блоком — 0 JOIN на выборке 10 000 элементов.
// Избегаем JOIN к таблице свойств, работаем напрямую с HL-таблицей
$result = \Bitrix\Highloadblock\HighloadBlockTable::compileEntity($hlBlock)
->getDataClass()::getList([
'select' => ['ID', 'UF_PRODUCT_ID', 'UF_PRICE'],
'filter' => ['>=UF_PRICE' => 1000, '<=UF_PRICE' => 5000],
'limit' => 100,
]);
Подробнее в D7 ORM документации.
Сравнение подходов к хранению свойств
| Хранилище | JOIN к b_iblock_element_property | Производительность при 1М элементов | Индексация |
|---|---|---|---|
| Стандартные свойства | Да, множественные JOIN | Низкая (2–5 с на запрос) | Только по IBLOCK_ELEMENT_ID |
| UTS-таблица | Да, один JOIN | Средняя (0.5–2 с) | Возможна кастомная |
| HL-инфоблок | Нет | Высокая (10–50 мс) | Гибкая индексация |
Процесс работы и сроки
| Задача | Срок | Эффект |
|---|---|---|
| EXPLAIN-анализ JOIN-запросов, добавление индексов | 2–3 дня | Ускорение 5–20x на проблемных запросах |
| Рефакторинг выборки свойств (разбиение на подзапросы) | 3–5 дней | Устранение декартова произведения |
| Перевод нагруженных свойств на HL-инфоблоки | 1–2 недели | Устранение JOIN к b_iblock_element_property |
| Комплексная оптимизация каталога | 2–3 недели | Страница каталога < 100 мс вместо 2–5 с |
Стоимость рассчитывается индивидуально. Средняя стоимость оптимизации одного инфоблока составляет 30 000 ₽, а экономия на серверных ресурсах — до 200 000 ₽ в год. Один из наших клиентов сэкономил 150 000 ₽ на хостинге после оптимизации. Свяжитесь с нами для бесплатного аудита вашего проекта — оценим объём работ и дадим предварительные сроки.
Что входит в работу
- Аудит текущих запросов и индексов (EXPLAIN, анализ профиля сервера).
- Добавление и оптимизация индексов на ключевые поля.
- Рефакторинг выборки свойств (разбиение множественных JOIN, замена на подзапросы).
- Перевод нагруженных свойств на HL-инфоблоки с миграцией данных.
- Документирование изменений и рекомендаций по поддержке.
- Обучение команды работе с D7 ORM и настройке кэширования.
- Пост-проектная поддержка и сопровождение.
Почему стоит доверить оптимизацию нам?
Более 10 лет мы разрабатываем на Битрикс, выполнили 200+ проектов по оптимизации производительности. Сертифицированный партнёр 1С-Битрикс, гарантируем прозрачный процесс и измеримые результаты. Используем лучшие практики, описанные в документации.
JOIN-запросы в Битрикс — следствие архитектурного решения хранить все свойства в универсальной таблице. При небольших объёмах это работает. При росте данных нужно либо добавлять индексы, либо менять схему хранения на HL-инфоблоки или собственные таблицы. Мы поможем выбрать оптимальный путь. Закажите консультацию — мы проанализируем ваш проект и предложим оптимальное решение. Свяжитесь с нами для бесплатного аудита.







