Чому Highload-блоки працюють повільно?
Highload-блоки в Бітрікс — зручний інструмент для зберігання довідників, логів, налаштувань без ієрархії. Але їх продуктивність часто розчаровує. Ми, команда сертифікованих Бітрікс-розробників з 5+ років досвіду, регулярно стикаємося з проектами, де HL-блоки гальмують. Наш досвід показує: у 90% випадків проблема — відсутність індексів або неефективне кешування. Пропонуємо повний аудит і прискорення під ключ — результат гарантуємо.
Кожен HL-блок — окрема MySQL-таблиця з автогенерованим іменем виду b_uts_catalog_props або користувацьким іменем через налаштування. На відміну від інфоблоків, HL-блоки не мають складних JOIN-залежностей, і теоретично мають працювати швидко. На практиці повільні запити зустрічаються не рідше — через відсутність індексів, неправильне використання API і невдалу схему даних.
Як правильно індексувати HL-блоки?
При створенні 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 мс на запит.
Індекси створюються напряму через SQL або через міграції. Для прикладу вище:
ALTER TABLE b_uts_my_highload ADD INDEX idx_status_created (UF_STATUS, UF_CREATED_AT); | Тип індексу | Коли використовувати | Приклад |
|---|---|---|
| Простий | Одне поле в filter | INDEX (UF_STATUS) |
| Складений | Кілька полів в filter/order | INDEX (UF_STATUS, UF_CREATED_AT) |
| Покриваючий | Всі поля запиту включені | INDEX (UF_STATUS, UF_CREATED_AT, UF_NAME, UF_VALUE) |
Правила вибору індексів:
- Поля в
filter— кандидати для індексування, але порядок важливий: поле з меншою кардинальністю (наприклад,UF_STATUSз 3 значеннями) ставиться першим тільки якщо воно використовується в запитах самостійно; інакше складений індекс починається з висококардинального поля. - Поля в
orderповинні бути в індексі або покриваючому індексі. -
SELECT *замінюйте на явнийselectз потрібними полями — скорочує обсяг даних, іноді дозволяє використовувати covering index.
Кешування: як уникнути повторних запитів?
Highload-блоки не мають автоматичного тегованого кеша, як інфоблоки. При використанні 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 | Деградує при великих зміщеннях | Маленькі таблиці (<10 000 записів) |
| Курсорна (по ID) | Константний час | Великі таблиці, монотонне сортування |
Стандартна пагінація через 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, ]); Це працює тільки при монотонному порядку сортування за індексованим полем.
З нашої практики: кейс із логами замовлень
Один з наших клієнтів — інтернет-магазин з обігом понад 50 000 замовлень на місяць — використовував HL-блок як лог подій замовлення. У таблиці було 3,2 млн записів, 8 полів. Запит історії конкретного замовлення (фільтр по UF_ORDER_ID) без індексу виконувався 1,2 секунди. Ми додали індекс:
ALTER TABLE b_uts_order_log ADD INDEX idx_order_id (UF_ORDER_ID); Час запиту впав з 1,2 с до 2 мс. Додатково ми винесли зведену статистику за період в окремий кешований звіт, що оновлюється вночі — це зняло навантаження на MySQL у бізнес-години.
Що входить у роботу з оптимізації HL-блоків
- Аудит: аналіз slow query log, панелі продуктивності Бітрікс, EXPLAIN всіх важких запитів.
- Проектування: підбір індексів, налаштування кешування, рефакторинг запитів.
- Реалізація: створення індексів, впровадження ManagedCache, перехід на курсорну пагінацію.
- Тестування: вимірювання продуктивності до/після, перевірка на бойових даних.
- Документація: передача схеми індексів, інструкцій з підтримки.
Орієнтовні строки: від 3 до 10 днів залежно від обсягу даних та складності схеми.
Коли варто звернутися до фахівців?
Якщо ви помічаєте, що сторінки адмінки або каталогу завантажуються довше 2 секунд, база даних «тупить» при роботі з довідниками, або в slow query log постійно фігурують запити до HL-блоків — пора проводити аудит. Ми гарантуємо прискорення запитів мінімум у 10 разів. Зв'яжіться з нами, щоб отримати консультацію та попередню оцінку проекту.
Згідно з Wikipedia, правильно підібрані індекси прискорюють пошук по таблицях до кількох порядків. Наша команда підтверджує це на практиці.







