Оптимізація запитів до highload-блоків 1С-Бітрікс

Наша компанія займається розробкою, підтримкою та обслуговуванням рішень на Бітрікс та Бітрікс24 будь-якої складності. Від простих односторінкових сайтів до складних інтернет-магазинів, CRM систем з інтеграцією 1С та телефонії. Досвід розробників підтверджено сертифікатами від вендора.
Пропоновані послуги
Показано 1 з 1 послугУсі 1626 послуг
Оптимізація запитів до highload-блоків 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

Оптимізація запитів до Highload-блоків 1С-Бітрікс

HighLoad-блоки (модуль highloadblock) з'явилися в Бітрікс як відповідь на проблеми продуктивності інфоблоків при зберіганні великих обсягів даних без ієрархії. Кожен HL-блок — це окрема MySQL-таблиця з автогенерованою назвою виду b_uts_catalog_props або призначеною для користувача назвою через налаштування. На відміну від інфоблоків, HL-блоки не мають складних JOIN-залежностей і теоретично мають працювати швидко. На практиці повільні запити до HL-блоків зустрічаються не рідше — через відсутність індексів, неправильне використання API і невдалу схему даних.

Архітектура HL-блоків і точки зростання

Таблиця HL-блоку містить колонки ID, UF_* (поля користувача). При створенні HL-блоку через інтерфейс Бітрікс жодних індексів, крім первинного ключа по ID, автоматично не створюється. Це означає, що будь-який фільтр по полях, крім ID, призводить до повного перебору таблиці (type = ALL в EXPLAIN).

Приклад проблемного запиту:

$result = HighloadBlockTable::getList([
    'filter' => [
        '=UF_STATUS' => 'active',
        '>=UF_CREATED_AT' => new DateTime('-30 days'),
    ],
    'select' => ['ID', 'UF_NAME', 'UF_VALUE'],
    'order'  => ['UF_CREATED_AT' => 'DESC'],
    'limit'  => 50,
]);

Без індексу по UF_STATUS і UF_CREATED_AT цей запит сканує всю таблицю. При 500 000 записів — 300–800 мс на запит.

Індекси для HL-блоків

Бітрікс не надає UI для створення індексів HL-блоків. Індекси створюються безпосередньо через SQL або через міграції. Для прикладу вище:

ALTER TABLE b_uts_my_highload
  ADD INDEX idx_status_created (UF_STATUS, UF_CREATED_AT);

Правила вибору індексів:

  • Поля у filter — кандидати для індексування, але порядок важливий: поле з меншою кардинальністю (наприклад, UF_STATUS з 3 значеннями) ставиться першим лише якщо воно використовується в запитах самостійно; інакше складений індекс починається з високо-кардинального поля
  • Поля у order мають бути в індексі або покривальному індексі
  • SELECT * замінюється явним select з потрібними полями — скорочує обсяг даних, іноді дозволяє використовувати covering index

Кешування результатів HL-запитів

HL-блоки не мають автоматичного тегованого кешу, як інфоблоки. При використанні HighloadBlockTable::getList() кешування потрібно додавати явно через ManagedCache:

$cache = \Bitrix\Main\Application::getInstance()->getManagedCache();
$cacheKey = 'hl_my_block_active_' . md5(serialize($filter));

if (!$cache->read(600, $cacheKey, 'hl_my_block')) {
    $result = MyHLTable::getList(['filter' => $filter, 'select' => $select]);
    $data = $result->fetchAll();
    $cache->set($cacheKey, $data);
} else {
    $data = $cache->get($cacheKey);
}

Інвалідація за тегом hl_my_block викликається в обробнику OnAfterAdd / OnAfterUpdate HL-блоку.

Пагінація на великих таблицях

Стандартна пагінація через offset деградує на великих даних: OFFSET 50000 LIMIT 50 змушує MySQL прочитати та відкинути 50 000 записів. Для HL-блоків з десятками тисяч записів і нескінченним прокручуванням або великою кількістю сторінок використовується cursor-based пагінація:

// Замість offset використовуємо останній ID
$result = MyHLTable::getList([
    'filter' => ['=UF_STATUS' => 'active', '>ID' => $lastId],
    'order'  => ['ID' => 'ASC'],
    'limit'  => 50,
]);

Це працює лише при монотонному порядку сортування по індексованому полю.

Кейс: зберігання логів замовлень у HL-блоку

Інтернет-магазин використовував HL-блок як лог подій замовлення: 3,2 млн записів, 8 полів. Запит історії конкретного замовлення (фільтр по UF_ORDER_ID) без індексу — 1,2 с. Додавання індексу по UF_ORDER_ID:

ALTER TABLE b_uts_order_log ADD INDEX idx_order_id (UF_ORDER_ID);

Час запиту: 1,2 с → 2 мс. Додатково: запити зведеної статистики за період (агрегація по UF_EVENT_TYPE за місяць) винесені в окремий кешований звіт, що оновлюється вночі — зняло навантаження на MySQL в бізнес-години.

Денормалізація та HL-блоки як довідники

Часто HL-блоки використовуються як довідники (типи оплати, статуси, регіони). При цьому в основних запитах до інфоблоку виконується JOIN з HL-блоком для отримання текстового значення. Правильне рішення — денормалізація: зберігати текстове значення безпосередньо в полі інфоблоку, оновлювати його при зміні довідника через агент. Це виключає JOIN у 95% запитів на читання.

Етапи оптимізації

Роботи Термін
Аудит запитів (slow query log, панель Бітрікс) 0,5–1 день
Додавання індексів 0,5 дня
Переробка кешування 1–3 дні
Рефакторинг схеми (денормалізація, cursor пагінація) 2–5 днів

Починати завжди з EXPLAIN — без нього неможливо зрозуміти, чи потрібен індекс і який саме.