BitrixRedisBash

Оптимизация Redis для Битрикс: фоновая очистка тегов без потери сессий

Redis на Битриксе съел 977 МБ из 1 ГБ. Почему FLUSHDB — плохая идея, как найти, чем забита память, и как чистить кеш точечно, не разлогинив пользователей. Скрипты и чек-лист настройки.

РезультатОсвобождено 97% RAM
Александр Бутаков
Александр Бутаков
обновлено 4 мин чтения

Если Redis на проекте с Битриксом упёрся в лимит памяти, не спешите делать FLUSHDB: если сессии лежат в той же базе, он разлогинит всех пользователей и очистит корзины. Сначала найдите, чем именно забита память (redis-cli --bigkeys, INFO memory), потом удаляйте только мусорные ключи по маске через SCAN и UNLINK. В этом кейсе так освободилось 97% памяти без единой потерянной сессии.

Что случилось

Проект на Битриксе упёрся в лимиты железа хостинга. Redis занимал 977 МБ из доступного 1 ГБ, INFO memory показывал критическое заполнение. Политика вытеснения LRU не справлялась: теги записывались быстрее, чем Redis успевал выбрасывать старое.

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

Почему Redis на Битриксе забивается

Битрикс кладёт в Redis не только сам кеш, но и служебные наборы тегов, по которым потом сбрасывает связанный кеш. Если каталог часто обновляется (импорт цен и остатков, правки товаров), таких наборов становится всё больше, и многие из них давно никому не нужны. Добавьте сюда сессии — и всё это живёт в одной памяти с одним лимитом.

Отдельная ловушка — политика вытеснения. Посмотреть её можно так:

Bash
redis-cli CONFIG GET maxmemory
redis-cli CONFIG GET maxmemory-policy

При volatile-lru Redis вытесняет только ключи с заданным временем жизни, а ключи без TTL копятся бесконечно. При allkeys-lru под вытеснение попадает всё подряд — включая сессии, если они в том же экземпляре. Ни один вариант не хорош, пока кеш и сессии делят одну память.

Шаг 1. Понять, чем занята память

Не гадать, а смотреть:

Bash
# общая картина: used_memory_human, maxmemory_human
redis-cli INFO memory
# растёт ли вытеснение
redis-cli INFO stats | grep evicted_keys

# самые крупные ключи каждого типа, без блокировки сервера
redis-cli --bigkeys

# сколько весит конкретный ключ
redis-cli MEMORY USAGE "ключ"

Если evicted_keys растёт — Redis уже выбрасывает данные под давлением, и выбрасывает не обязательно то, что вы хотели бы.

Аудит через --bigkeys в этом кейсе показал причину сразу: 84% ключей оказались протухшими наборами тегов.

Шаг 2. Удалять точечно, а не всё

Вместо полного сброса я написал bash-скрипт: он находит ключи по маске и удаляет их асинхронно через UNLINK. В отличие от DEL, UNLINK освобождает память в фоне и не блокирует основной поток Redis — для живого магазина это важно.

Bash
REDIS_CLI="/usr/bin/redis-cli"
# TODO: маски подберите под свой проект по результатам --bigkeys
$REDIS_CLI --scan --pattern '*IL:*' | xargs -L 100 $REDIS_CLI unlink
$REDIS_CLI --scan --pattern '*domain.com/public_html*' | xargs -L 100 $REDIS_CLI unlink

Почему именно --scan, а не KEYS: KEYS * проходит всю базу за один вызов и на большом объёме подвешивает Redis. SCAN идёт порциями, и сервер продолжает отвечать.

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

Bash
redis-cli --scan --pattern '*IL:*' | head -50
redis-cli --scan --pattern '*IL:*' | wc -l

Скрипт поставил в cron на часы минимальной нагрузки.

Шаг 3. Не отдавать первым посетителям холодный кеш

Очистка — половина дела: после неё кеш пустой, и кто-то должен первым дождаться тяжёлой генерации. Я добавил прогрев: сервер читает sitemap и сам запрашивает нужные страницы через curl до прихода реальных посетителей.

Bash
# TODO: замените домен и число страниц под свой sitemap
urls=$(curl -s https://domain.com/sitemap.xml | grep -oP '(?<=<loc>).*?(?=</loc>)' | head -n 10)
for url in $urls; do
  curl -s -L -o /dev/null "$url"
done

Здесь греются первые страницы из sitemap. Для большого каталога прогрев надо строить иначе — с отбором разделов и троттлингом, подробно разбирал это в кейсе про каталог на 200 тыс. SKU.

Результат

  • Освобождено 97% RAM — потребление упало с 688 МБ до 19 МБ
  • Пользовательские сессии полностью сохранены при очистке
  • Ответ сервера для первых посетителей сокращён до миллисекунд
  • Остались на текущем тарифе хостинга, апгрейд железа не понадобился

Как настроить Redis для Битрикса, чтобы это не повторилось

Что советую проверить на любом проекте:

  1. Развести кеш и сессии. Минимум — разные базы Redis (db 0 под кеш, db 1 под сессии), лучше — разные экземпляры со своими лимитами памяти. Тогда сброс кеша физически не может задеть сессии.
  2. Выбрать политику под назначение. Для экземпляра с кешем подходит allkeys-lru: выбросить старый кеш не страшно. Для сессий — noeviction и запас памяти: молча терять сессии хуже, чем получить ошибку записи.
  3. Задать maxmemory явно. Без лимита Redis растёт, пока его не убьёт система.
  4. Мониторить used_memory и evicted_keys. Рост вытеснений — ранний сигнал, задолго до того, как закончится память.
  5. Регулярно чистить протухшие теги по маске, как выше, и держать прогрев после очистки.

FAQ

Можно ли сделать FLUSHDB в Redis на Битриксе? Только если вы точно знаете, что сессии хранятся в другой базе или другом экземпляре. Иначе FLUSHDB разлогинит всех пользователей и очистит корзины. Безопаснее удалять ключи по маске через SCAN и UNLINK.

Как узнать, чем забита память Redis? Команда redis-cli INFO memory показывает общее потребление, redis-cli --bigkeys — самые крупные ключи по типам, MEMORY USAGE — вес конкретного ключа. Этого обычно хватает, чтобы найти виновника.

Чем UNLINK лучше DEL? UNLINK удаляет ключ из пространства имён сразу, а память освобождает в фоновом потоке. DEL делает всё синхронно и на больших ключах может заметно подвесить Redis.

Какую maxmemory-policy выбрать для Битрикса? Для кеша — allkeys-lru. Для сессий — noeviction с запасом памяти, и лучше в отдельном экземпляре Redis, чтобы вытеснение кеша не трогало сессии.

Статья была полезной?

Нужна помощь с похожей задачей?

Поддержка сайтов →