Оптимизация запросов к инфоблокам 1С-Битрикс
Инфоблок Битрикс — гибкий, но тяжёлый инструмент. Таблицы b_iblock_element, b_iblock_element_property, b_iblock_section, b_iblock_element_property_enum образуют реляционную структуру, которая при неаккуратных запросах порождает чудовищные планы выполнения. EXPLAIN запроса к списку 100 товаров с 5 свойствами нередко показывает перебор миллионов строк и несколько секунд выполнения. Для каталогов с активным трафиком это прямо влияет на TTFB и нагрузку на MySQL.
Проблемы старого API (CIBlockElement::GetList)
Старый API Битрикс (CIBlockElement::GetList) при запросе с PROPERTY_ фильтром генерирует запрос с JOIN к b_iblock_element_property для каждого свойства. При 5 свойствах в фильтре — 5 JOIN-ов. При этом API не позволяет явно управлять планом запроса.
Дополнительная проблема — параметр arSelect. Если передать "*" или упустить явный список полей, API запрашивает все поля, включая объёмные текстовые описания и ненужные метаданные. Для страницы каталога с 48 товарами это умножает объём передаваемых данных из БД в 3–5 раз.
Переход на D7 API (Iblock\ElementTable)
API D7 (\Bitrix\Iblock\ElementTable) предоставляет Query Builder с явным контролем select, filter, order, limit. Каждый запрос транслируется в SQL и может быть проанализирован через getQuery()->getSql() до выполнения.
Пример оптимального запроса для страницы каталога:
use Bitrix\Iblock\ElementTable;
$result = ElementTable::getList([
'select' => [
'ID',
'NAME',
'CODE',
'PREVIEW_PICTURE',
'IBLOCK_SECTION_ID',
],
'filter' => [
'=IBLOCK_ID' => CATALOG_IBLOCK_ID,
'=ACTIVE' => 'Y',
'=IBLOCK_SECTION_ID' => $sectionId,
],
'order' => ['SORT' => 'ASC', 'ID' => 'ASC'],
'limit' => 24,
'offset' => $page * 24,
'cache' => ['ttl' => 3600],
]);
Explicit select без свойств — запрос только к b_iblock_element, без JOIN к b_iblock_element_property. Если свойства нужны для части товаров (например, только для тех, которые попали на страницу), они запрашиваются отдельным запросом по списку ID — это паттерн «запрос-по-ID-батчами» вместо N+1.
N+1 — главный враг производительности инфоблока
N+1 проблема в контексте Битрикс: получили список из 24 элементов, затем для каждого элемента в цикле вызываете CIBlockElement::GetProperty() или отдельный GetList для связанных данных. Итого: 1 запрос списка + 24 запроса свойств + 24 запроса связанных элементов = 49 запросов.
Решение — батчевая выборка:
// Получаем ID всех элементов
$ids = array_column($elements, 'ID');
// Один запрос для всех свойств
$propsResult = \CIBlockElement::GetPropertyValuesArray(
$ids,
CATALOG_IBLOCK_ID,
['CODE' => ['BRAND', 'COLOR', 'SIZE']]
);
Или через D7 с использованием \Bitrix\Iblock\ElementPropertyTable для выборки свойств списком ID.
Кеширование на уровне компонентов vs на уровне данных
Кеш компонента (файловый, через initCache) — грубый инструмент: кешируется весь HTML-фрагмент, инвалидируется по тегу IBLOCK_N. Для страниц с персонализацией или частыми обновлениями каталога это неудобно.
Кеширование на уровне данных через ManagedCache с гранулярными тегами (по разделу, по набору свойств) позволяет инвалидировать только затронутые записи. Накладные расходы — необходимость явно прописывать кеш в каждом месте обращения к данным.
Кейс: каталог стройматериалов, 45 000 SKU
До оптимизации: страница раздела каталога (48 товаров) — 1,8 с TTFB (без кеша), 340 ms с кешем. MySQL slow query log показывал: один запрос к списку товаров — 640 мс, второй запрос «похожих товаров» — 310 мс, суммарно 12 запросов на страницу.
Выявленные проблемы:
-
CIBlockElement::GetListсSELECT => "*"иPROPERTY_FILTER => [5 свойств] - N+1 в блоке «похожие товары»: запрос к каждому из 6 товаров отдельно
- Запрос раздела с полным деревом дочерних разделов без ограничения уровня
Что сделали:
- Переписали основной запрос на D7, explicit select (8 полей вместо всех)
- Батчевый запрос свойств для блока «похожие товары»
- Ограничение дерева разделов через параметр
DEPTH_LEVELи кеш с тегом раздела - Добавление индексов на
b_iblock_element: составной по(IBLOCK_ID, IBLOCK_SECTION_ID, ACTIVE, SORT)
Результат: страница раздела (без кеша) — 380 мс TTFB, с кешем — 45 мс. Количество запросов к БД: 12 → 4. Нагрузка на MySQL снизилась, появилась возможность обслуживать в 3 раза больше одновременных пользователей без добавления мощностей.
Составные индексы для инфоблоков
Стандартные индексы Битрикс не покрывают все типичные выборки. Критичный индекс для каталога:
ALTER TABLE b_iblock_element
ADD INDEX idx_iblock_section_active_sort
(IBLOCK_ID, IBLOCK_SECTION_ID, ACTIVE, SORT);
Анализ текущих индексов через SHOW INDEX FROM b_iblock_element и EXPLAIN конкретных запросов из slow log — обязательный шаг перед добавлением.
Сроки
| Этап | Срок |
|---|---|
| Аудит запросов (slow log, Explain, панель Битрикс) | 1–2 дня |
| Переработка критичных запросов (D7, batching) | 3–7 дней |
| Индексы и настройка кеша | 1–2 дня |
На крупных проектах с большим количеством кастомных компонентов — до 3 недель.







