До 10%Рекомендуйте нас и получайте процент за каждого приведённого клиента

Highload-блоки в 1С-Битрикс: большие справочники и характеристики

Highload-блоки в 1С-Битрикс: большие справочники характеристик, привязка к товарам, умный фильтр и ORM D7

Каталог растёт, и в нём появляется «бренд», записанный строкой у каждого из двадцати тысяч товаров: где-то «BMW», где-то «Бмв», где-то «bmw» с лишним пробелом. Фильтр по бренду тормозит, единого списка нет, а правка названия превращается в массовое обновление тысяч строк. Ровно эту боль в 1С-Битрикс закрывает highload-блок — отдельная быстрая таблица под один большой справочник, к которой товары обращаются по ссылке, а не хранят значение у себя.

Ниже — системный разбор highload-блоков: чем highload блок битрикс отличается от инфоблока, как устроено хранение, когда справочник стоит выносить в highloadblock, как привязать его к товарам свойством «Привязка к элементам highload-блока», как это работает в умном фильтре, и как читать и писать данные через ORM D7 (HighloadBlockTable, getEntityDataClass, getList/add/update). Это выжимка из наших проектов по разработке highload-проектов на 1С-Битрикс — без теории ради теории.

Коротко

  • Highload-блок — это отдельная таблица в базе под один большой справочник: каждое поле — колонка, никаких служебных таблиц свойств.
  • Он нужен для больших единых справочников: бренды, характеристики, цвета, города, VIN, справочники совместимости — десятки и сотни тысяч записей.
  • К товарам справочник привязывают свойством «Привязка к элементам highload-блока» — значение хранится один раз, товары ссылаются на него.
  • Доступ к данным — через ORM D7: HighloadBlockTable::compileEntity()->getDataClass(), затем getList/add/update.
  • Скорость дают индексы на поля фильтрации и теговое кэширование выборок; маленькому справочнику highload-блок не нужен.

Зачем магазину highload-блоки

Инфоблоки — универсальный инструмент Битрикса: в них лежат товары, новости, разделы, посадочные. Универсальность удобна, пока данных немного. Но у инфоблоков есть цена за гибкость — структура хранения свойств. Значения свойств элементов лежат не в самом элементе, а в общих служебных таблицах, и на больших объёмах выборка по свойству превращается в тяжёлый запрос с джойнами. Пока в справочнике десяток значений, это незаметно. Когда справочник разрастается до десятков тысяч записей и к нему идут частые обращения — фильтр, автодополнение, вывод плашек, — инфоблок начинает буксовать.

Highload-блок создавали именно под этот случай: большой справочник, к которому часто обращаются. Вместо универсальной обвязки он даёт отдельную физическую таблицу, где каждое поле — колонка базы данных, а выборка по значению — простой индексируемый запрос. За это платят отказом от части возможностей инфоблока (разделы, привязки между элементами, богатый интерфейс), но для плоского справочника они и не нужны. Highload-блок решает три задачи сразу:

Обратная сторона — highload-блок не бесплатен по сложности. Это не «свойство, которое ставится галочкой»: под него заводят отдельную сущность, поля, привязку к товарам, часто индексы и код на ORM D7. Поэтому всё искусство здесь — понять, где справочник действительно большой и общий (и highload-блок окупается), а где хватит обычного свойства инфоблока. К этой границе мы вернёмся в разделе «Когда highload-блок НЕ нужен».

Ключевая идея: справочник отдельно от товара

Главную мысль стоит закрепить сразу. В плохой схеме характеристика живёт «внутри» товара — строкой в свойстве. В хорошей схеме характеристика живёт в отдельном справочнике, а товар лишь ссылается на неё. Это классическая нормализация: одно значение — одна запись, много ссылок. Highload-блок — это техническая реализация такого «отдельного справочника» в Битриксе, оптимизированная под большой объём и частое чтение. Всё остальное в статье — детали того, как это завести и как этим пользоваться.

Что такое highload-блок простыми словами

Технически highload-блок (highloadblock, «хайлоад-блок») — это описанная в Битриксе отдельная таблица базы данных плюс ORM-сущность D7 поверх неё. Вы задаёте имя таблицы и имя сущности, добавляете пользовательские поля (они же станут колонками), и модуль highloadblock создаёт таблицу и даёт к ней объектный доступ через ядро D7. С этого момента справочник живёт как самостоятельная таблица, к которой можно обращаться и из административного интерфейса, и из кода.

Важно, что highload-блок — это именно справочник, а не замена каталогу товаров. У него нет разделов, нет древовидной структуры, нет привязки к другим элементам, нет сложных прав на отдельные записи. Есть плоский список записей, у каждой — набор полей. Такой минимализм и есть источник его скорости: чем проще структура, тем быстрее и предсказуемее запросы. Highload-блок хорош ровно там, где нужен большой плоский список значений с быстрым доступом.

Название вводит в заблуждение. «Highload» в имени — не про то, что блок сам по себе «выдерживает высокие нагрузки волшебным образом». Это про то, что он спроектирован под большие объёмы данных и частые выборки за счёт отдельной таблицы. Настоящую нагрузку всё равно держат индексы, кэш и грамотные запросы — highload-блок лишь даёт для этого удобную основу.

Highload-блок против инфоблока: в чём разница

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

КритерийИнфоблокHighload-блок
НазначениеКонтент: товары, новости, разделыБольшой плоский справочник
Хранение свойствСлужебные таблицы свойствОтдельная таблица, поле = колонка
Разделы и деревоЕстьНет
Привязка к элементамЕстьНет (сам может быть целью привязки)
Права на записиГибкиеМинимальные
Объём без просадкиХорош до умеренных объёмов свойствДесятки и сотни тысяч записей
Доступ из кодаCIBlockElement, ORM D7ORM D7 (HighloadBlockTable)
Типичное применениеКаталог товаров, контент-разделыБренды, цвета, города, VIN, совместимость

Читать таблицу стоит так: если сущности нужны разделы, привязки, права и богатый интерфейс — это инфоблок. Если нужен большой единый список значений с быстрым доступом и он часто участвует в выборках и фильтрах — это highload-блок. Они не конкурируют, а дополняют друг друга: товары остаются в инфоблоке, а их «словарные» характеристики выносятся в highload-справочники и подключаются к товарам ссылкой.

Как хранятся данные: инфоблок и highload-блок Инфоблок Таблица элементов Свойства Значения Служебные связи (джойны) гибко, но много таблиц и связей Highload-блок Одна таблица справочника UF_XML_ID UF_NAME UF_COUNTRY поле = колонка, быстрый индексируемый запрос
Схема: инфоблок раскладывает значения свойств по служебным таблицам со связями, а highload-блок держит весь справочник в одной таблице, где каждое поле — отдельная колонка.

Как устроено хранение: отдельная таблица

Разберём физику хранения подробнее — именно она объясняет, откуда берётся скорость. Когда вы создаёте highload-блок, вы задаёте имя таблицы (например, b_hl_brand) и имя ORM-сущности (например, Brand). Модуль создаёт в базе реальную таблицу с этим именем. Каждое пользовательское поле, которое вы добавляете (UF_NAME, UF_XML_ID, UF_COUNTRY), становится колонкой этой таблицы. Каждая запись справочника — строка таблицы.

Сравните это с инфоблоком, где значение свойства «Бренд» у товара хранится в общей таблице значений свойств вместе со значениями всех остальных свойств всех остальных товаров. Чтобы выбрать товары определённого бренда, ядру приходится джойнить таблицу элементов с таблицей значений свойств и фильтровать по идентификатору свойства. На больших объёмах это дорого. В highload-блоке выборка «все бренды из Германии» — это SELECT ... WHERE UF_COUNTRY = 'Германия' по одной таблице с индексом по колонке. Разница в стоимости запроса и определяет, почему большой справочник живёт в highload-блоке.

Есть и следствие для дисциплины: поскольку это реальная таблица, к ней применимы все обычные правила проектирования БД — типы колонок, индексы, уникальность. Highload-блок не прячет базу за абстракцией «свойств», а показывает её почти напрямую. Это и ответственность (нужно думать про индексы), и возможность (можно строить действительно быстрые справочники).

Когда нужен highload-блок: сценарии

Теория обретает смысл на конкретных задачах. Вот типовые сценарии, где highload-блок — правильный инструмент, встречающиеся в реальных магазинах и порталах.

Общий признак у всех сценариев один: справочник большой, единый и часто читается. Как только вы ловите себя на мысли «это значение повторяется у тысяч товаров, и я хочу хранить его один раз и быстро искать» — это кандидат в highload-блок. Как справочники характеристик работают в связке с торговыми предложениями (SKU), мы разбираем в материале про торговые предложения и SKU в 1С-Битрикс.

Создание highload-блока по шагам

Завести highload-блок можно из административного интерфейса (раздел работы с highload-блоками) или из кода. Логически процесс один и тот же: описать блок, добавить поля, наполнить данными. Разберём порядок.

  1. Создать highload-блок. Задаёте имя ORM-сущности (латиницей, с заглавной — например, Brand) и имя таблицы в базе (например, b_hl_brand). Модуль highloadblock создаёт таблицу.
  2. Добавить пользовательские поля. Каждое поле (UF_NAME, UF_XML_ID, UF_COUNTRY, UF_SORT) — это будущая колонка. Указываете тип (строка, число, список, файл, привязка) и настройки.
  3. Настроить обязательность и уникальность. Ключевые поля вроде UF_XML_ID помечаете обязательными и уникальными — это внешний ключ, по которому справочник сопоставляется с обменом.
  4. Наполнить справочник. Записи заводят вручную через интерфейс, импортом или кодом через ORM. Для большого справочника — обычно скриптом или из обмена.
  5. Проставить индексы. На поля фильтрации и поиска (UF_XML_ID, коды, внешние ключи) добавляете индексы в таблице — без них большая таблица будет сканироваться целиком.
  6. Подключить к товарам. Создаёте у товара свойство «Привязка к элементам highload-блока», указывающее на этот справочник. С этого момента товары могут ссылаться на записи.

Создание из кода полезно, когда highload-блок должен появляться при установке модуля или разворачиваться миграцией на нескольких средах (dev, stage, prod). Тогда описание блока и полей задают программно, чтобы структура воспроизводилась одинаково везде. Ниже — как это выглядит через ORM.

Пользовательские поля справочника

Поля highload-блока — это те же пользовательские поля (user fields, UF), что и в остальном Битриксе, поэтому их имена начинаются с префикса UF_. Набор типов богатый, и от выбора типа зависит и хранение, и удобство работы. Разберём, что обычно кладут в справочник.

ПолеТипРоль в справочнике
UF_XML_IDСтрока (уникальная)Внешний ключ / код записи для обмена и привязки
UF_NAMEСтрокаОтображаемое название (бренд, цвет, город)
UF_SORTЧислоПорядок вывода в списках и фильтре
UF_FILEФайлЛоготип бренда, изображение, иконка
UF_HEXСтрокаHEX-код для цветовой плашки в фильтре
UF_ACTIVEДа/НетПоказывать запись или скрыть
UF_DEFЧисло/строкаДополнительные атрибуты (страна, регион, вес)

Практический принцип — держать в справочнике всё, что относится к самому значению, а не к товару. Логотип и страна бренда относятся к бренду, поэтому живут в справочнике; а вот «дата поступления конкретного товара этого бренда» относится к товару и в справочнике ей не место. Ещё один важный момент — UF_XML_ID: это стабильный внешний ключ записи. Именно его чаще всего хранят у товара как ссылку и именно по нему справочник сопоставляется с данными из учётной системы. Поэтому он обязателен, уникален и не должен меняться после создания записи.

UF_XML_ID — якорь справочника. Название (UF_NAME) может меняться (переименовали бренд), а вот код (UF_XML_ID) должен оставаться стабильным: на него ссылаются товары и по нему идёт сопоставление при обмене. Меняете код записи — рвёте все привязки. Проектируйте UF_XML_ID как неизменяемый идентификатор.

Привязка справочника к товарам

Справочник сам по себе бесполезен, пока товары на него не ссылаются. Связь делают через специальный тип свойства инфоблока — «Привязка к элементам highload-блока» (в некоторых версиях интерфейса — «Справочник» / directory). Создав такое свойство у товара и указав, на какой highload-блок оно ссылается, вы получаете у товара выпадающий список значений прямо из справочника.

Что при этом хранится в базе: у товара в значении свойства лежит не название бренда строкой, а ссылка на запись справочника — как правило, её UF_XML_ID. Поэтому тысячи товаров одного бренда ссылаются на одну запись. Правка названия бренда — это изменение одной строки в справочнике; у товаров ничего трогать не нужно, они продолжают ссылаться на ту же запись по её коду. Это и есть выигрыш нормализации: одно значение — одна запись — много ссылок.

  1. Создать highload-блок-справочник с полями UF_XML_ID и UF_NAME (см. предыдущие разделы).
  2. Добавить свойство товара типа «Привязка к элементам highload-блока» и выбрать в его настройках нужный справочник.
  3. Настроить отображаемое поле. Указать, какое поле справочника показывать в списке (обычно UF_NAME) и какое использовать как значение (обычно UF_XML_ID).
  4. Проставить значения товарам — вручную, импортом или в обмене с 1С по коду.
  5. Вывести в каталоге — в карточке и списке, при необходимости подтягивая доп. поля справочника (логотип, HEX-код).
Одна запись справочника — много ссылок из товаров HL-справочник «Бренды» UF_XML_ID = bmw UF_NAME = BMW Товар №1 → бренд: bmw Товар №2 → бренд: bmw Товар №3 → бренд: bmw свойство «Привязка к элементам highload-блока» хранит код записи
Схема: значение бренда хранится один раз в highload-справочнике, а множество товаров ссылается на него по коду UF_XML_ID через свойство привязки.

Highload-справочник в умном фильтре

Одна из главных причин выносить справочник в highload-блок — участие в умном фильтре каталога. Свойство товара типа «Привязка к элементам highload-блока» включается в фильтр так же, как обычное свойство: в настройках компонента фильтра его отмечают, и в интерфейсе появляется набор значений из справочника — бренды, цвета, материалы.

Работает это в связке двух механизмов. Highload-справочник — источник значений и их атрибутов: он отдаёт список брендов, а для цветов ещё и HEX-код, чтобы фильтр рисовал цветные плашки вместо текста. А быстрый отклик самого фильтра обеспечивает фасетный индекс — заранее посчитанная таблица соответствий «значение → товары», по которой фильтр мгновенно понимает, сколько товаров под каждой галочкой и какие показать. Как устроен фасет и почему он ускоряет фильтрацию, мы подробно разбираем в материале про умный фильтр и фасетный индекс в 1С-Битрикс.

Практическое следствие: после массовых изменений (обмен с 1С, пакетная правка справочника) фасетный индекс нужно перестроить, иначе фильтр покажет неактуальные наборы значений и неверные счётчики. Это та же дисциплина, что и с кэшем: изменение данных → перестроение индекса → прогрев. Highload-блок, фасет и кэш работают в одной цепочке, и рвётся она обычно на забытом перестроении.

HL-справочник в умном фильтре HL-справочник Цвета: Красный (#E23) Синий (#25F) Чёрный (#111) значения + HEX для плашек Фасетный индекс значение → товары счётчики заранее Умный фильтр Красный (42) Синий (31) Чёрный (58) после обмена и правок справочника фасет перестраивают
Схема: highload-справочник отдаёт значения и их атрибуты (HEX-код), фасетный индекс сопоставляет значения товарам и считает счётчики, а пользователь видит готовые плашки в умном фильтре.

ORM D7: HighloadBlockTable и getEntityDataClass

Программный доступ к highload-блоку идёт через ORM D7 — объектный слой ядра Битрикса. Ключевой класс — \Bitrix\Highloadblock\HighloadBlockTable. Он описывает сами highload-блоки (их список), но данные конкретного справочника читаются не через него напрямую, а через скомпилированную ORM-сущность. Порядок такой: подключить модуль, получить описание блока, скомпилировать сущность, взять класс данных.

use Bitrix\Main\Loader;
use Bitrix\Highloadblock\HighloadBlockTable;

Loader::includeModule('highloadblock');

// 1. Описание highload-блока по ID (или по имени таблицы через getList с фильтром)
$hlblock = HighloadBlockTable::getById(HL_BRAND_ID)->fetch();

// 2. Компилируем ORM-сущность справочника
$entity = HighloadBlockTable::compileEntity($hlblock);

// 3. Получаем класс данных — дальше он работает как обычная ORM-таблица D7
$dataClass = $entity->getDataClass();

Эти три шага повторяются в каждом месте, где нужны данные справочника, поэтому их удобно вынести в собственный хелпер. Так и появляется функция вида getEntityDataClass(), которую многие держат в проекте, чтобы одной строкой получать класс данных по идентификатору highload-блока.

function getEntityDataClass($hlId)
{
    static $cache = [];
    if (isset($cache[$hlId])) {
        return $cache[$hlId];        // не компилируем сущность повторно
    }

    \Bitrix\Main\Loader::includeModule('highloadblock');
    $hlblock = \Bitrix\Highloadblock\HighloadBlockTable::getById($hlId)->fetch();
    if (!$hlblock) {
        return null;
    }

    $entity = \Bitrix\Highloadblock\HighloadBlockTable::compileEntity($hlblock);
    return $cache[$hlId] = $entity->getDataClass();
}

// использование:
$brandClass = getEntityDataClass(HL_BRAND_ID);
Не компилируйте сущность в цикле. compileEntity() — операция не бесплатная. Если вызвать её на каждой итерации при обработке тысяч товаров, вы получите на ровном месте тяжёлую нагрузку. Компилируйте класс данных один раз (например, через статический кэш в хелпере) и переиспользуйте — как в примере выше.
ORM D7: путь к данным справочника includeModule highloadblock getById() описание блока compileEntity() ORM-сущность getDataClass() класс данных $dataClass работает как обычная ORM-таблица getList() add() update() / delete()
Схема: путь к данным highload-блока в D7 — подключить модуль, получить описание блока, скомпилировать сущность, взять класс данных; дальше это обычная ORM-таблица с методами getList, add, update.

Чтение данных: getList, фильтры, выборка

Как только у вас есть класс данных, чтение справочника — это стандартный ORM-запрос D7 через статический метод getList(). Он принимает массив параметров: что выбрать (select), по какому условию (filter), в каком порядке (order), сколько (limit/offset). Результат перебирают через fetch() или собирают массивом через fetchAll().

$brandClass = getEntityDataClass(HL_BRAND_ID);

$result = $brandClass::getList([
    'select' => ['ID', 'UF_XML_ID', 'UF_NAME', 'UF_COUNTRY'],
    'filter' => ['=UF_ACTIVE' => 1, '=UF_COUNTRY' => 'Германия'],
    'order'  => ['UF_SORT' => 'ASC', 'UF_NAME' => 'ASC'],
    'limit'  => 50,
]);

while ($row = $result->fetch()) {
    echo $row['UF_NAME'] . ' — ' . $row['UF_COUNTRY'] . "\n";
}

// Разом в массив, ключами по коду записи:
$brands = [];
foreach ($brandClass::getList(['select' => ['UF_XML_ID', 'UF_NAME']])->fetchAll() as $r) {
    $brands[$r['UF_XML_ID']] = $r['UF_NAME'];
}

Несколько практических правил. Всегда указывайте select явно и берите только нужные поля — «выбрать всё» на большой таблице лишнее. В filter используйте операторы-префиксы (= — точное равенство, % — like, >, <, @ — вхождение в массив), они превращаются в условия SQL. И помните, что фильтр по неиндексированной колонке на большой таблице приведёт к полному сканированию — про индексы будет отдельный раздел.

Частый паттерн для каталога — предзагрузить справочник в ассоциативный массив по коду (как во втором примере), а затем сопоставлять товарам их значения по коду из свойства привязки. Это одна выборка справочника на страницу вместо запроса на каждый товар, что снимает «эффект N+1». Такую предзагрузку логично ещё и закэшировать — об этом ниже.

Запись данных: add, update, delete

Запись в highload-блок — такой же ORM D7, только методами add(), update() и delete(). Каждый возвращает объект результата, у которого нужно проверять isSuccess() и при необходимости читать ошибки — без этой проверки легко «молча» потерять запись.

$brandClass = getEntityDataClass(HL_BRAND_ID);

// Добавление записи
$addResult = $brandClass::add([
    'UF_XML_ID'  => 'bmw',
    'UF_NAME'    => 'BMW',
    'UF_COUNTRY' => 'Германия',
    'UF_SORT'    => 100,
    'UF_ACTIVE'  => 1,
]);

if ($addResult->isSuccess()) {
    $newId = $addResult->getId();
} else {
    $errors = $addResult->getErrorMessages();   // массив ошибок
}

// Обновление по первичному ключу (ID)
$updResult = $brandClass::update($newId, [
    'UF_COUNTRY' => 'Germany',
]);

// Удаление
$brandClass::delete($newId);

Для наполнения справочника из обмена типичен паттерн «найти по коду — обновить или добавить» (upsert): по UF_XML_ID ищем запись, если есть — update, если нет — add. Так справочник синхронизируется с источником без дублей. Именно поэтому UF_XML_ID делают уникальным: он служит ключом сопоставления.

// Upsert записи справочника по внешнему коду
function upsertBrand(array $data)
{
    $brandClass = getEntityDataClass(HL_BRAND_ID);

    $existing = $brandClass::getList([
        'select' => ['ID'],
        'filter' => ['=UF_XML_ID' => $data['UF_XML_ID']],
        'limit'  => 1,
    ])->fetch();

    if ($existing) {
        return $brandClass::update($existing['ID'], $data);
    }
    return $brandClass::add($data);
}
Всегда проверяйте isSuccess(). ORM не бросает исключение при неудачной записи — она возвращает объект результата с ошибками. Если не проверить isSuccess(), запись «не пройдёт» тихо: например, из-за нарушения уникальности UF_XML_ID или пропущенного обязательного поля. При массовой загрузке это оборачивается частично залитым справочником, который потом трудно чинить.

Индексы и производительность

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

Правило простое: на каждое поле, по которому вы фильтруете, ищете или джойните, должен быть индекс. В первую очередь это UF_XML_ID (по нему идёт сопоставление и привязка), затем — коды, внешние ключи, поля совместимости, любые колонки в filter. Поле UF_XML_ID, помеченное уникальным, обычно уже получает уникальный индекс; остальные индексы добавляют вручную на таблицу.

СитуацияБез индексаС индексом
Поиск записи по UF_XML_IDПолное сканирование таблицыБыстрый точечный доступ
Фильтр каталога по справочникуТормозит на большом объёмеДержит десятки тысяч записей
Upsert при обменеКаждый поиск — скан таблицыСинхронизация идёт быстро
Джойн справочника совместимостиТяжёлые запросыПредсказуемое время отклика

Дополнительно к индексам работает дисциплина выборок: не тянуть лишние поля в select, не делать запрос на каждый товар в цикле (предзагружать справочник разом), не компилировать сущность повторно. И не забывайте, что таблица highload-блока — обычная таблица базы: под неё применимы все приёмы оптимизации SQL, включая анализ плана запроса. Если справочник большой, а фильтр по нему тормозит — почти всегда причина в отсутствующем индексе. Целостно вопросы скорости каталога и базы мы разбираем в услуге ускорения интернет-магазина на 1С-Битрикс.

Кэширование выборок из справочника

Справочники читаются часто — на каждой странице каталога, где выводятся бренды, цвета, города. При этом их данные меняются редко. Это идеальный профиль для кэширования: собрать список значений один раз, отдавать из памяти, сбрасывать только при обновлении справочника. Кэшируют highload-выборки через тот же D7-механизм Cache и TaggedCache, что и остальной код.

use Bitrix\Main\Data\Cache;
use Bitrix\Main\Application;

function getCachedBrands()
{
    $cache = Cache::createInstance();
    $cacheId = 'hl_brands_all';
    $cacheDir = '/hl/brands';
    $taggedCache = Application::getInstance()->getTaggedCache();

    if ($cache->initCache(86400, $cacheId, $cacheDir)) {
        return $cache->getVars();
    }

    if ($cache->startDataCache()) {
        $taggedCache->startTagCache($cacheDir);

        $brandClass = getEntityDataClass(HL_BRAND_ID);
        $brands = $brandClass::getList([
            'select' => ['UF_XML_ID', 'UF_NAME', 'UF_COUNTRY'],
            'order'  => ['UF_SORT' => 'ASC'],
        ])->fetchAll();

        // Собственный тег справочника — сбросим его при правке записей
        $taggedCache->registerTag('hl_brand_ref');
        $taggedCache->endTagCache();

        $cache->endDataCache($brands);
        return $brands;
    }
    return [];
}

Дальше нужно сбрасывать этот кэш при изменении справочника. Поскольку у highload-блоков нет автоматических тегов инфоблоков, тег регистрируют и сбрасывают вручную: после add/update/delete записей вызывают сброс по своему тегу. Удобно повесить это на событие изменения highload-блока или делать явно в коде, который правит справочник.

// После изменения записей справочника сбрасываем его кэш
Application::getInstance()->getTaggedCache()->clearByTag('hl_brand_ref');

Так highload-справочник ведёт себя как штатный кэшируемый источник: пока он не менялся — отдаётся из памяти сколь угодно долго, а при первой же правке инвалидируется по тегу. Про уровни кэша, теги и хранилище в памяти — отдельный подробный материал про кэширование в 1С-Битрикс; highload-выборки встраиваются в ту же систему.

Миграция строковых свойств в справочник

Классическая задача рефакторинга каталога — перенести «строковую» характеристику в highload-справочник. Симптом знакомый: бренд (или материал, страна, цвет) записан текстом в свойстве у тысяч товаров, значения дублируются с разнобоем в написании, единого списка нет, фильтр тормозит, а переименование — это массовое обновление. Миграция превращает этот хаос в нормализованный справочник.

  1. Собрать уникальные значения. Выбрать все различные значения строкового свойства по каталогу — это черновик будущего справочника.
  2. Нормализовать. Свести дубли и разнобой («BMW», «Бмв», «bmw ») к единым каноническим записям, задать код (UF_XML_ID) каждой.
  3. Создать highload-блок и залить туда нормализованный справочник через ORM (add/upsert).
  4. Добавить свойство-привязку у товара типа «Привязка к элементам highload-блока», указывающее на новый справочник.
  5. Сопоставить товары. Пройти по товарам, по старому строковому значению найти запись справочника и проставить ссылку в новое свойство.
  6. Переключить вывод и фильтр на новое свойство: карточку, список, умный фильтр; перестроить фасетный индекс.
  7. Проверить и отключить старое. Убедиться, что все товары сопоставлены (нет «осиротевших»), затем отключить старое строковое свойство.

Отдельного внимания требует шаг сопоставления: почти всегда находятся товары со значением, которого нет в справочнике (опечатка, редкий вариант). Их нельзя терять — по ним составляют отчёт и разбирают вручную или правилами. Миграцию удобно делать скриптом на ORM, прогоняя её сначала на копии базы, а на боевой — в часы низкой нагрузки с последующим перестроением фасета и сбросом кэша. Результат: единый справочник, дешёвая правка названий, быстрый фильтр и порядок в данных.

Не удаляйте старое свойство сразу. Пока не убедились, что все товары корректно сопоставлены и вывод переключён, старое строковое свойство лучше отключить, а не удалять. Так остаётся путь отката и источник для разбора «осиротевших» товаров. Удаляют его последним шагом, когда новая схема отработала на витрине.

Highload-блоки и большие каталоги

На большом каталоге highload-блоки — не «фишка», а часть архитектуры. Когда товаров десятки и сотни тысяч, а характеристик у каждого много, разница между «справочник строкой в свойстве» и «справочник в highload-блоке» становится разницей между тормозящим и быстрым каталогом. Правильное разделение данных выглядит так: товары и их торговые предложения — в инфоблоках, а «словарные» характеристики (бренды, цвета, материалы, совместимость) — в highload-справочниках, подключённых к товарам ссылками.

Эта схема хорошо масштабируется, потому что справочники читаются из отдельных индексированных таблиц и кэшируются, а не пересчитываются из тяжёлых свойств инфоблока на каждый запрос. Она же упрощает обмен: справочник синхронизируется по коду отдельно от товаров, и добавление нового бренда не трогает тысячи товарных карточек. Как торговые предложения (SKU) сочетаются с характеристиками из справочников, мы разбираем в материале про торговые предложения и SKU в 1С-Битрикс, а быстрый фильтр по ним — в статье про умный фильтр.

Если проект изначально строится вокруг больших объёмов и высоких нагрузок, highload-справочники, индексы, ORM D7 и кэш стоит закладывать в архитектуру с самого начала, а не прикручивать потом. Именно так мы проектируем highload-проекты на Битриксе: с запасом по объёму данных и по скорости отклика. Другие материалы по производительности и архитектуре собраны в рубрике «Разработка и архитектура».

Когда highload-блок НЕ нужен

Highload-блок — мощный инструмент, но не универсальный. Его частая ошибка применения — ставить туда всё подряд «на будущее», усложняя проект без выгоды. Есть ситуации, где highload-блок избыточен и лучше обойтись обычным свойством инфоблока.

Ориентир для решения держите простым. Задайте три вопроса: справочник большой? он единый на весь каталог? к нему идут частые выборки и фильтрация? Если на все три ответ «да» — highload-блок оправдан. Если хотя бы на два «нет» — почти наверняка достаточно свойства инфоблока, и не стоит плодить сущности. Правильная архитектура — это не «highload везде», а «highload там, где он реально окупается».

Highload-блоки и обмен с 1С

Справочники редко живут только на сайте — чаще они приходят из учётной системы. Бренды, характеристики, единицы измерения нередко ведутся в 1С и должны синхронизироваться с highload-блоками магазина. Здесь работает всё, о чём мы говорили: сопоставление по коду (UF_XML_ID), upsert, проверка isSuccess(), а после загрузки — сброс кэша справочника и перестроение фасета.

Практический сценарий такой. Обмен приносит актуальный список значений справочника по их кодам. Скрипт синхронизации проходит по нему upsert-ом: обновляет существующие записи и добавляет новые, при необходимости деактивируя пропавшие. После завершения — сброс тега кэша справочника и, если он участвует в фильтре, перестроение фасетного индекса. Затем прогрев ключевых страниц каталога. Это та же дисциплина «обмен → инвалидация → перестроение → прогрев», что и с товарами. Как устроен обмен и как он влияет на витрину, подробно разобрано в материалах рубрики про интеграции и в статье про кэширование в 1С-Битрикс.

Код записи — общий язык с 1С. Чтобы обмен корректно сопоставлял записи справочника, код (UF_XML_ID) на сайте и в учётной системе должен совпадать и быть стабильным. Если 1С шлёт свой GUID, храните именно его в UF_XML_ID. Тогда синхронизация идёт по ключу без дублей и «потерянных» значений.

Частые ошибки при работе с HL-блоками

Ошибки с highload-блоками повторяются из проекта в проект. Зная их заранее, вы сэкономите часы отладки и не наступите на грабли масштаба.

Чек-лист по highload-блокам

Короткий список, по которому удобно свериться при проектировании и ревью справочников каталога:

Вывод

Highload-блок в 1С-Битрикс — это отдельная быстрая таблица под один большой справочник: бренды, характеристики, цвета, города, VIN, совместимость. Его сила не в магии, а в архитектуре: данные лежат в собственной индексированной таблице, поле — это колонка, выборка — простой запрос без джойнов служебных таблиц свойств. За счёт этого справочник на десятки и сотни тысяч записей отдаётся быстро, хранится один раз и правится в одном месте, а тысячи товаров лишь ссылаются на него через свойство «Привязка к элементам highload-блока».

Практический порядок такой: убедиться, что справочник большой и общий (иначе хватит свойства инфоблока), завести highload-блок с кодом UF_XML_ID и индексами, привязать к товарам свойством-привязкой, включить в умный фильтр с перестроением фасета, читать и писать через ORM D7 с единожды скомпилированной сущностью, кэшировать частые выборки с тегом и синхронизировать с 1С по коду. Сделайте это по порядку — и большой каталог будет быстрым, а данные — чистыми и непротиворечивыми. Если проект строится вокруг больших объёмов, highload-архитектуру стоит закладывать сразу — это часть highload-проекта на 1С-Битрикс и грамотно спроектированного каталога товаров, а не «оптимизация на потом».

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

Чем highload-блок отличается от инфоблока в 1С-Битрикс?

Инфоблок — универсальное хранилище контента (товары, новости, разделы) с богатой обвязкой: свойства, разделы, права доступа, привязка к элементам, интерфейс редактирования. Но за универсальность он платит структурой хранения: значения свойств лежат в общих служебных таблицах, и на больших объёмах выборки становятся тяжёлыми. Highload-блок — это отдельная физическая таблица в базе под один справочник, где каждое поле — это колонка. Он лишён разделов, привязок между элементами и сложной обвязки, зато выдерживает десятки и сотни тысяч записей и быстро отдаёт данные через ORM D7. Проще говоря: инфоблок — для контента с богатой структурой, highload-блок — для больших плоских справочников, к которым часто обращаются.

Когда нужен highload-блок, а когда достаточно свойства инфоблока?

Highload-блок оправдан, когда справочник большой (тысячи и десятки тысяч значений), единый на весь каталог и часто используется в фильтрах: бренды, единый список цветов и материалов, города и пункты выдачи, справочник совместимости, база VIN. Если же значений немного (десяток-другой) и они нужны только как ярлык у товара, хватит обычного свойства инфоблока типа «Список» или «Справочник». Ставить highload-блок ради пяти значений — усложнение без выгоды. Ориентир простой: большой единый справочник, к которому идут частые выборки и фильтрация, — highload-блок; маленький локальный набор значений — свойство инфоблока.

Как привязать highload-справочник к товарам?

У свойств инфоблока есть специальный тип — «Привязка к элементам highload-блока» (директория/справочник). Создав такое свойство у товара, вы получаете выпадающий список значений прямо из вашего highload-блока, а в базе у товара хранится ссылка на запись справочника (обычно её UF_XML_ID). Так один справочник «Бренды» обслуживает весь каталог: значение бренда хранится один раз в highload-блоке, а тысячи товаров ссылаются на него. При правке названия бренда меняется одна запись в справочнике, а не тысячи строковых свойств у товаров.

Как получить доступ к данным highload-блока через ORM D7?

Сначала подключается модуль highloadblock, затем по ID или имени таблицы берётся описание блока через HighloadBlockTable::getById(), из него компилируется ORM-сущность методом compileEntity(), а у неё запрашивается класс данных через getDataClass(). Дальше этот класс работает как обычная ORM-таблица D7: getList() для выборки, add()/update()/delete() для записи. Многие оборачивают эти три шага в собственный хелпер getEntityDataClass($hlId), чтобы не повторять их в каждом месте. Ключевой момент — не пересоздавать сущность в цикле: компиляцию делают один раз и переиспользуют полученный класс.

Highload-блоки быстрее инфоблоков на больших объёмах?

На чтении больших однородных справочников — да, при условии правильных индексов. Highload-блок хранит данные в отдельной таблице, где каждое поле — колонка, поэтому выборка по значению превращается в простой индексируемый запрос без джойнов служебных таблиц свойств. Но «быстрее» не появляется само собой: на поля, по которым идёт фильтрация и поиск (UF_XML_ID, коды, внешние ключи), нужно ставить индексы, иначе большая таблица будет сканироваться целиком. Highload-блок даёт потолок производительности выше инфоблока, но реализуют этот потолок индексы и грамотные выборки.

Как highload-справочник участвует в умном фильтре?

Свойство товара типа «Привязка к элементам highload-блока» можно включить в умный фильтр так же, как обычное свойство. Тогда в фильтре появляется набор значений из справочника (бренды, цвета), а сам фильтр работает через фасетный индекс: заранее посчитанную таблицу «какие товары каким значениям соответствуют». Highload-блок здесь — источник справочных значений и их свойств (например, HEX-кода цвета для цветовых плашек), а быстрый отклик фильтра обеспечивает фасет. После массового обмена с 1С фасетный индекс, как и кэш, нужно перестроить, иначе фильтр покажет неактуальные наборы.

Нужно ли кэшировать выборки из highload-блока?

Да, если справочник читается часто (а справочники для того и нужны). Данные highload-блоков меняются реже товаров, поэтому они — идеальный кандидат на долгий теговый кэш: собрали список значений один раз, отдаём из памяти, сбрасываем только при обновлении справочника. Кэшируют через тот же D7-механизм Cache и TaggedCache: оборачивают getList() в кэш и регистрируют собственный тег справочника, который сбрасывают при add/update/delete записей. Это снимает повторные обращения к базе на каждой странице каталога, где выводятся, например, бренды или цвета.

Можно ли перенести строковое свойство товара в highload-справочник?

Можно и часто нужно, если одно и то же значение (бренд, материал, страна) записано строкой у тысяч товаров с разнобоем в написании. Миграция идёт по шагам: собрать уникальные значения строкового свойства, нормализовать их, создать highload-блок и залить туда справочник, создать у товара свойство «Привязка к элементам highload-блока», сопоставить каждому товару запись справочника и переключить вывод и фильтр на новое свойство. После проверки старое строковое свойство отключают. Результат — единый непротиворечивый справочник, быстрый фильтр и правка названия в одном месте вместо тысяч строк.

Поделиться:

Нужен быстрый каталог на больших справочниках?

Спроектируем highload-блоки и привязки, наведём порядок в характеристиках, настроим ORM D7, индексы, умный фильтр и кэш. Рассчитаем работу по вашему каталогу на 1С-Битрикс.

Услуга «Highload-проект»

Игорь Воскресенский

Команда студии B2Bsite. С 2014 года разрабатываем, ускоряем и поддерживаем интернет-магазины на 1С-Битрикс: каталоги и highload-справочники, ORM D7 и обмен с 1С, умный фильтр, кэширование и производительность для среднего и крупного бизнеса.

← Все статьи блога