Как остановить неограниченный рост кеша catalog.section в 1С-Битрикс?
Высокая нагрузка на сайт, со стороны facebook crawler ( Организация, деятельность которой запрещена на территории РФ ), подробнее тут - Аномальная нагрузка на VPS под управлением BitrixVM и причем тут crawler? - подсветила важную проблему: каталог товаров может активно раздувать кеш catalog.section.
На практике это выглядело так:
- в bitrix/cache/s1/bitrix/catalog.section очень быстро появлялись новые файлы,
- объем кеша рос на десятки гигабайт,
- один кеш-файл сам по себе был достаточно тяжелым,
- а число комбинаций URL практически не имело естественного ограничения.
Разберем, почему это происходит и как ограничить кеширование таким образом, чтобы не отключать кеш полностью, а перестать кешировать самые токсичные запросы.
Почему именно catalog.section стал точкой роста?
В каталоге Битрикса компонент catalog.section фактически отвечает за выдачу списка товаров в разделе:
- с фильтрами,
- с сортировками,
- с пагинацией,
- с разными шаблонами отображения.
Если в проекте одновременно включены:
- фильтрация,
- CACHE_FILTER = Y,
- smart.filter в SEF-режиме,
- сортировка,
- пагинация,
- разные режимы отображения товаров,
тогда каждый новый вариант URL начинает восприниматься как отдельный сценарий выдачи и порождает новый кеш.
Именно это и происходило на проекте.
Что показал анализ кешей
Разбор содержимого файлов `catalog.section` показал, что кеш множится по комбинации параметров:
- раздел каталога,
- smart.filter,
- диапазонные фильтры from/to,
- PAGEN_1,
- sort,
- order,
- display,
- linerow.
Особенно опасными оказались диапазонные фильтры вида:
- prop_shirina-from-330
- prop_dlina-from-2250
- price-base-from-1000
Такие URL потенциально можно генерировать практически бесконечно, даже если фактического смысла у большинства значений нет.
Отдельным множителем выступали глубокая пагинация и вариативные параметры интерфейса:
- PAGEN_1=21
- PAGEN_1=53
- sort=SHOWS
- display=table
- linerow=3
Все это приводило к лавинообразному увеличению числа кеш-файлов.
Почему не хотелось просто отключить кеш целиком?
Самое простое решение выглядело бы так:
"CACHE_TYPE" => "N",
"CACHE_FILTER" => "N",
Но для реального интернет магазина это плохой путь. Полный отказ от кеша на каталоге:
- увеличивает нагрузку на БД,
- замедляет выдачу для обычных пользователей,
- убирает преимущества кеширования там, где оно действительно полезно.
Поэтому задача была не в том, чтобы убрать кеш вообще, а в том, чтобы перестать кешировать только самые дорогие и бесконтрольные варианты запросов.
Идея решения
Мы вынесли правила в отдельный централизованный класс CatalogCachePolicy, который на уровне запроса принимает решение:
- можно ли оставлять кеш включенным,
- или для конкретного URL его нужно отключить.
Ключевая идея:
- для обычных страниц раздела кеш работает как раньше,
- для опасных комбинаций кеш отключается автоматически.
Такой подход оказался намного безопаснее, чем хаотичные локальные правки в шаблонах компонентов.
Что именно решили отключать от кеширования
В итоговую политику вошли несколько правил.
1. Диапазонные фильтры
Если URL содержит -from- или -to-, кеш для такого запроса отключается.
Это самый важный пункт, потому что именно диапазоны дают почти бесконечное число вариантов URL.
2. Глубокая пагинация
Для обычных страниц раздела был введен лимит по пагинации.
Например:
- для большинства разделов кеш выключается при PAGEN_1 > 5,
- для тяжелых разделов лимит строже.
3. Глубокая пагинация на фильтрах
Для фильтрованных страниц лимит еще ниже. Это логично: фильтрованная выдача сама по себе сильно дробит кеш.
4. Нестандартная сортировка
Если запрос использует сортировку, отличную от стандартной, кеш отключается.
5. Нестандартный display
Если пользователь или бот запрашивает не базовый тип отображения списка, кеш не создается.
6. Нестандартный linerow
Если меняется число колонок в листинге, этот вариант тоже не должен раздувать кеш.
7. Ajax-перезагрузки
Ajax-ответы каталога не должны плодить тяжелые кеши выдачи.
Пример реализации
Ниже упрощенный пример логики policy:
class CatalogCachePolicy
{
public static function getCatalogComponentCacheOptions(): array
{
$cacheType = 'A';
$cacheFilter = 'Y';
$isRangeFilter = self::containsRangeFilter();
$page = self::resolvePageNumber();
$hasNonDefaultSort = self::hasNonDefaultSort();
$hasNonDefaultDisplay = self::hasNonDefaultDisplay();
$hasNonDefaultLineRow = self::hasNonDefaultLineRow();
if (
$isRangeFilter
|| $page > 5
|| $hasNonDefaultSort
|| $hasNonDefaultDisplay
|| $hasNonDefaultLineRow
) {
$cacheType = 'N';
$cacheFilter = 'N';
}
return [
'CACHE_TYPE' => $cacheType,
'CACHE_FILTER' => $cacheFilter,
];
}
}
В реальном проекте логика была оформлена более детально и с отдельными правилами для разных разделов каталога.
Конфиг выглядел примерно так:
CatalogCachePolicy::configure([
'rules' => [
'disable_for_ajax' => true,
'disable_for_range_filters' => true,
'disable_for_page_gt' => 5,
'disable_for_filtered_page_gt' => 3,
'disable_for_non_default_sort' => true,
'default_sort_field' => 'sort',
'default_sort_order' => 'asc',
'disable_for_non_default_display' => true,
'default_display' => 'block',
'disable_for_non_default_linerow' => true,
'default_linerow' => '4',
],
'section_rules' => [
'lineynye-svetilniki' => [
'disable_for_page_gt' => 3,
'disable_for_filtered_page_gt' => 2,
],
'plastikovye-konteynery' => [
'disable_for_filtered_page_gt' => 2,
],
],
]);
А затем вычисленные значения передавались в bitrix:catalog:
$catalogCacheOptions = \Offer\CatalogCachePolicy::getCatalogComponentCacheOptions();
$APPLICATION->IncludeComponent(
"bitrix:catalog",
"main",
[
"CACHE_TYPE" => $catalogCacheOptions["CACHE_TYPE"],
"CACHE_FILTER" => $catalogCacheOptions["CACHE_FILTER"],
// ...
]
);
Почему smart.filter сыграл ключевую роль
При анализе стало видно, что огромную долю новых кешей порождали URL smart.filter.
Сам по себе smart.filter полезен:
- он формирует человекочитаемые SEO-URL,
- позволяет строить страницы под различные свойства товаров,
- помогает пользователям быстро сужать выдачу.
Но в связке с CACHE_FILTER = Y и отсутствием жесткой валидации URL появляется серьезный побочный эффект: можно генерировать огромное число страниц, в том числе для несуществующих или бессмысленных комбинаций значений.
Каждый такой URL становится кандидатом на отдельный кеш.
Именно поэтому ограничение кеширования решает только часть проблемы. Если не контролировать сами URL smart.filter, система все равно будет получать бесконечный поток вариаций.
Результат
После внедрения policy поведение каталога стало заметно стабильнее:
- тяжелые и бесполезные варианты URL перестали создавать новые кеши,
- рост директории catalog.section стал контролируемым,
- обычные страницы разделов сохранили преимущества кеширования,
- нагрузка от токсичных комбинаций фильтров и пагинации резко снизилась.
Мы не убрали кеш, а сделали его управляемым.
Проблему разрастания catalog.section редко можно решить одним флажком. Обычно на размер кеша влияет несколько факторов:
- CACHE_FILTER = Y,
- smart.filter,
- диапазонные фильтры,
- пагинация,
- сортировки,
- разные режимы отображения списка.
Правильный подход здесь — централизованная политика кеширования, которая оставляет кеш там, где он полезен, и отключает его там, где он начинает работать против проекта.
Но чтобы проблема была закрыта полностью, недостаточно ограничить только кеш. Нужно еще научить каталог корректно обрабатывать несуществующие URL smart.filter. Именно это мы и сделали следующим этапом, о котором подробно расскажем в третьей статье.