Каталог растёт, и в нём появляется «бренд», записанный строкой у каждого из двадцати тысяч товаров: где-то «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-блок |
|---|---|---|
| Назначение | Контент: товары, новости, разделы | Большой плоский справочник |
| Хранение свойств | Служебные таблицы свойств | Отдельная таблица, поле = колонка |
| Разделы и дерево | Есть | Нет |
| Привязка к элементам | Есть | Нет (сам может быть целью привязки) |
| Права на записи | Гибкие | Минимальные |
| Объём без просадки | Хорош до умеренных объёмов свойств | Десятки и сотни тысяч записей |
| Доступ из кода | CIBlockElement, ORM D7 | ORM D7 (HighloadBlockTable) |
| Типичное применение | Каталог товаров, контент-разделы | Бренды, цвета, города, VIN, совместимость |
Читать таблицу стоит так: если сущности нужны разделы, привязки, права и богатый интерфейс — это инфоблок. Если нужен большой единый список значений с быстрым доступом и он часто участвует в выборках и фильтрах — это highload-блок. Они не конкурируют, а дополняют друг друга: товары остаются в инфоблоке, а их «словарные» характеристики выносятся в highload-справочники и подключаются к товарам ссылкой.
Как устроено хранение: отдельная таблица
Разберём физику хранения подробнее — именно она объясняет, откуда берётся скорость. Когда вы создаёте highload-блок, вы задаёте имя таблицы (например, b_hl_brand) и имя ORM-сущности (например, Brand). Модуль создаёт в базе реальную таблицу с этим именем. Каждое пользовательское поле, которое вы добавляете (UF_NAME, UF_XML_ID, UF_COUNTRY), становится колонкой этой таблицы. Каждая запись справочника — строка таблицы.
Сравните это с инфоблоком, где значение свойства «Бренд» у товара хранится в общей таблице значений свойств вместе со значениями всех остальных свойств всех остальных товаров. Чтобы выбрать товары определённого бренда, ядру приходится джойнить таблицу элементов с таблицей значений свойств и фильтровать по идентификатору свойства. На больших объёмах это дорого. В highload-блоке выборка «все бренды из Германии» — это SELECT ... WHERE UF_COUNTRY = 'Германия' по одной таблице с индексом по колонке. Разница в стоимости запроса и определяет, почему большой справочник живёт в highload-блоке.
Есть и следствие для дисциплины: поскольку это реальная таблица, к ней применимы все обычные правила проектирования БД — типы колонок, индексы, уникальность. Highload-блок не прячет базу за абстракцией «свойств», а показывает её почти напрямую. Это и ответственность (нужно думать про индексы), и возможность (можно строить действительно быстрые справочники).
Когда нужен highload-блок: сценарии
Теория обретает смысл на конкретных задачах. Вот типовые сценарии, где highload-блок — правильный инструмент, встречающиеся в реальных магазинах и порталах.
- Бренды и производители. Единый список брендов на весь каталог: название, логотип, страна, описание, ЧПУ. Тысячи товаров ссылаются на одну запись бренда.
- Справочник характеристик. Единые списки цветов (с HEX-кодом для плашек), материалов, размеров, единиц измерения — то, что повторяется у множества товаров и участвует в фильтре.
- Города и пункты выдачи. База населённых пунктов, ПВЗ, складов с координатами и зонами доставки — часто десятки тысяч записей.
- Совместимость и подбор. Справочники «подходит к» — автозапчасти по маркам и моделям, картриджи по принтерам, аксессуары по устройствам. Классически большие и часто запрашиваемые таблицы.
- База VIN и серийных номеров. Большие технические справочники, где по коду нужно быстро найти запись, — типичный highload-кейс на сотни тысяч строк.
- Единый список значений с внешним ключом. Любой справочник, который приходит из учётной системы по своему коду (XML_ID) и должен быстро сопоставляться при обмене.
Общий признак у всех сценариев один: справочник большой, единый и часто читается. Как только вы ловите себя на мысли «это значение повторяется у тысяч товаров, и я хочу хранить его один раз и быстро искать» — это кандидат в highload-блок. Как справочники характеристик работают в связке с торговыми предложениями (SKU), мы разбираем в материале про торговые предложения и SKU в 1С-Битрикс.
Создание highload-блока по шагам
Завести highload-блок можно из административного интерфейса (раздел работы с highload-блоками) или из кода. Логически процесс один и тот же: описать блок, добавить поля, наполнить данными. Разберём порядок.
- Создать highload-блок. Задаёте имя ORM-сущности (латиницей, с заглавной — например,
Brand) и имя таблицы в базе (например,b_hl_brand). Модульhighloadblockсоздаёт таблицу. - Добавить пользовательские поля. Каждое поле (
UF_NAME,UF_XML_ID,UF_COUNTRY,UF_SORT) — это будущая колонка. Указываете тип (строка, число, список, файл, привязка) и настройки. - Настроить обязательность и уникальность. Ключевые поля вроде
UF_XML_IDпомечаете обязательными и уникальными — это внешний ключ, по которому справочник сопоставляется с обменом. - Наполнить справочник. Записи заводят вручную через интерфейс, импортом или кодом через ORM. Для большого справочника — обычно скриптом или из обмена.
- Проставить индексы. На поля фильтрации и поиска (
UF_XML_ID, коды, внешние ключи) добавляете индексы в таблице — без них большая таблица будет сканироваться целиком. - Подключить к товарам. Создаёте у товара свойство «Привязка к элементам 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_NAME) может меняться (переименовали бренд), а вот код (UF_XML_ID) должен оставаться стабильным: на него ссылаются товары и по нему идёт сопоставление при обмене. Меняете код записи — рвёте все привязки. Проектируйте UF_XML_ID как неизменяемый идентификатор.
Привязка справочника к товарам
Справочник сам по себе бесполезен, пока товары на него не ссылаются. Связь делают через специальный тип свойства инфоблока — «Привязка к элементам highload-блока» (в некоторых версиях интерфейса — «Справочник» / directory). Создав такое свойство у товара и указав, на какой highload-блок оно ссылается, вы получаете у товара выпадающий список значений прямо из справочника.
Что при этом хранится в базе: у товара в значении свойства лежит не название бренда строкой, а ссылка на запись справочника — как правило, её UF_XML_ID. Поэтому тысячи товаров одного бренда ссылаются на одну запись. Правка названия бренда — это изменение одной строки в справочнике; у товаров ничего трогать не нужно, они продолжают ссылаться на ту же запись по её коду. Это и есть выигрыш нормализации: одно значение — одна запись — много ссылок.
- Создать highload-блок-справочник с полями
UF_XML_IDиUF_NAME(см. предыдущие разделы). - Добавить свойство товара типа «Привязка к элементам highload-блока» и выбрать в его настройках нужный справочник.
- Настроить отображаемое поле. Указать, какое поле справочника показывать в списке (обычно
UF_NAME) и какое использовать как значение (обычноUF_XML_ID). - Проставить значения товарам — вручную, импортом или в обмене с 1С по коду.
- Вывести в каталоге — в карточке и списке, при необходимости подтягивая доп. поля справочника (логотип, HEX-код).
UF_XML_ID через свойство привязки.Highload-справочник в умном фильтре
Одна из главных причин выносить справочник в highload-блок — участие в умном фильтре каталога. Свойство товара типа «Привязка к элементам highload-блока» включается в фильтр так же, как обычное свойство: в настройках компонента фильтра его отмечают, и в интерфейсе появляется набор значений из справочника — бренды, цвета, материалы.
Работает это в связке двух механизмов. Highload-справочник — источник значений и их атрибутов: он отдаёт список брендов, а для цветов ещё и HEX-код, чтобы фильтр рисовал цветные плашки вместо текста. А быстрый отклик самого фильтра обеспечивает фасетный индекс — заранее посчитанная таблица соответствий «значение → товары», по которой фильтр мгновенно понимает, сколько товаров под каждой галочкой и какие показать. Как устроен фасет и почему он ускоряет фильтрацию, мы подробно разбираем в материале про умный фильтр и фасетный индекс в 1С-Битрикс.
Практическое следствие: после массовых изменений (обмен с 1С, пакетная правка справочника) фасетный индекс нужно перестроить, иначе фильтр покажет неактуальные наборы значений и неверные счётчики. Это та же дисциплина, что и с кэшем: изменение данных → перестроение индекса → прогрев. Highload-блок, фасет и кэш работают в одной цепочке, и рвётся она обычно на забытом перестроении.
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() — операция не бесплатная. Если вызвать её на каждой итерации при обработке тысяч товаров, вы получите на ровном месте тяжёлую нагрузку. Компилируйте класс данных один раз (например, через статический кэш в хелпере) и переиспользуйте — как в примере выше.
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(), запись «не пройдёт» тихо: например, из-за нарушения уникальности 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-справочник. Симптом знакомый: бренд (или материал, страна, цвет) записан текстом в свойстве у тысяч товаров, значения дублируются с разнобоем в написании, единого списка нет, фильтр тормозит, а переименование — это массовое обновление. Миграция превращает этот хаос в нормализованный справочник.
- Собрать уникальные значения. Выбрать все различные значения строкового свойства по каталогу — это черновик будущего справочника.
- Нормализовать. Свести дубли и разнобой («BMW», «Бмв», «bmw ») к единым каноническим записям, задать код (
UF_XML_ID) каждой. - Создать highload-блок и залить туда нормализованный справочник через ORM (
add/upsert). - Добавить свойство-привязку у товара типа «Привязка к элементам highload-блока», указывающее на новый справочник.
- Сопоставить товары. Пройти по товарам, по старому строковому значению найти запись справочника и проставить ссылку в новое свойство.
- Переключить вывод и фильтр на новое свойство: карточку, список, умный фильтр; перестроить фасетный индекс.
- Проверить и отключить старое. Убедиться, что все товары сопоставлены (нет «осиротевших»), затем отключить старое строковое свойство.
Отдельного внимания требует шаг сопоставления: почти всегда находятся товары со значением, которого нет в справочнике (опечатка, редкий вариант). Их нельзя терять — по ним составляют отчёт и разбирают вручную или правилами. Миграцию удобно делать скриптом на ORM, прогоняя её сначала на копии базы, а на боевой — в часы низкой нагрузки с последующим перестроением фасета и сбросом кэша. Результат: единый справочник, дешёвая правка названий, быстрый фильтр и порядок в данных.
Highload-блоки и большие каталоги
На большом каталоге highload-блоки — не «фишка», а часть архитектуры. Когда товаров десятки и сотни тысяч, а характеристик у каждого много, разница между «справочник строкой в свойстве» и «справочник в highload-блоке» становится разницей между тормозящим и быстрым каталогом. Правильное разделение данных выглядит так: товары и их торговые предложения — в инфоблоках, а «словарные» характеристики (бренды, цвета, материалы, совместимость) — в highload-справочниках, подключённых к товарам ссылками.
Эта схема хорошо масштабируется, потому что справочники читаются из отдельных индексированных таблиц и кэшируются, а не пересчитываются из тяжёлых свойств инфоблока на каждый запрос. Она же упрощает обмен: справочник синхронизируется по коду отдельно от товаров, и добавление нового бренда не трогает тысячи товарных карточек. Как торговые предложения (SKU) сочетаются с характеристиками из справочников, мы разбираем в материале про торговые предложения и SKU в 1С-Битрикс, а быстрый фильтр по ним — в статье про умный фильтр.
Если проект изначально строится вокруг больших объёмов и высоких нагрузок, highload-справочники, индексы, ORM D7 и кэш стоит закладывать в архитектуру с самого начала, а не прикручивать потом. Именно так мы проектируем highload-проекты на Битриксе: с запасом по объёму данных и по скорости отклика. Другие материалы по производительности и архитектуре собраны в рубрике «Разработка и архитектура».
Когда highload-блок НЕ нужен
Highload-блок — мощный инструмент, но не универсальный. Его частая ошибка применения — ставить туда всё подряд «на будущее», усложняя проект без выгоды. Есть ситуации, где highload-блок избыточен и лучше обойтись обычным свойством инфоблока.
- Мало значений. Если в справочнике десяток-другой значений (пол, сезон, тип упаковки), хватит свойства типа «Список». Highload-блок ради пяти значений — усложнение без пользы.
- Значение локально для одного инфоблока. Если характеристика нужна только в одном разделе и нигде не переиспользуется, отдельный справочник не даёт выигрыша нормализации.
- Нужны разделы, привязки, права. Если сущности требуются древовидная структура, связи между элементами или гибкие права — это инфоблок, а не highload-блок (у него этого нет).
- Редкое чтение. Если к данным почти не обращаются и они не участвуют в фильтрах, ускорение от отдельной таблицы незаметно, а сложность растёт.
- Контент с медиа и версткой. Статьи, посадочные, карточки с богатым контентом — это инфоблоки; highload-блок для плоских справочников, а не для контента.
Ориентир для решения держите простым. Задайте три вопроса: справочник большой? он единый на весь каталог? к нему идут частые выборки и фильтрация? Если на все три ответ «да» — highload-блок оправдан. Если хотя бы на два «нет» — почти наверняка достаточно свойства инфоблока, и не стоит плодить сущности. Правильная архитектура — это не «highload везде», а «highload там, где он реально окупается».
Highload-блоки и обмен с 1С
Справочники редко живут только на сайте — чаще они приходят из учётной системы. Бренды, характеристики, единицы измерения нередко ведутся в 1С и должны синхронизироваться с highload-блоками магазина. Здесь работает всё, о чём мы говорили: сопоставление по коду (UF_XML_ID), upsert, проверка isSuccess(), а после загрузки — сброс кэша справочника и перестроение фасета.
Практический сценарий такой. Обмен приносит актуальный список значений справочника по их кодам. Скрипт синхронизации проходит по нему upsert-ом: обновляет существующие записи и добавляет новые, при необходимости деактивируя пропавшие. После завершения — сброс тега кэша справочника и, если он участвует в фильтре, перестроение фасетного индекса. Затем прогрев ключевых страниц каталога. Это та же дисциплина «обмен → инвалидация → перестроение → прогрев», что и с товарами. Как устроен обмен и как он влияет на витрину, подробно разобрано в материалах рубрики про интеграции и в статье про кэширование в 1С-Битрикс.
UF_XML_ID) на сайте и в учётной системе должен совпадать и быть стабильным. Если 1С шлёт свой GUID, храните именно его в UF_XML_ID. Тогда синхронизация идёт по ключу без дублей и «потерянных» значений.
Частые ошибки при работе с HL-блоками
Ошибки с highload-блоками повторяются из проекта в проект. Зная их заранее, вы сэкономите часы отладки и не наступите на грабли масштаба.
- Highload-блок ради маленького справочника. Пять значений вынесли в отдельную сущность с ORM и привязкой — усложнили проект без выгоды. Для малого списка хватит свойства инфоблока.
- Нет индексов на полях фильтрации. Большая таблица без индекса на
UF_XML_IDи коды сканируется целиком — highload-блок теряет смысл. Ставьте индексы на всё, по чему фильтруете. - compileEntity() в цикле. Компиляция сущности на каждой итерации при обработке тысяч товаров создаёт тяжёлую нагрузку. Компилируйте класс данных один раз и переиспользуйте.
- Не проверяют isSuccess(). Запись «молча» не проходит из-за нарушения уникальности или пропущенного обязательного поля. Всегда проверяйте результат
add/update. - Меняют UF_XML_ID у существующих записей. Смена кода рвёт привязки товаров и сопоставление с обменом. Код — неизменяемый якорь записи.
- Выборки без кэша. Список справочника тянут из базы на каждой странице каталога вместо кэша. Кэшируйте частые выборки с тегом и сбрасывайте при правке.
- Забыли перестроить фасет. После правки справочника или обмена умный фильтр показывает старые значения и счётчики. Перестраивайте фасетный индекс после изменений.
- Эффект N+1 на справочнике. Запрос к справочнику на каждый товар в списке вместо одной предзагрузки. Тяните справочник разом в массив по коду.
- Хранят в справочнике данные товара. В справочник кладут то, что относится к конкретному товару, а не к значению. Справочник — про значение, товарные атрибуты — у товара.
Чек-лист по highload-блокам
Короткий список, по которому удобно свериться при проектировании и ревью справочников каталога:
- Справочник действительно большой, единый и часто читается — highload-блок оправдан (иначе свойство инфоблока).
- У записи есть стабильный уникальный код
UF_XML_ID— якорь для привязки и обмена. - На поля фильтрации и поиска (
UF_XML_ID, коды, внешние ключи) поставлены индексы. - Товары связаны со справочником свойством «Привязка к элементам highload-блока», а не строкой.
- Доступ к данным идёт через ORM D7 с единожды скомпилированной сущностью (хелпер
getEntityDataClass). - Запись через
add/updateпроверяетisSuccess(); синхронизация — upsert по коду. - Частые выборки справочника кэшируются с собственным тегом; тег сбрасывается при правке записей.
- Справочник в списке предзагружается разом (нет N+1), а не запрашивается на каждый товар.
- После правок и обмена перестраивается фасетный индекс фильтра и сбрасывается кэш.
- В справочнике хранятся атрибуты значения, а не данные конкретного товара.
- Структура highload-блока воспроизводима на всех средах (создание кодом/миграцией).
Вывод
Highload-блок в 1С-Битрикс — это отдельная быстрая таблица под один большой справочник: бренды, характеристики, цвета, города, VIN, совместимость. Его сила не в магии, а в архитектуре: данные лежат в собственной индексированной таблице, поле — это колонка, выборка — простой запрос без джойнов служебных таблиц свойств. За счёт этого справочник на десятки и сотни тысяч записей отдаётся быстро, хранится один раз и правится в одном месте, а тысячи товаров лишь ссылаются на него через свойство «Привязка к элементам highload-блока».
Практический порядок такой: убедиться, что справочник большой и общий (иначе хватит свойства инфоблока), завести highload-блок с кодом UF_XML_ID и индексами, привязать к товарам свойством-привязкой, включить в умный фильтр с перестроением фасета, читать и писать через ORM D7 с единожды скомпилированной сущностью, кэшировать частые выборки с тегом и синхронизировать с 1С по коду. Сделайте это по порядку — и большой каталог будет быстрым, а данные — чистыми и непротиворечивыми. Если проект строится вокруг больших объёмов, highload-архитектуру стоит закладывать сразу — это часть highload-проекта на 1С-Битрикс и грамотно спроектированного каталога товаров, а не «оптимизация на потом».