Highload-блоки в Битрикс — когда и зачем использовать

Разбираем highload-блоки: отличия от обычных инфоблоков, производительность, ограничения. Примеры использования для каталогов, логов, справочников.

Дмитрий Мещеряков
Дмитрий Мещеряков
📅 2 сентября 2026 г.📖 8 мин чтения

Highload-блоки окружены двумя противоположными мифами. Первый: «это волшебная таблетка, всё нужно перенести туда». Второй: «это неудобная штука без нормальных компонентов, лучше не связываться». Оба неверны, и оба ведут к плохим решениям.

Правда в том, что HL-блок — это просто плоская таблица MySQL с ORM-обёрткой и админкой. Всё, что вы про них знаете про обычные таблицы, применимо; всё, чем удобны инфоблоки, — отсутствует. Разберём, где эта сделка выгодна.

Что такое Highload-блоки

Highload-блоки (HL-блоки) — это альтернатива инфоблокам для хранения данных в Битрикс. Главное отличие: каждый HL-блок хранится в отдельной таблице MySQL с плоской структурой.

Инфоблоки:

  • Данные в общих таблицах b_iblock_element, b_iblock_element_property
  • EAV-модель (Entity-Attribute-Value) — свойства хранятся построчно
  • Универсальные, но медленные на больших объёмах

Highload-блоки:

  • Своя таблица для каждого блока
  • Каждое свойство — отдельная колонка
  • Быстрые, но менее гибкие
💡 Совет

Когда использовать HL-блоки: справочники (города, бренды), логи, временные данные, счётчики, любые данные с простой структурой и большим объёмом (100k+ записей).

Сравнение производительности

Разница в скорости реальна, но её постоянно преувеличивают, потому что сравнивают с худшим вариантом инфоблока. Разберём честно.

Инфоблок с первым вариантом хранения свойств держит все значения в общей таблице b_iblock_element_property — строка на каждое значение каждого свойства. Выборка с фильтром по нескольким свойствам разворачивается в несколько JOIN к таблице, где лежат значения всего сайта. Вот здесь HL-блок выигрывает в разы.

Инфоблок со вторым вариантом хранения (VERSION = 2) устроен принципиально иначе: у каждого инфоблока своя таблица b_iblock_element_prop_s{ID}, где каждое свойство — отдельная колонка. То есть структурно это почти то же самое, что HL-блок. Разница на выборках здесь уже не «в 10–50 раз», а в единицы десятков процентов — за счёт одного JOIN к b_iblock_element и меньшего количества служебных полей.

💡 Совет

Практический вывод. Прежде чем переносить инфоблок в HL-блок ради скорости, проверьте SELECT ID, VERSION FROM b_iblock и переключите хранилище на второй вариант, если стоит первый. Это делается в настройках инфоблока, занимает несколько минут и часто снимает вопрос производительности целиком — без переписывания кода и потери штатных компонентов.

Переезд на HL-блок оправдан не столько скоростью, сколько тем, что вам не нужны разделы, ЧПУ, поиск и права доступа, а нужны быстрая запись и простая структура.

Создание Highload-блока

Через админку

Админка → Контент → Highload-блоки → Добавить.

  • Название — человекочитаемое (Города)
  • Символьный код — для API (Cities)
  • Таблица — имя в БД (b_hlbd_cities)

Через код

php
<?php
use Bitrix\Highloadblock\HighloadBlockTable;

$result = HighloadBlockTable::add([
    'NAME' => 'Cities',
    'TABLE_NAME' => 'b_hlbd_cities',
]);
if ($result->isSuccess()) {
    $hlblockId = $result->getId();
    // Добавляем поля
    $userField = new \CUserTypeEntity();
    // Название города
    $userField->Add([
        'ENTITY_ID' => 'HLBLOCK_' . $hlblockId,
        'FIELD_NAME' => 'UF_NAME',
        'USER_TYPE_ID' => 'string',
        'MANDATORY' => 'Y',
        'EDIT_FORM_LABEL' => ['ru' => 'Название'],
        'LIST_COLUMN_LABEL' => ['ru' => 'Название'],
    ]);
    // Код региона
    $userField->Add([
        'ENTITY_ID' => 'HLBLOCK_' . $hlblockId,
        'FIELD_NAME' => 'UF_REGION_CODE',
        'USER_TYPE_ID' => 'string',
        'MANDATORY' => 'N',
    ]);
    // Население (число)
    $userField->Add([
        'ENTITY_ID' => 'HLBLOCK_' . $hlblockId,
        'FIELD_NAME' => 'UF_POPULATION',
        'USER_TYPE_ID' => 'integer',
        'MANDATORY' => 'N',
    ]);
}

Работа с данными (D7 API)

Получение класса сущности

💡 Совет

compileEntity() — не бесплатный вызов. Он собирает описание сущности и объявляет PHP-класс на лету. Делать это в цикле или в каждом методе репозитория — заметная лишняя работа. Получайте класс один раз и держите в статическом свойстве или в контейнере.

php
<?php
use Bitrix\Main\Loader;
use Bitrix\Highloadblock\HighloadBlockTable;
Loader::includeModule('highloadblock');
// По ID
$hlblock = HighloadBlockTable::getById(1)->fetch();
// По имени
$hlblock = HighloadBlockTable::getList([
    'filter' => ['NAME' => 'Cities']
])->fetch();
// Получаем класс для работы с данными
$entity = HighloadBlockTable::compileEntity($hlblock);
$entityClass = $entity->getDataClass();
// Теперь $entityClass — это полноценный DataManager
// Например: CityTable (автогенерируемое имя)

CRUD операции

php
<?php
// CREATE — добавление
$result = $entityClass::add([
    'UF_NAME' => 'Москва',
    'UF_REGION_CODE' => '77',
    'UF_POPULATION' => 12600000,
]);
if ($result->isSuccess()) {
    $id = $result->getId();
}
// READ — чтение
$cities = $entityClass::getList([
    'select' => ['ID', 'UF_NAME', 'UF_POPULATION'],
    'filter' => ['>=UF_POPULATION' => 1000000],
    'order' => ['UF_POPULATION' => 'DESC'],
    'limit' => 10,
])->fetchAll();
// Одна запись по ID
$city = $entityClass::getById(1)->fetch();
// UPDATE — обновление
$result = $entityClass::update($id, [
    'UF_POPULATION' => 12700000,
]);
// DELETE — удаление
$result = $entityClass::delete($id);

Фильтрация и сортировка

php
<?php
// Сложный фильтр
$cities = $entityClass::getList([
    'filter' => [
        'LOGIC' => 'OR',
        [
            '>=UF_POPULATION' => 1000000,
        ],
        [
            'UF_REGION_CODE' => '77', // Москва
        ],
    ],
    'order' => ['UF_NAME' => 'ASC'],
])->fetchAll();
// Подсчёт
$count = $entityClass::getCount([
    '>=UF_POPULATION' => 500000,
]);
// Группировка и агрегация
$stats = $entityClass::getList([
    'select' => [
        'UF_REGION_CODE',
        new \Bitrix\Main\Entity\ExpressionField('TOTAL', 'COUNT(*)'),
        new \Bitrix\Main\Entity\ExpressionField('AVG_POP', 'AVG(%s)', ['UF_POPULATION']),
    ],
    'group' => ['UF_REGION_CODE'],
])->fetchAll();

Практические примеры

Пример 1: Лог действий пользователей

php
<?php
// Создаём HL-блок UserActionsLog с полями:
// UF_USER_ID (integer)
// UF_ACTION (string)
// UF_DATA (text)
// UF_CREATED_AT (datetime)
class UserActionLogger
{
    private static $entityClass = null;
    public static function getEntityClass(): string
    {
        if (self::$entityClass === null) {
            $hlblock = HighloadBlockTable::getList([
                'filter' => ['NAME' => 'UserActionsLog']
            ])->fetch();
            $entity = HighloadBlockTable::compileEntity($hlblock);
            self::$entityClass = $entity->getDataClass();
        }
        return self::$entityClass;
    }
    public static function log(int $userId, string $action, array $data = []): void
    {
        $class = self::getEntityClass();
        $class::add([
            'UF_USER_ID' => $userId,
            'UF_ACTION' => $action,
            'UF_DATA' => json_encode($data, JSON_UNESCAPED_UNICODE),
            'UF_CREATED_AT' => new \Bitrix\Main\Type\DateTime(),
        ]);
    }
    public static function getUserActions(int $userId, int $limit = 100): array
    {
        $class = self::getEntityClass();
        return $class::getList([
            'filter' => ['UF_USER_ID' => $userId],
            'order' => ['UF_CREATED_AT' => 'DESC'],
            'limit' => $limit,
        ])->fetchAll();
    }
}
// Использование
UserActionLogger::log($USER->GetID(), 'order.create', ['order_id' => 12345]);

Пример 2: Справочник брендов для фильтра

php
<?php
// HL-блок Brands:
// UF_NAME, UF_CODE, UF_LOGO, UF_SORT, UF_ACTIVE
class BrandRepository
{
    private string $entityClass;
    public function __construct()
    {
        $hlblock = HighloadBlockTable::getList([
            'filter' => ['NAME' => 'Brands']
        ])->fetch();
        $entity = HighloadBlockTable::compileEntity($hlblock);
        $this->entityClass = $entity->getDataClass();
    }
    public function getActiveList(): array
    {
        $class = $this->entityClass;
        return $class::getList([
            'filter' => ['UF_ACTIVE' => true],
            'order' => ['UF_SORT' => 'ASC', 'UF_NAME' => 'ASC'],
            'cache' => ['ttl' => 3600, 'cache_joins' => true],
        ])->fetchAll();
    }
    public function getByCode(string $code): ?array
    {
        $class = $this->entityClass;
        return $class::getList([
            'filter' => ['UF_CODE' => $code, 'UF_ACTIVE' => true],
            'limit' => 1,
        ])->fetch() ?: null;
    }
}
⚠️ Важно

Ограничение: HL-блоки не поддерживают SEO-URL из коробки, множественные свойства работают через сериализацию, нет встроенных связей между блоками. Для сложных структур используйте инфоблоки.

Оптимизация

Индексы

Это тот пункт, где HL-блоки чаще всего разочаровывают: Битрикс не создаёт индексы за вас. Он создаёт колонку, и всё. Таблица на миллион записей с фильтром по неиндексированному полю ведёт себя ровно так, как ведёт себя любая таблица MySQL в этой ситуации, — и «быстрые HL-блоки» внезапно оказываются медленнее инфоблока, у которого нужные индексы ядро создало само.

sql
-- Добавляем индекс вручную
ALTER TABLE b_hlbd_cities ADD INDEX idx_region (UF_REGION_CODE);
ALTER TABLE b_hlbd_cities ADD INDEX idx_population (UF_POPULATION);
-- Составной индекс
ALTER TABLE b_hlbd_user_actions ADD INDEX idx_user_date (UF_USER_ID, UF_CREATED_AT);

Кэширование

php
<?php
// Встроенное кэширование D7
$items = $entityClass::getList([
    'filter' => [...],
    'cache' => [
        'ttl' => 3600, // 1 час
        'cache_joins' => true,
    ],
])->fetchAll();

// Или через Bitrix Cache
$cache = \Bitrix\Main\Data\Cache::createInstance();
if ($cache->initCache(3600, 'brands_list')) {
    $brands = $cache->getVars()['brands'];
} elseif ($cache->startDataCache()) {
    $brands = (new BrandRepository())->getActiveList();
    $cache->endDataCache(['brands' => $brands]);
}

Когда НЕ использовать HL-блоки

  • Нужен SEO URL (ЧПУ)
  • Сложная иерархия (разделы, подразделы)
  • Множественные свойства
  • Связи между элементами
  • Нужен поиск по контенту
  • Интеграция со стандартными компонентами Битрикс

К этому списку добавлю то, что обычно всплывает уже в процессе.

Права доступа. У инфоблока они есть на уровне ядра: группы, права на чтение и запись, ограничения по разделам. У HL-блока — только то, что вы напишете сами. Для справочника брендов это неважно, для данных, которые не должны видеть все, — критично.

Изменение структуры на живых данных. Добавление поля в HL-блок — это ALTER TABLE. На таблице в несколько миллионов строк операция блокирует запись и выполняется минутами. Инфоблок в этом смысле удобнее: новое свойство не трогает структуру таблиц.

Резервное копирование и перенос между окружениями. HL-блок — это запись в служебной таблице плюс отдельная таблица данных плюс набор пользовательских полей в b_user_field. Перенести его на другой сервер «просто выгрузив таблицу» не получится: без описания полей вы получите таблицу без сущности. Учитывайте это в миграциях.

Выборка без limit. ORM с радостью вернёт вам миллион записей в память. Для инфоблоков привычка ставить nTopCount выработана давно, для HL-блоков — почему-то нет.

Итоги

КритерийИнфоблокиHL-блоки
ПроизводительностьСредняяВысокая
ГибкостьВысокаяНизкая
SEOЕстьНет
РазделыЕстьНет
Множественные свойстваНативноЧерез сериализацию
Для справочниковНе рекомендуетсяИдеально
Для каталогаИдеальноНе рекомендуется

Правило выбора, к которому всё сводится: инфоблок — для контента, HL-блок — для данных.

Контент — это то, у чего есть страница, адрес, права доступа и место в структуре сайта: товары, статьи, разделы. Данные — это то, что читается и пишется кодом: справочники, логи, счётчики, промежуточные результаты интеграций. Инфоблок оплачивает свою универсальность производительностью, HL-блок оплачивает производительность отсутствием всего остального.

И два практических напоминания напоследок. Индексы в HL-блоках создаёте вы, а не Битрикс, — без них весь смысл теряется. А прежде чем переносить существующий инфоблок ради скорости, проверьте вариант хранения его свойств: возможно, задача решается галочкой в настройках вместо недели работы.

Частые вопросы

Что такое highload-блок в Битриксе?

Highload-блок — это плоская таблица MySQL с ORM-обёрткой и админкой: каждый блок живёт в отдельной таблице, а каждое свойство — отдельная колонка. Инфоблоки для сравнения хранят данные в общих таблицах b_iblock_element и b_iblock_element_property по EAV-модели, где каждое значение каждого свойства — отдельная строка.

Насколько HL-блоки быстрее инфоблоков?

Зависит от варианта хранения свойств инфоблока. Против 1-го варианта, где все значения лежат в общей таблице b_iblock_element_property и выборка разворачивается в несколько JOIN, HL-блок выигрывает в разы. Против 2-го варианта, где у каждого инфоблока своя таблица с колонками, разница уже единицы десятков процентов — структурно это почти то же самое.

Стоит ли переносить инфоблок в HL-блок ради скорости?

Сначала проверьте вариант хранения запросом SELECT ID, VERSION FROM b_iblock. Если стоит первый, переключите на второй в настройках инфоблока — это несколько минут и часто снимает вопрос производительности целиком, без переписывания кода и потери штатных компонентов. Переезд на HL-блок оправдан не столько скоростью, сколько тем, что вам не нужны разделы, ЧПУ, поиск и права доступа, а нужны быстрая запись и простая структура.

Создаёт ли Битрикс индексы в HL-блоках автоматически?

Нет. Битрикс создаёт колонку, и всё. Таблица на миллион записей с фильтром по неиндексированному полю ведёт себя ровно так, как любая таблица MySQL в этой ситуации, — и «быстрые HL-блоки» внезапно оказываются медленнее инфоблока, у которого нужные индексы ядро создало само. Индексы добавляют вручную через ALTER TABLE, в том числе составные.

Когда HL-блоки использовать не стоит?

Когда нужны ЧПУ, иерархия разделов, множественные свойства, связи между элементами, поиск по контенту или интеграция со штатными компонентами. К этому добавьте права доступа (у HL-блока их нет на уровне ядра, только то, что напишете сами), изменение структуры на живых данных (добавление поля — это ALTER TABLE, который на миллионах строк блокирует запись минутами) и перенос между окружениями: HL-блок — это запись в служебной таблице плюс таблица данных плюс набор полей в b_user_field, выгрузкой одной таблицы его не перенести.

Что выбрать: инфоблок или highload-блок?

Инфоблок — для контента, HL-блок — для данных. Контент — это то, у чего есть страница, адрес, права доступа и место в структуре сайта: товары, статьи, разделы. Данные — это то, что читается и пишется кодом: справочники, логи, счётчики, промежуточные результаты интеграций. Инфоблок оплачивает свою универсальность производительностью, HL-блок оплачивает производительность отсутствием всего остального.