Как кешировать выборки D7 ORM в 1С-Битрикс через cache в getList?

Тяжёлый getList() на справочнике городов тормозит каждый хит — типичная проблема после "cache" с ttl 3600: витрина показывает вчерашний список, хотя в админке всё поправили. Встроенный ORM-кеш в 1С-Битрикс: Управление сайтом ускоряет повторяющиеся выборки через ключ cache в getList, но требует cache_joins, cleanCache() и cache_flags. Ниже - гайд: включите кеш на staging, проверьте managed_cache и сбрасывайте выборку точечно, без "Очистить весь кеш".

Встроенный ORM-кеш доступен с main 16.5.9: передайте "cache" => ["ttl" => N] в getList, getRow или getByPrimary. Файлы лежат в /bitrix/managed_cache/MYSQL/orm_<table>/, ключ - md5(sql). После add, update, delete через DataManager кеш сбрасывается сам; после raw SQL или импорта вызовите Table::cleanCache(). JOIN без cache_joins не кешируется. Права пользователя в ключ не входят - учитывайте это в filter.

Речь про коробочную CMS, не про CRM-портал Bitrix24. ORM (Object-Relational Mapping) - слой D7, который связывает PHP-класс *Table с таблицей MySQL. Простую выборку инфоблока разобрали в сравнении GetList и ElementTable (B60). Сложные отчёты с агрегацией - в гайде по Query и ExpressionField (B83). Здесь - только встроенный кеш выборок DataManager, без обзора всех видов кеша.

Выберите, когда нужен ORM cache, а не TaggedCache или полная очистка

Сравнение ORM cache, TaggedCache и полной очистки кеша в Битрикс

ORM-кеш ускоряет повторяющиеся getList() на своих Table или HL DataClass в сервисе модуля. Это не registerTag в шаблоне и не кнопка в админке.

Механизм Где включается Когда использовать
ORM cache (cache в getList) DataManager::getList / getRow Тяжёлая выборка из одной ORM-талицы, одинаковая для всех посетителей
TaggedCache / registerTag Компонент, result_modifier HTML-блок витрины с тегами iblock_* - см. гайд по тегированному кешу (B66)
Очистка через админку Настройки - Производительность Аварийный сброс всего managed cache - когда и как чистить кеш (B20)

Делайте: кешируйте ORM-выборку, если один и тот же SQL уходит на каждый хит и данные меняются через DataManager. Не делайте: не путайте CityTable::cleanCache() с clearByTag из B66 и не жмите "Очистить весь кеш" при каждой правке справочника.

Включите cache в getList и проверьте managed_cache на диске

Чеклист включения ORM cache в getList и проверки managed_cache на диске

По умолчанию кеширование выборок ORM отключено. Минимальный рабочий пример - ttl в секундах:

use Bitrix\Main\GroupTable;

$result = GroupTable::getList([
    'select' => ['ID', 'NAME'],
    'filter' => ['=ACTIVE' => 'Y'],
    'order'  => ['C_SORT' => 'ASC'],
    'cache'  => ['ttl' => 3600],
]);

Тот же ключ cache работает в getRow() и getByPrimary(). Второй хит читается из /bitrix/managed_cache/MYSQL/orm_b_group/, ключ файла - md5(sql). Из кеша приходит ArrayResult, не "живой" DB Result - для fetch() обычно без разницы.

  1. Добавьте "cache" => ["ttl" => 3600] к повторяющемуся getList на staging.
  2. Обновите страницу дважды и откройте /bitrix/managed_cache/MYSQL/ - появится orm_b_*.
  3. Сравните slow log или SqlTracker: на втором хите SQL к таблице не уходит.
  4. Измените запись через GroupTable::update() и зафиксируйте ttl под частоту правок: справочник раз в неделю - 86400, оперативные данные - 300-600.

Делайте: кешируйте только стабильные справочники и каталоги с редкими правками. Не делайте: не ставьте ttl 86400 на данные, которые меняются каждый час без автосброса через ORM.

Настройте cache_joins для Reference и JOIN-выборок

Workflow cache_joins для JOIN-выборок D7 ORM с Reference

Выборки с JOIN и Reference по умолчанию не кешируются. Нужен cache_joins => true. Типичная ошибка на практике: ttl есть, JOIN есть, а SQL идёт каждый раз - забыли cache_joins. Или cache_joins включили, а связанное поле устарело после правки соседней таблицы.

$result = CityTable::getList([
    'select'  => ['ID', 'NAME', 'COUNTRY_NAME' => 'COUNTRY.NAME'],
    'runtime' => [
        new Reference(
            'COUNTRY',
            CountryTable::class,
            Join::on('this.COUNTRY_ID', 'ref.ID')
        ),
    ],
    'cache' => [
        'ttl' => 3600,
        'cache_joins' => true,
    ],
]);

Подробнее про Reference и Join - в гайде по связям D7 ORM (B79). Здесь важно правило: cache_joins => true только когда уверены, что связанные таблицы инвалидируются вместе или ttl приемлем.

Делайте: включайте cache_joins для стабильных JOIN-справочников. Не делайте: не кешируйте JOIN с часто меняющимися полями без короткого ttl или cleanCache() на обеих таблицах.

Сбросьте кеш через cleanCache() после raw SQL и импорта

DataManager автоматически сбрасывает ORM-кеш таблицы при add(), update(), delete(). Принудительный сброс всей выборки сущности:

CityTable::cleanCache();
// эквивалент:
CityTable::getEntity()->cleanCache();

Автосброс не сработает при прямом SQL, legacy CIBlockElement::Update, нестандартном addMulti() или импорте в обход Table. Тогда CityTable::cleanCache() один раз - свежие данные без полной очистки из B20. Workflow: импорт → cleanCache() → getList с cache → витрина обновлена.

Делайте: вешайте cleanCache() в конец скрипта импорта или агента. Не делайте: не полагайтесь на автосброс, если правка шла мимо DataManager.

Проверьте cache_flags в .settings.php, если кеш "не пишется"

DevOps может ограничить TTL таблицы глобально. В /bitrix/.settings.php секция cache → cache_flags:

'cache_flags' => [
    'value' => [
        'b_group_min_ttl' => 60,
        'b_group_max_ttl' => 3600,
    ],
    'readonly' => false,
],

b_<table>_min_ttl - код не задаст TTL меньше минимума. b_<table>_max_ttl - верхняя граница; значение 0 полностью запрещает ORM-кеш этой таблицы, даже если в getList указан ttl 3600. Entity::getCacheTtl() учитывает эти лимиты - приоритет у maximum.

Если в коде cache есть, а каталога orm_b_* нет - первым делом проверьте cache_flags и readonly. Для отладки "почему не кешируется" смотрите логирование через Logger (B84), а не только var_dump в шаблоне.

Делайте: согласуйте ttl в коде с лимитами на staging перед prod. Не делайте: не отлаживайте "кеш не работает" часами, если max_ttl=0 выставлен в .settings.php.

Избегите ловушек: права, offset и персональные данные

На практике в реальном проекте ORM-кеш не добавляет в ключ пользователя и группы - персональные списки кешируйте с USER_ID в filter или без ORM cache. Offset-пагинация даёт отдельный файл кеша на страницу. Кешируется Result выборки, не EO_Collection - для готового массива после fetchAll() есть отдельный слой Data\Cache.

Делайте: кешируйте публичные справочники с одинаковым результатом для всех. Не делайте: не кешируйте "мои заказы" с ttl 3600 без учёта USER_ID в filter - получите чужие данные.

Пройдите чек-лист staging: правка → свежие данные

  1. Включите cache с ttl на тестовом getList и убедитесь в файлах orm_* на диске.
  2. Измените запись через Table::update() - витрина должна обновиться без cleanCache().
  3. Измените ту же таблицу через raw SQL - данные "залипнут"; вызовите cleanCache().
  4. Проверьте JOIN: без cache_joins SQL идёт каждый раз; с cache_joins - файлы кеша и актуальность связанных полей.
  5. Сверьте cache_flags: при max_ttl=0 файлов orm_* не будет - это ожидаемо.

Нужна помощь с кешем и производительностью модуля - обсудим задачу, примеры внедрений - в портфолио.

Автор: Максим Мольков, Senior-разработчик 1С-Битрикс.
Источники: выборка данных ORM Bitrix Framework, кеширование ManagedCache и cache_flags, урок D7: кеширование выборки getList, урок: cache_flags в .settings.php.

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

Чем ORM cache отличается от registerTag в компоненте?

ORM cache живёт в параметре cache у getList/getRow и кеширует результат SQL-запроса к таблице DataManager. registerTag в компоненте помечает HTML-блок тегами iblock_* и сбрасывается через clearByTag - это слой витрины, не выборки ORM. Подробнее - в гайде B66 по тегированному кешу.

Вызывается ли cleanCache при Table::update()?

Да. add(), update() и delete() через DataManager автоматически сбрасывают ORM-кеш этой таблицы. Ручной cleanCache() нужен после raw SQL, legacy-правок в обход ORM или нестандартного массового импорта.

Зачем cache_joins при JOIN в getList?

Без cache_joins => true выборки с Reference или JOIN не кешируются, даже если указан ttl. С cache_joins ключ учитывает связанные таблицы - включайте только когда готовы к инвалидации или короткому ttl. Детали JOIN - в B79.

Где на диске лежит кеш ORM-запросов?

В /bitrix/managed_cache/MYSQL/orm_<имя_таблицы>/, например orm_b_group. Имя файла - md5 от SQL. Это управляемый кеш, не путать с /bitrix/cache/ (неуправляемый компонентный).

Почему addMulti может не сбросить кеш?

Стандартный addMulti в DataManager участвует в автосбросе, но кастомные обёртки или HL-блоки с переопределённым addMulti без вызова родителя могут обойти сброс. После массовой загрузки безопаснее вызвать Table::cleanCache() явно.

Можно ли кешировать EO_Collection из getList?

Нет. ORM cache сохраняет Result выборки как ArrayResult - набор строк. Коллекции объектов с методами и замыканиями в этот механизм не попадают. Сохраняйте массив через fetchAll() или используйте отдельный слой Data\Cache для готовых структур.

Обсудить вашу задачу

Было полезно?

Поставьте лайк, если статья помогла разобраться.

Поделиться статьёй

Отправьте тому, кому это пригодится.

Telegram ↗ MAX ↗ ВКонтакте ↗ Одноклассники ↗
Все статьи ↗

Как настроить регистрацию и личный кабинет покупателя на 1С-Битрикс?

Структура шаблонов сайта в Битриксе и поиск header footer

Как настроить скидки и промокоды на 1С-Битрикс: правила корзины и купоны