Фасетный индекс: когда ускоряет, а когда мешает

Почему умный фильтр тормозит не на выборке, а на счётчиках, что именно денормализует фасетный индекс и в каких случаях он делает каталог медленнее.

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

Каталог на двести тысяч позиций, умный фильтр из двадцати свойств. Страница раздела отдаётся восемь секунд, причём выборка самих товаров занимает из них меньше десятой доли.

Включили фасетный индекс — стало триста миллисекунд. Победа, которую портит одна деталь: обмен с 1С идёт каждые пятнадцать минут, после каждого индекс частично устаревает, а полный пересчёт занимает сорок минут. Каталог теперь быстрый, но значительную часть суток показывает в фильтре не то, что есть на складе.

Фасетный индекс — хороший инструмент с высокой ценой владения. Разберём, за что именно он платит, и в каких случаях эта сделка невыгодна.

Почему тормозит умный фильтр

Первое заблуждение, которое стоит снять: фильтр тормозит не на выборке товаров.

Выбрать сто товаров из двухсот тысяч по нескольким условиям — задача, с которой база справляется быстро при нормальных индексах. Дорого стоит другое — счётчики.

Чтобы нарисовать фильтр в привычном виде:

text
Цвет
  ☐ Синий (42)
  ☐ Красный (17)
  ☐ Чёрный (0)   ← недоступен при текущих условиях

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

А теперь вспомним, как свойства хранятся в инфоблоке. Схема EAV, по строке на каждое значение каждого элемента, — разница между этой моделью и плоской таблицей разобрана в статье про выбор между инфоблоками и Highload-блоками. Каждый счётчик — это агрегирующий запрос с соединением по таблице, где на двести тысяч элементов приходятся миллионы строк.

Отсюда неочевидное следствие: на скорость фильтра число свойств влияет сильнее, чем число товаров. Каталог на пять тысяч позиций с сорока свойствами в фильтре тормозит заметнее, чем каталог на пятьдесят тысяч с пятью.

Что такое фасетный индекс

Фасетный индекс — это денормализованная структура, в которой заранее посчитано соответствие «раздел + свойство + значение → элементы». Вместо того чтобы вычислять счётчики соединениями по EAV на каждый запрос, Битрикс читает готовые данные.

Классический размен: место и стоимость поддержания против скорости чтения.

Без индексаС индексом
Счётчики фильтраАгрегирующие запросы по EAVЧтение готовой структуры
Стоимость запросаРастёт с числом свойствПочти не растёт
Место в базе—Дополнительная структура
Изменение товараБесплатноТребует обновления индекса
Массовый импортБесплатноТребует пересчёта

Последние две строки и есть цена, о которой обычно не думают при включении.

Сколько он занимает и как это посмотреть

Имя таблицы индекса между версиями Битрикса отличается, поэтому надёжнее искать по шаблону, а не по точному названию:

sql
SELECT
    TABLE_NAME,
    TABLE_ROWS,
    ROUND((DATA_LENGTH + INDEX_LENGTH) / 1024 / 1024) AS total_mb
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = DATABASE()
  AND TABLE_NAME LIKE '%facet%'
ORDER BY total_mb DESC;

Для сравнения — сколько занимает само хранилище свойств:

sql
SELECT
    TABLE_NAME,
    TABLE_ROWS,
    ROUND((DATA_LENGTH + INDEX_LENGTH) / 1024 / 1024) AS total_mb
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = DATABASE()
  AND TABLE_NAME LIKE 'b_iblock_element_prop%'
ORDER BY total_mb DESC;
💡 Совет

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

Когда индекс не нужен

Три ситуации, в которых включение не окупается.

Маленький каталог. Ориентировочно до пяти тысяч товаров с умеренным числом свойств выигрыш измеряется десятками миллисекунд. Их не видно на фоне сетевой задержки и рендеринга, а пересчёт вы получите в полном объёме.

Фильтром почти не пользуются. Прежде чем включать, посмотрите в метрике долю сессий, где фильтр реально применяли. На каталогах, где покупатели ходят по разделам и поиску, эта доля бывает единицами процентов, и оптимизировать имеет смысл совсем другое.

Свойств в фильтре мало. Три-четыре свойства не создают той нагрузки, ради снятия которой строится индекс.

Когда индекс мешает

Здесь интереснее, потому что вред неочевиден и проявляется не сразу.

Частый обмен

Главный конфликт. Массовое изменение товаров делает часть индекса неактуальной, и до пересчёта фильтр отдаёт устаревшие счётчики.

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

Выходов три, и выбирают между ними по ситуации:

  • развести обмен и пересчёт по времени: тяжёлый импорт ночью, пересчёт следом, лёгкие обновления цен днём без затрагивания свойств фильтра;
  • пересчитывать не весь индекс, а затронутые разделы;
  • отказаться от индекса, если расхождение фильтра со складом дороже, чем задержка страницы.
⚠️ Важно

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

Пересчёт внутри обмена

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

Частая смена структуры свойств

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

Как честно измерить эффект

Синтетические тесты здесь бесполезны: выигрыш зависит от числа свойств, их заполненности и распределения значений. Мерить нужно на копии с боевыми данными.

bash
# Время ответа страницы раздела с применённым фильтром, 10 замеров
for i in $(seq 1 10); do
  curl -s -o /dev/null \
    -w "%{time_starttransfer}\n" \
    "https://staging.site.ru/catalog/smesiteli/filter/color-is-blue/apply/"
done | sort -n | awk '{a[NR]=$1} END {print "медиана:", a[int(NR/2)+1]}'

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

💡 Совет

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

Дополнительно стоит замерить время полного пересчёта — это то, что вы обязуетесь платить после каждого массового изменения:

bash
# Пересчёт запускается из админки; засекаем и записываем результат.
# Это число понадобится, чтобы спланировать окно между обменами
date +%s > /tmp/facet_start

Что проверить перед включением

Пять пунктов, каждый из которых способен отменить решение:

  1. Сколько товаров и свойств в фильтре. Мало и того и другого — не включать.
  2. Как часто идёт обмен. Чаще, чем длится пересчёт, — конфликт, решать до включения.
  3. Работают ли агенты по cron. Нет — пересчёт ночью не запустится.
  4. Хватает ли памяти базе с учётом новой структуры.
  5. Пользуются ли фильтром вообще. Проверяется в метрике за минуту.

Что делать, если индекс не помог

Фильтр остался медленным после включения — значит, узкое место не там, где предполагали. Проверять в таком порядке:

Индекс реально построен. Включённая настройка и построенный индекс — разные вещи. Смотрите, есть ли в таблице строки.

Свойства подходят для фильтрации. Строковые свойства со свободным вводом дают тысячи уникальных значений вместо десятка. Фильтр по такому свойству будет медленным при любом индексе, а список значений — бессмысленным для покупателя.

Дело не в самом фильтре. Медленной может быть выборка товаров, вывод изображений или сторонний код в шаблоне. Порядок разбора для смежных проблем умного фильтра описан в статьях про 404 в умном фильтре и валидацию его URL.

Проблема в сортировке, а не в фильтрации. Сортировка по свойству — отдельная задача со своей стоимостью, разобранная в статье про фильтрацию и сортировку по свойству.

Итоги

Фильтр тормозит на счётчиках, а не на выборке. Понимание этого меняет направление оптимизации: смотреть надо на число свойств, а не на число товаров.

Фасетный индекс — размен места и пересчёта на скорость чтения. Как всякий размен, он выгоден не всегда, и невыгодным его делают не размеры каталога, а частота изменений.

Обмен чаще, чем длится пересчёт, — противопоказание. Вы получите постоянно занятый сервер и постоянно неактуальный фильтр одновременно.

Пересчёт агентом без cron ночью не выполнится. Проверьте это раньше, чем включите индекс.

Измеряйте на боевых данных с отключённым кэшем. И записывайте время пересчёта — это ваш будущий бюджет обслуживания.

И наблюдение, ради которого стоит открыть метрику перед тем, как трогать настройки. На заметной доле проектов, где просят «ускорить умный фильтр», фильтром пользуются считанные проценты посетителей — а тормозит у остальных не он, а тяжёлая витрина раздела. Фасетный индекс в такой ситуации честно ускорит то, чем почти никто не пользуется, добавит пересчёт после каждого обмена и оставит основную проблему нетронутой.

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

Что такое фасетный индекс в Битриксе?

Это вспомогательная денормализованная структура, в которой заранее посчитано, какие элементы каких разделов имеют какие значения свойств. Умный фильтр вместо соединений с EAV-таблицей читает готовые данные. Индекс не хранит товары и не заменяет каталог — он только отвечает на вопрос, какие значения фильтра доступны и сколько за каждым позиций.

Почему умный фильтр тормозит даже на небольшом каталоге?

Потому что основная стоимость не в выборке товаров, а в счётчиках. Чтобы показать «Синий (42)», нужно для каждого значения каждого свойства посчитать, сколько товаров останется при текущем наборе условий. На 20 свойствах это десятки агрегирующих запросов по EAV-таблице на одну загрузку страницы, и объём каталога здесь вторичен по отношению к числу свойств в фильтре.

Когда фасетный индекс не нужен?

На каталогах примерно до 5 000 товаров с небольшим числом свойств в фильтре — выигрыш там измеряется десятками миллисекунд и не оправдывает пересчёта. Также он избыточен, если умным фильтром почти не пользуются: посмотрите долю сессий с применённым фильтром в метрике, прежде чем включать.

Почему после обмена с 1С фильтр показывает неверные значения?

Потому что индекс устарел, а пересчёт ещё не прошёл. Массовое изменение товаров делает часть индекса неактуальной, и до пересчёта фильтр отдаёт данные на момент последнего построения. При обмене раз в 15 минут и долгом пересчёте каталог рискует находиться в неактуальном состоянии большую часть времени.

Как проверить, помогает ли фасетный индекс на моём проекте?

Замерить время ответа страницы раздела с применённым фильтром до и после включения, на копии с боевыми данными. Синтетические каталоги для этого не годятся: эффект зависит от числа свойств, их заполненности и распределения значений. Если разница в пределах сотни миллисекунд, а обмен идёт часто, индекс скорее навредит.