Блог разработчика 1С-Битрикс

Оптимальное создание и использование Highload-блока в 1С-Битрикс

Генерация тестовых данных, быстрый поиск и правильная индексация UF_NAME

Оптимальное создание и использование Highload-блока
<?php
use Bitrix\Main;
use Bitrix\Main\Loader;
use Bitrix\Highloadblock as HL;
use Bitrix\Main\Type\DateTime;

/**
 * Класс-утилита для работы с HL-блоком Test1.
 * Демонстрирует полный цикл: создание, наполнение, выборки, индексация.
 */
class Test1Helper
{
    /** Создаёт HL-блок Test1 (UF_NAME varchar(255), UF_TIME datetime). */
    public static function createTestHLBlock(): bool
    {
        if (!Loader::includeModule('highloadblock')) {
            return false;
        }
        // Уже существует?
        $exists = HL\HighloadBlockTable::getList([
            'filter' => ['=NAME' => 'Test1'],
        ])->fetch();
        if ($exists) {
            return true;
        }
        // Создаём HL-блок
        $res = HL\HighloadBlockTable::add([
            'NAME'       => 'Test1',
            'TABLE_NAME' => 'testio_test1',
        ]);
        if (!$res->isSuccess()) {
            return false;
        }
        $hlId = (int)$res->getId();
        $ute  = new \CUserTypeEntity();
        // UF_NAME (varchar(255) => подходит для индекса)
        $ute->Add([
            'ENTITY_ID'      => "HLBLOCK_{$hlId}",
            'FIELD_NAME'     => 'UF_NAME',
            'USER_TYPE_ID'   => 'string',
            'MULTIPLE'       => 'N',
            'MANDATORY'      => 'Y',
            'SHOW_IN_LIST'   => 'Y',
            'EDIT_IN_LIST'   => 'Y',
            'SORT'           => 100,
            'SETTINGS'       => [
                'DEFAULT_VALUE' => '',
                'SIZE'          => 20,
                'ROWS'          => 1,
                'MAX_LENGTH'    => 255,    // varchar(255)
            ],
            'EDIT_FORM_LABEL'=> ['ru' => 'Имя', 'en' => 'Name'],
        ]);
        // UF_TIME
        $ute->Add([
            'ENTITY_ID'      => "HLBLOCK_{$hlId}",
            'FIELD_NAME'     => 'UF_TIME',
            'USER_TYPE_ID'   => 'datetime',
            'MULTIPLE'       => 'N',
            'MANDATORY'      => 'Y',
            'SHOW_IN_LIST'   => 'Y',
            'EDIT_IN_LIST'   => 'Y',
            'SORT'           => 200,
            'SETTINGS'       => [
                'DEFAULT_VALUE' => ['TYPE' => 'NONE', 'VALUE' => ''],
            ],
            'EDIT_FORM_LABEL'=> ['ru' => 'Время', 'en' => 'Time'],
        ]);
        return true;
    }
    /** Создаёт HL-блок (если нет) и заполняет случайными данными. */
    public static function createAndFillTestHLBlock(int $count = 100): bool
    {
        if (!self::createTestHLBlock()) {
            return false;
        }
        self::fillTestHLBlock($count);
        self::createIndexForUfName();   // сразу ставим индекс
        return true;
    }
    /** Массовое добавление тестовых записей. */
    public static function fillTestHLBlock(int $count = 100): void
    {
        if (!Loader::includeModule('highloadblock')) {
            return;
        }
        $hl = HL\HighloadBlockTable::getList([
            'filter' => ['=NAME' => 'Test1'],
        ])->fetch();
        if (!$hl) {
            return;
        }
        $dataClass = HL\HighloadBlockTable::compileEntity($hl)->getDataClass();
        for ($i = 0; $i < $count; $i++) {
            $dataClass::add([
                'UF_NAME' => 'Test Name ' . mt_rand(1000, 9999),
                'UF_TIME' => (new DateTime())->add('-' . mt_rand(1, 720) . ' hours'),
            ]);
        }
    }
    /** Получает все записи, моложе $hours часов. */
    public static function getRecentRecordsByHours(int $hours = 24): array
    {
        if (!Loader::includeModule('highloadblock')) {
            return [];
        }
        $hl = HL\HighloadBlockTable::getList([
            'filter' => ['=NAME' => 'Test1'],
        ])->fetch();
        if (!$hl) {
            return [];
        }
        $dataClass = HL\HighloadBlockTable::compileEntity($hl)->getDataClass();
        $border    = (new DateTime())->add('-' . abs($hours) . ' hours');
        return $dataClass::getList([
            'filter' => ['>UF_TIME' => $border],
            'order'  => ['UF_TIME' => 'DESC'],
        ])->fetchAll();
    }
    /** Получает одну случайную запись (способ №1: OFFSET). */
    public static function getRandomRecord(): ?array
    {
        if (!Loader::includeModule('highloadblock')) {
            return null;
        }
        $hl = HL\HighloadBlockTable::getList([
            'filter' => ['=NAME' => 'Test1'],
        ])->fetch();
        if (!$hl) {
            return null;
        }
        $dataClass = HL\HighloadBlockTable::compileEntity($hl)->getDataClass();
        $total     = $dataClass::getCount();
        if ($total === 0) {
            return null;
        }
        $offset = mt_rand(0, $total - 1);
        return $dataClass::getList([
            'limit'  => 1,
            'offset' => $offset,
        ])->fetch() ?: null;
    }
    /** Альтернативный случайный выбор (способ №2: RAND() в runtime). */
    public static function getRandomRecordByRand(): ?array
    {
        if (!Loader::includeModule('highloadblock')) {
            return null;
        }
        $hl = HL\HighloadBlockTable::getList([
            'filter' => ['=NAME' => 'Test1'],
        ])->fetch();
        if (!$hl) {
            return null;
        }
        $dataClass = HL\HighloadBlockTable::compileEntity($hl)->getDataClass();
        return $dataClass::getList([
            'runtime' => [
                'RAND' => [
                    'data_type'  => 'float',
                    'expression' => ['RAND()'],
                ],
            ],
            'order'  => ['RAND' => 'ASC'],
            'limit'  => 1,
            'select' => ['ID', 'UF_NAME', 'UF_TIME'],
        ])->fetch() ?: null;
    }
    /** Создаёт индекс по UF_NAME, если ещё не создан. */
    public static function createIndexForUfName(): void
    {
        if (!Loader::includeModule('highloadblock')) {
            return;
        }
        $hl = HL\HighloadBlockTable::getList([
            'filter' => ['=NAME' => 'Test1'],
        ])->fetch();
        if (!$hl) {
            return;
        }
        $conn   = Main\Application::getConnection();
        $table  = $conn->getSqlHelper()->quote($hl['TABLE_NAME']);
        $exists = $conn->query("
            SHOW INDEX FROM {$table} WHERE Column_name = 'UF_NAME'
        ")->fetch();
        if (!$exists) {
            $conn->queryExecute("
                ALTER TABLE {$table} ADD INDEX idx_uf_name (`UF_NAME`(255))
            ");
        }
    }
}

Введение

Highload-блоки (HL-blocks) в 1С-Битрикс — незаменимый инструмент, когда нужно хранить и обрабатывать большие объёмы структурированных данных быстрее, чем это позволяет стандартный iblock. В данной статье мы:

  • создаём тестовый HL-блок Test1 с двумя полями: UF_NAME и UF_TIME;
  • генерируем случайные данные для нагрузочного тестирования;
  • получаем свежие или случайные записи двумя способами;
  • оптимизируем поиск добавлением индекса на UF_NAME, предварительно меняя тип поля с text на varchar(255) — критически важно для MySQL/MariaDB.

Материал пригодится как начинающим, так и опытным разработчикам, занимающимся оптимизацией производительности 1С-Битрикс.

Что делает каждая часть кода

Метод Назначение Ключевые моменты
createTestHLBlock() Создаёт HL-блок Test1 (если ещё не существует) Сразу задаём UF_NAME как varchar(255) для будущего индекса
createAndFillTestHLBlock() Обёртка: создаёт блок и заполняет его После заполнения сразу создаётся индекс
fillTestHLBlock($count) Добавляет $count случайных записей Используется DateTime()->add('-N hours') для реалистичного параметра времени
getRecentRecordsByHours($hours) Возвращает записи свежее $hours часов Удобно для протухающих данных (кэш, лог)
getRandomRecord() Случайная запись через offset Быстро на малых объёмах, но O(n) на больших
getRandomRecordByRand() Случайная запись через RAND() runtime-поле Рекомендуется при больших объёмах данных
createIndexForUfName() Добавляет индекс idx_uf_name Проверяет наличие перед добавлением

Почему varchar(255), а не text

  • MySQL не индексирует TEXT напрямую; приходится указывать префикс.
  • Записи с TEXT хранятся вне основного ряда таблицы (off-row), что замедляет чтение.
  • varchar(255) помещает строку целиком внутри row-id, а индексация делается без префикса или с коротким префиксом автоматически.

Примеры использования

1. Инициализация и наполнение

// где-нибудь в admin-скрипте или миграции
Test1Helper::createAndFillTestHLBlock(250); // создаём и добавляем 250 записей

2. Выбор свежих записей (за последние 12 часов)

$recent = Test1Helper::getRecentRecordsByHours(12);
foreach ($recent as $row) {
    echo $row['UF_NAME'] . ' — ' . $row['UF_TIME']->format('d.m.Y H:i') . PHP_EOL;
}

3. Получение случайной записи

// Способ №1 (offset)
$random1 = Test1Helper::getRandomRecord();
// Способ №2 (RAND() runtime) — предпочтительный
$random2 = Test1Helper::getRandomRecordByRand();

4. Индексация поля UF_NAME отдельно

Если HL-блок был создан раньше и UF_NAME имеет тип text, сначала поменяйте тип вручную (или через миграцию) на varchar(255), затем вызовите:

Test1Helper::createIndexForUfName();

Заключение

Использование Highload-блоков с корректно настроенными индексами значительно повышает производительность выборок в 1С-Битрикс, особенно при больших объёмах данных.

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

Интегрируйте решения из статьи в свои миграции или модули, и работа вашего проекта на Bitrix станет заметно быстрее и стабильнее.

Изучите также базовую информацию - Работа с Highload-блоками в Битрикс D7
Теги:  Highload-блоки, оптимизация, индексация, UF_NAME, PHP, разработка


Стоимость услуг по разработке и сопровождению сайтов на 1C-Битрикс

Лендинг

от 3 дней

от 25 000 рублей

Разработка одностраничного сайта на платформе Битрикс

* стоимость зависит от наличия верстки, использования готового решения и т.д.

Перенос сайтов на «1С-Битрикс»

сайты на платформе «1С-Битрикс» — это удобство, надежность и высокая посещаемость

от 12 000 рублей
Перенос сайтов с любых CMS и статичных страниц на платформу «1С-Битрикс», с учетом дизайна, верстки и урл-адресов. С сохранением всей информации и структуры сайта.

* зависит от объема выполняемых работ.

Разработка корпоративного сайта

от 7 дней

от 40 000 рублей

Разработка сайта без системы оплаты заказов через корзину

* стоимость зависит от наличия верстки, использования готового решения и т.д.