Архив рассылок сообщества снова открыт. Читать письма 2003-2009
Главная/Блог/Эксплуатация
Эксплуатация13 мин чтенияНиколай Ковалёв

Redis на Linux: установка, настройка и кэширование для веб-приложений

От apt install до продакшена: сеть и сокет, память, персистентность, ACL, паттерн cache-aside, метрики попаданий и чек-лист перед запуском.

Серверная стойка с подсвеченными модулями и потоки данных к ноутбуку в тёмном дата-центре

Коротко: Redis ставится на Ubuntu или Debian одной командой apt install redis-server, но в продакшене его нужно донастроить: слушать только localhost или unix-сокет, задать пароль через ACL, ограничить память параметром maxmemory и выбрать политику вытеснения allkeys-lru или allkeys-lfu. Для чистого кэша сохранение на диск можно отключить, для сессий и очередей — оставить AOF. Ниже — конфиг, команды проверки и типичные ошибки.

Redis — хранилище «ключ — значение» в оперативной памяти. Ответ на GET приходит за доли миллисекунды, поэтому его ставят перед базой данных: кэш страниц и выборок, сессии пользователей, счётчики, лимиты запросов, простые очереди задач. Поставить его легко, а вот забытый maxmemory или открытый наружу порт 6379 превращают удобный кэш в источник падений и утечек.

Кэш — это решение не только администратора, но и разработчиков. Если веб-приложение вам делает подрядчик, например YuSMP Group, ещё на этапе архитектуры договоритесь: какие данные попадут в Redis, сколько они живут, что приложение делает, если Redis недоступен, и кто отвечает за сервер после запуска. Тогда конфиг ниже ляжет на понятные требования, а не на догадки.

Когда Redis действительно нужен, а когда нет?

Redis ускоряет то, что приложение много раз читает и редко меняет. Если узкое место в медленном SQL-запросе без индекса, кэш спрячет проблему, но не решит её: сначала посмотрите план запроса, а настройки самой базы мы разбирали в статье PostgreSQL на Linux: настройка и тюнинг.

ЗадачаПодходит ли RedisЧто важно
Кэш тяжёлых выборок и HTML-фрагментовДа, основной сценарийTTL на каждый ключ, политика вытеснения
Сессии пользователейДаВключить AOF, иначе рестарт разлогинит всех
Rate limiting, счётчикиДаАтомарные INCR и EXPIRE
Очередь фоновых задачДа, для небольшой нагрузкиПерсистентность, отдельный инстанс от кэша
Основное хранилище данныхОбычно нетВсё должно влезать в RAM, сложнее бэкап и восстановление
Объектный кэш WordPressДа, если много запросов к базеПлагин и PHP-расширение; на маленьком сайте выигрыш может быть незаметен

Если сомневаетесь, замерьте. Время ответа до и после включения кэша и число запросов к базе на страницу скажут больше, чем общие советы.

Как установить Redis на Ubuntu и Debian?

Пакет есть в стандартных репозиториях, и для большинства задач его версии хватает:

sudo apt update
sudo apt install redis-server -y
sudo systemctl enable --now redis-server
redis-cli ping

Ответ PONG значит, что сервер запущен и принимает команды. Версию из репозитория дистрибутива обновляют редко. Если нужна свежая ветка, подключите официальный репозиторий packages.redis.io по инструкции на redis.io.

Redis или Valkey?

Для админа разница пока небольшая: Valkey совместим с Redis по протоколу и командам, конфиг почти тот же, клиентские библиотеки работают без изменений. Появился форк из-за лицензии: в 2024 году Redis её сменил, и сообщество под крылом Linux Foundation выпустило Valkey под BSD. Позже Redis добавил вариант лицензии AGPLv3. Если ваш дистрибутив предлагает пакет valkey, всё ниже применимо с заменой имени сервиса и файла конфигурации.

Базовая настройка redis.conf для продакшена

Основной файл — /etc/redis/redis.conf. Перед правкой сохраните копию: sudo cp /etc/redis/redis.conf /etc/redis/redis.conf.orig. Дальше по порядку — пять блоков, без которых Redis в продакшен выпускать не стоит.

Интеграция с systemd

supervised systemd

Так systemd узнаёт, что Redis действительно готов принимать соединения, а не просто запустил процесс. Как устроены юниты и зависимости между сервисами, разобрано в статье systemd без боли.

Сеть: только localhost или unix-сокет

bind 127.0.0.1 -::1
protected-mode yes
port 6379
# если приложение на том же сервере, сокет быстрее TCP:
unixsocket /run/redis/redis-server.sock
unixsocketperm 770

Если Redis и приложение живут на одной машине, порт наружу не нужен вовсе: можно поставить port 0 и работать только через сокет. Пользователя, под которым запущено приложение, добавьте в группу redis, иначе он не получит доступ к сокету.

Память и политика вытеснения

maxmemory 512mb
maxmemory-policy allkeys-lru

По умолчанию maxmemory не задан, а политика — noeviction. Redis растёт, пока не съест всю RAM, после чего его убивает OOM killer или он начинает отвечать ошибкой на запись. Для кэша оставьте под Redis не больше половины свободной памяти сервера, особенно если рядом работает база.

Сохранение на диск: RDB, AOF или ничего

# чистый кэш — данные можно потерять без последствий
save ""
appendonly no

# сессии и очереди — нужна устойчивость к рестарту
appendonly yes
appendfsync everysec

RDB — периодический снимок всей базы, AOF — журнал команд. При appendfsync everysec в худшем случае теряется около секунды записей, а производительность почти не страдает.

Ядро: overcommit памяти

При старте Redis пишет в лог предупреждение про vm.overcommit_memory. Без этой настройки фоновое сохранение через fork() может упасть на загруженном сервере:

echo 'vm.overcommit_memory = 1' | sudo tee /etc/sysctl.d/90-redis.conf
sudo sysctl --system

Что делает этот параметр, описано в документации ядра. После всех правок перезапустите сервис: sudo systemctl restart redis-server, затем проверьте журнал командой journalctl -u redis-server -n 50.

Какую политику вытеснения выбрать?

Для кэша почти всегда подходит allkeys-lru или allkeys-lfu. Остальные политики нужны, когда в одном инстансе лежат и временные, и постоянные данные.

ПолитикаЧто удаляет при нехватке памятиКогда использовать
noevictionНичего, запись возвращает ошибкуОчереди и данные, которые терять нельзя
allkeys-lruДавно не использованные ключиУниверсальный кэш
allkeys-lfuРедко используемые ключиКэш с явными «горячими» ключами
volatile-lruДавно не использованные ключи с TTLКэш и постоянные ключи в одном инстансе
volatile-ttlКлючи с ближайшим истечением TTLКогда TTL отражает ценность данных
allkeys-randomСлучайные ключиРедко; при равномерном доступе ко всем ключам

Если используете volatile-*, а у ключей нет TTL, Redis удалять нечего, и поведение будет как у noeviction. Это частая причина внезапных ошибок OOM command not allowed.

Как защитить Redis: пароль, ACL и опасные команды

Redis, открытый в интернет без пароля, находят сканеры за минуты: через CONFIG SET и SAVE на такой сервер подкладывали SSH-ключи и майнеры. Поэтому порядок такой: сначала закрыть сеть, затем ACL. Общая база по защите сервера — в статье Защита Linux-сервера: база.

С версии 6 вместо одного requirepass есть ACL: у каждого приложения свой пользователь и только нужные права. Пример для приложения, которое работает с ключами по префиксу cache::

# в redis.conf или в отдельном файле aclfile
user default off
user app on >длинный-случайный-пароль ~cache:* +@read +@write +@keyspace -@dangerous
user admin on >другой-пароль ~* +@all

Пользователь default выключен, поэтому анонимное подключение не пройдёт. У app нет доступа к CONFIG, FLUSHALL, KEYS и другим командам из категории @dangerous. Проверить права можно командой ACL WHOAMI и попыткой выполнить запрещённую команду. Пароли храните в менеджере секретов, а не в репозитории, — частые ошибки разобраны в статье Секреты в проде.

Если Redis нужен приложению на другом сервере, не открывайте 6379 на весь мир. Варианты по надёжности: приватная сеть провайдера, WireGuard между серверами, правило ufw только для IP приложения и TLS (tls-port, если Redis собран с поддержкой TLS).

Кэширование в приложении: паттерн cache-aside

Самая распространённая схема: приложение сначала ищет данные в Redis, при промахе идёт в базу и кладёт результат в кэш с TTL. Пример на Python с библиотекой redis-py:

import json, random, redis

r = redis.Redis(unix_socket_path="/run/redis/redis-server.sock",
                username="app", password=APP_PASSWORD)

def get_product(product_id):
    key = f"cache:product:{product_id}"
    cached = r.get(key)
    if cached:
        return json.loads(cached)
    product = db_fetch_product(product_id)        # медленный запрос к базе
    ttl = 300 + random.randint(0, 60)             # разброс TTL против лавины
    r.set(key, json.dumps(product), ex=ttl)
    return product

Три правила, которые экономят нервы

  1. TTL у каждого ключа. Ключ без срока жизни будет лежать вечно и отдавать устаревшие данные после изменения в базе.
  2. Разброс TTL. Если тысяча ключей создана одновременно с одинаковым TTL, они и истекут одновременно, и вся нагрузка упадёт на базу разом. Случайная добавка в 10–20 % сглаживает пик.
  3. Инвалидация при записи. Изменили товар в базе — удалите ключ DEL cache:product:42. Следующий запрос возьмёт свежие данные.

Приложение должно переживать недоступность Redis: при ошибке соединения логировать её и идти напрямую в базу, а не отдавать пользователю 500. Кэш ускоряет сервис, но не должен становиться точкой отказа.

Как проверить, что кэш работает?

Главная метрика — доля попаданий. Её считают по двум счётчикам из INFO stats:

redis-cli --user admin --askpass INFO stats | grep -E 'keyspace|evicted_keys'
# keyspace_hits:182340
# keyspace_misses:20117
# evicted_keys:0
redis-cli --user admin --askpass INFO memory | grep -E 'used_memory_human|maxmemory_human'
redis-cli --user admin --askpass --latency
redis-cli --user admin --askpass SLOWLOG GET 10

Hit rate = hits / (hits + misses). В примере это около 90 %, хороший результат для кэша выборок. Если доля попаданий низкая, TTL слишком короткий или ключи строятся так, что почти не повторяются. Если быстро растёт evicted_keys, кэшу не хватает памяти: увеличьте maxmemory или сократите то, что в нём хранится.

Для постоянного наблюдения подключите redis_exporter к Prometheus и выведите hit rate, память и число соединений на дашборд. Как собрать такую связку, мы показывали в статье Мониторинг Linux-сервера: Prometheus и Grafana.

Частые ошибки при настройке Redis

СимптомПричинаЧто сделать
Redis убит OOM killer, сервер тормозитНе задан maxmemoryЗадать лимит и политику allkeys-lru
OOM command not allowedЛимит достигнут, политика noeviction или volatile-* без TTLСменить политику или проставить TTL
Redis «тормозит» раз в несколько минутКоманда KEYS * в коде или cronЗаменить на SCAN, запретить KEYS через ACL
После перезагрузки все пользователи разлогиненыСессии в Redis без AOFВключить appendonly yes
Permission denied на сокетеПользователь приложения не в группе redisusermod -aG redis www-data, перезапустить приложение
Фоновое сохранение падает с ошибкой forkvm.overcommit_memory = 0Выставить 1 через sysctl

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

Бэкап Redis: что и когда копировать

Для чистого кэша бэкап не нужен: после рестарта он наполнится сам. Сессии и очереди копируют так же, как другие данные. Сделайте BGSAVE или включите AOF, затем вынесите файлы из /var/lib/redis/ за пределы сервера. Общая схема, которая переживает потерю целого сервера, — в статье Backup-стратегии: правило 3-2-1. Проверка восстановления обязательна: запустите Redis на тестовой машине с копией файла и посчитайте ключи командой DBSIZE.

Чек-лист перед запуском в продакшен

  • Redis слушает только 127.0.0.1, сокет или приватную сеть; ss -tlnp | grep 6379 не показывает 0.0.0.0.
  • Пользователь default выключен, у приложения свой ACL-пользователь без @dangerous.
  • Заданы maxmemory и политика вытеснения под сценарий.
  • Персистентность выбрана осознанно: для кэша выключена, для сессий и очередей включён AOF.
  • vm.overcommit_memory = 1, в журнале при старте нет предупреждений.
  • У каждого ключа кэша есть TTL, в коде нет KEYS *.
  • Приложение работает, если Redis недоступен.
  • Hit rate, память и evicted_keys выведены в мониторинг.

Частые вопросы о Redis на Linux

Сколько памяти выделить Redis под кэш?

Начните с 256–512 МБ для небольшого сайта и смотрите на evicted_keys и hit rate. Если вытеснение идёт постоянно, а доля попаданий низкая, увеличивайте лимит. Оставляйте запас RAM для базы, приложения и fork при сохранении.

Чем Redis отличается от Memcached?

Memcached хранит только строки и не умеет сохранять данные на диск. Redis поддерживает списки, хэши, множества, сортированные множества, TTL на ключ, персистентность, репликацию и ACL. Для простого кэша подойдут оба, но для сессий, счётчиков и очередей удобнее Redis.

Можно ли держать кэш и очередь в одном инстансе Redis?

Технически да, но лучше разделить. Кэшу нужна политика вытеснения, а очереди — noeviction и AOF. В одном инстансе придётся выбирать компромисс, и при нехватке памяти кэш может вытеснить задачи из очереди.

Нужен ли Redis Cluster или Sentinel?

Для одного веб-приложения на одном сервере — нет. Sentinel нужен, когда требуется автоматическое переключение на реплику при падении основного узла. Cluster нужен, когда данные не помещаются в память одной машины. Для кэша обычно хватает одного инстанса и корректной обработки его недоступности в коде.

Почему нельзя использовать KEYS в продакшене?

Redis выполняет команды в одном потоке, и KEYS * перебирает все ключи, блокируя остальные запросы. На базе в миллионы ключей это секунды простоя. Используйте SCAN с курсором: он обходит ключи порциями и не блокирует сервер.

Как очистить кэш Redis?

FLUSHDB удаляет ключи текущей базы, FLUSHALL — все базы. Обе команды необратимы, поэтому выполняйте их от админского ACL-пользователя и только когда точно знаете, что в инстансе нет сессий и очередей. Удалить ключи по префиксу безопаснее через SCAN с MATCH cache:* и UNLINK.

Стоит ли запускать Redis в Docker?

Можно, если остальной стек уже в контейнерах. Не публикуйте порт контейнера на 0.0.0.0: Docker обходит правила ufw. Смонтируйте volume для /data, если нужна персистентность, и передайте свой redis.conf. Основы — в статье Docker-контейнеры простыми словами.