Оптимізація JOIN-запитів у 1С-Бітрікс

Наша компанія займається розробкою, підтримкою та обслуговуванням рішень на Бітрікс та Бітрікс24 будь-якої складності. Від простих односторінкових сайтів до складних інтернет-магазинів, CRM систем з інтеграцією 1С та телефонії. Досвід розробників підтверджено сертифікатами від вендора.
Пропоновані послуги
Показано 1 з 1 послугУсі 1626 послуг
Оптимізація JOIN-запитів у 1С-Бітрікс
Середня
~1-2 тижні
Часті питання

Наші компетенції:

Етапи розробки

Останні роботи

  • image_website-b2b-advance_0.png
    Розробка сайту компанії B2B ADVANCE
    1262
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Розробка веб-сайту для компанії ФІКСПЕР
    851
  • image_bitrix-bitrix-24-1c_development_of_an_online_appointment_booking_widget_for_a_medical_center_594_0.webp
    Розробка на базі Бітрікс, Бітрікс24, 1С для компанії Development of an Online
    585
  • image_bitrix-bitrix-24-1c_mirsanbel_458_0.webp
    Розробка на базі 1С Підприємство для компанії МИРСАНБЕЛ
    751
  • image_crm_dolbimby_434_0.webp
    Розробка сайту на CRM Бітрікс24 для компанії DOLBIMBY
    657
  • image_crm_technotorgcomplex_453_0.webp
    Розробка на базі Бітрікс24 для компанії ТЕХНОТОРГКОМПЛЕКС
    989

Оптимізація JOIN-запитів у 1С-Бітриксі

Бітрикс активно використовує JOIN у своєму SQL: запит до CIBlockElement::GetList() з виборкою властивостей може об'єднувати 8–12 таблиць. На малих обсягах це непомітно. Коли в b_iblock_element 500 000 рядків, а в b_iblock_element_property — 10 мільйонів, неоптимальний 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
  • Властивість-список (L) — додатковий JOIN до b_iblock_property_enum для отримання значень

Проблема: якщо запросити 10 властивостей для 1000 елементів, Бітрикс будує запит з 10+ JOIN. MySQL виконує вкладені цикли — для кожного рядка з однієї таблиці проходить по пов'язаній. Без індексів за ключами JOIN це full scan на кожній ітерації.

Головні причини повільних JOIN у Бітриксі

1. Відсутність індексів на колонках з'єднання.

Найчастіший випадок — 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-таблиць індекси не створюються автоматично при додаванні властивостей через адміністративний інтерфейс.

2. JOIN за строковим полем VALUE при числових даних.

Поле VALUE у b_iblock_element_property має тип TEXT або VARCHAR(255). Фільтрація WHERE p.VALUE = '100' — це порівняння рядків, індекс за TEXT-полем неефективний, приведення типів при JOIN ламає використання індекса. Для властивостей з числовими значеннями Бітрикс дублює дані у полі VALUE_NUM (FLOAT) — використовуйте його.

3. Запит множественних властивостей в одному JOIN.

Виборка 5 множественних властивостей дублює рядки: якщо у елемента 3 значення властивості A та 4 значення властивості B — запит повертає 12 рядків на елемент. MySQL обробляє декартово добуток, потім групує. На 10 000 елементів проміжна виборка може скласти мільйони рядків.

Оптимізація: розбити на два запити — спочатку отримати основні дані, потім підгрузити значення множественних властивостей окремим запитом за масивом ID.

Оптимізація на рівні запитів

Вивести фільтрацію до JOIN. Якщо потрібні елементи певного розділу з конкретною властивістю — спочатку відфільтруйте за b_iblock_element (з індексом за IBLOCK_SECTION_ID), потім робіть JOIN. MySQL повинен почати з найменшої виборки.

Використовуйте STRAIGHT_JOIN щоб зафіксувати порядок з'єднання, якщо оптимізатор вибирає неправильний план:

$connection = \Bitrix\Main\Application::getConnection();
$result = $connection->query("
    SELECT STRAIGHT_JOIN 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'
      AND p.VALUE_NUM BETWEEN 1000 AND 5000
    ORDER BY be.SORT
    LIMIT 20
");

Заміна LEFT JOIN на підзапит або EXISTS. Для перевірки наявності зв'язку (потрібне тільки значення або тільки факт наявності) EXISTS працює швидше, ніж LEFT JOIN з IS NOT NULL:

-- Повільніше: JOIN + групування
SELECT be.* FROM b_iblock_element be
LEFT JOIN b_iblock_element_property p ON p.IBLOCK_ELEMENT_ID = be.ID AND p.IBLOCK_PROPERTY_ID = 10
WHERE p.ID IS NOT NULL;

-- Швидше: EXISTS
SELECT be.* FROM b_iblock_element be
WHERE EXISTS (
    SELECT 1 FROM b_iblock_element_property p
    WHERE p.IBLOCK_ELEMENT_ID = be.ID AND p.IBLOCK_PROPERTY_ID = 10
);

Оптимізація через D7 ORM

D7 дозволяє контролювати які таблиці включаються в JOIN через параметр runtime та явну налаштування relations. При роботі з HL-інфоблоками (Highload), D7 генерує чистіші запити без зайвих JOIN до b_iblock_element_property.

// Уникаємо 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,
    ]);

HL-інфоблоки зберігають дані в окремій таблиці без JOIN до b_iblock_element_property — для високонавантажених властивостей (технічні характеристики, великі каталоги) це правильна архітектура.

Індекси для оптимізації JOIN

Таблиця Індекс Для чого
b_iblock_element_property (IBLOCK_PROPERTY_ID, VALUE_NUM) Фільтр за числовими властивостями
b_iblock_element_property (IBLOCK_ELEMENT_ID, IBLOCK_PROPERTY_ID) JOIN за елементом + властивістю
b_uts_iblock_N_single (UF_CUSTOM_FIELD) Фільтр за користувацькою властивістю
b_catalog_price (PRODUCT_ID, CATALOG_GROUP_ID) JOIN цін до елементів
b_iblock_section_element (IBLOCK_SECTION_ID) JOIN розділів до елементів

Тривалість робіт

Задача Тривалість Ефект
EXPLAIN-аналіз JOIN-запитів, додавання індексів 2–3 дні Прискорення 5–20x на проблемних запитах
Рефакторинг виборки властивостей (розділення на підзапити) 3–5 днів Усунення декартова добутку
Перевід навантажених властивостей на HL-інфоблоки 1–2 тижні Усунення JOIN до b_iblock_element_property
Комплексна оптимізація каталогу 2–3 тижні Сторінка каталогу < 100 мс замість 2–5 с

JOIN-запити у Бітриксі — наслідок архітектурного рішення зберігати всі властивості у універсальній таблиці. При невеликих обсягах це працює. При зростанні даних потрібно або додавати індекси, або змінювати схему зберігання на HL-інфоблоки або власні таблиці.