Client (redis-cli, приложение) → Redis Server (однопоточная event loop) → Database (0-15, по умолчанию 0) → Key → Value (string, hash, list, set, sorted set, stream, …)
In-memory: все данные в оперативной памяти. Чтение/запись за O(1), то есть микросекунды. Данные можно персистить на диск (RDB, AOF), но основной storage остаётся RAM.
Однопоточность: команды выполняются последовательно, одна за другой. Нет race conditions, нет блокировок. Одна команда является атомарной операцией. Пропускная способность: сотни тысяч операций в секунду.
Key-Value: всё хранится как ключ → значение. Ключ всегда является строкой. Значение представляет собой одну из структур данных (string, hash, list, set, sorted set, stream, …).
TTL: время жизни ключа. По истечении происходит автоматическое удаление. Основной механизм для кешей и сессий.
Pub/Sub: публикация/подписка на каналы. Для real-time уведомлений.
Lua scripting: атомарное выполнение нескольких команд на сервере.
redis-cli # интерактивный режимredis-cli GET mykey # одноразовая командаredis-cli -n 2 GET mykey # база данных №2redis-cli --scan --pattern "user:*"# перебрать ключи по паттернуredis-cli --bigkeys # найти самые большие ключиredis-cli --memkeys # потребление памяти по ключамredis-cli --stat # мониторинг в реальном времениredis-cli --latency # измерить задержкуredis-cli --pipe < commands.txt # массовое выполнение (pipeline)redis-cli MONITOR # логировать все команды в реальном времени
Общие команды
SELECT 2# переключиться на базу 2DBSIZE # количество ключей в текущей базеFLUSHDB # удалить все ключи в текущей базеFLUSHALL # удалить все ключи во всех базахINFO # полная информация о сервереINFO memory # только секция памятиINFO stats # статистика операцийINFO keyspace # статистика по базамCONFIG GET maxmemory # получить настройкуCONFIG SET maxmemory 512mb # изменить настройку на летуSLOWLOG GET 10# последние 10 медленных командCLIENT LIST # подключённые клиенты
Структуры данных
String
Базовый тип. Хранит строку, число или бинарные данные (до 512 MB).
# СтрокиSET name "Alice"# установитьGET name # получить → "Alice"GETSET name "Bob"# получить старое и установить новоеSETNX name "Carol"# установить только если не существует (atomic)MSET k1 "v1" k2 "v2" k3 "v3"# установить несколькоMGET k1 k2 k3 # получить несколько → ["v1", "v2", "v3"]APPEND name " Smith"# дописать → "Bob Smith"STRLEN name # длина → 9GETRANGE name 02# подстрока → "Bob"SETRANGE name 0"Rob"# заменить с позиции → "Rob Smith"# Числа (строки, которые Redis интерпретирует как числа)SET counter 10INCR counter # +1 → 11 (атомарно)INCRBY counter 5# +5 → 16DECR counter # -1 → 15DECRBY counter 3# -3 → 12INCRBYFLOAT price 0.50 # +0.50 (float)# TTL — время жизниSET session "data" EX 3600# истекает через 3600 секундSET session "data" PX 60000# через 60000 миллисекундSET token "abc" EXAT 1735689600# в конкретный unix timestampSETEX session 3600"data"# SET + EX (старый синтаксис)PSETEX session 60000"data"# SET + PX# SET с опциями (Redis 6.2+)SET lock "owner1" NX EX 30# SET если не существует + TTL 30s (distributed lock)SET key "val" XX # SET только если уже существуетSET key "val" GET # SET и вернуть предыдущее значение# TTL-команды (для любого типа)EXPIRE key 60# установить TTL 60 секундPEXPIRE key 60000# TTL в миллисекундахEXPIREAT key 1735689600# TTL до конкретного timestampTTL key # оставшееся время (-1 = нет TTL, -2 = не существует)PTTL key # TTL в миллисекундахPERSIST key # убрать TTL (сделать вечным)
Hash
Словарь (поле → значение) внутри одного ключа. Идеален для объектов.
# Установить поляHSET user:1 name "Alice" email "alice@example.com" role "admin"HSETNX user:1 phone "+7900"# только если поле не существует# ПолучитьHGET user:1 name # → "Alice"HMGET user:1 name email role # несколько полей → ["Alice", "alice@...", "admin"]HGETALL user:1 # все поля и значения → [name, Alice, email, ...]# ПроверкиHEXISTS user:1 phone # поле существует? → 0/1HLEN user:1 # количество полей → 4HKEYS user:1 # все ключи → [name, email, role, phone]HVALS user:1 # все значения → [Alice, alice@..., admin, +7900]# Удалить полеHDEL user:1 phone
# Числовые поляHSET product:1 views 0 price 29.99
HINCRBY product:1 views 1# +1 → 1 (атомарно)HINCRBYFLOAT product:1 price -5.00 # -5.00 → 24.99# Итерация (курсор, safe для больших хешей)HSCAN user:1 0 MATCH "e*" COUNT 100
Паттерн: Hash вместо множества String-ов
ПЛОХО (3 ключа на пользователя):
SET user:1:name "Alice"SET user:1:email "alice@example.com"SET user:1:role "admin"
ХОРОШО (1 ключ = 1 объект):
HSET user:1 name "Alice" email "alice@example.com" role "admin"
Hash с малым числом полей хранится в ziplist — компактнее и быстрее.
List
Двусвязный список. Быстрая вставка/удаление с обоих концов. Для очередей, стеков, последних N элементов.
# ДобавитьLPUSH queue "task3""task2""task1"# в начало (слева) → [task1, task2, task3]RPUSH queue "task4"# в конец (справа) → [task1, task2, task3, task4]# ПолучитьLRANGE queue 0 -1 # весь список → [task1, task2, task3, task4]LRANGE queue 02# первые 3 → [task1, task2, task3]LINDEX queue 0# по индексу → "task1"LLEN queue # длина → 4# Извлечь (удаляет элемент)LPOP queue # из начала → "task1"RPOP queue # из конца → "task4"LPOP queue 2# несколько из начала → ["task2", "task3"]# Очередь с блокировкой (worker ждёт новых задач)BLPOP queue 30# блокировать до 30 секундBRPOP queue 0# блокировать бесконечно# Перемещение между списками (атомарно)LMOVE source dest LEFT RIGHT # взять из source слева, положить в dest справаBLMOVE source dest LEFT RIGHT 30# блокирующий вариант# Обрезать (оставить только [start, stop])LTRIM notifications 099# оставить первые 100 элементов# Удалить по значениюLREM queue 1"task2"# удалить 1 вхождение "task2"LREM queue 0"task2"# удалить все вхождения# Установить по индексуLSET queue 0"new_task"
Паттерн
Команды
Очередь (FIFO)
RPUSH (добавить) + LPOP (взять)
Стек (LIFO)
LPUSH (добавить) + LPOP (взять)
Capped list
LPUSH + LTRIM 0 99 (последние 100 элементов)
Worker queue
RPUSH + BLPOP (блокирующий потребитель)
Reliable queue
RPUSH + LMOVE (перемещение в processing-список)
Set
Неупорядоченное множество уникальных строк. Для тегов, ролей, проверки членства, пересечений.
# ДобавитьSADD tags "redis""cache""nosql"SADD online:users "user:1""user:2""user:3"# ПолучитьSMEMBERS tags # все элементы → [redis, cache, nosql]SCARD tags # количество → 3SISMEMBER tags "redis"# входит ли? → 1 (true)SMISMEMBER tags "redis""sql""cache"# несколько проверок → [1, 0, 1]SRANDMEMBER tags 2# 2 случайных (без удаления)SPOP tags # случайный + удалить# УдалитьSREM tags "nosql"# удалить элемент# Операции над множествамиSADD set1 "a""b""c"SADD set2 "b""c""d"SINTER set1 set2 # пересечение → [b, c]SUNION set1 set2 # объединение → [a, b, c, d]SDIFF set1 set2 # разность (в set1, но не в set2) → [a]# Сохранить результатSINTERSTORE result set1 set2 # пересечение → resultSUNIONSTORE result set1 set2 # объединение → result# ИтерацияSSCAN tags 0 MATCH "re*" COUNT 100
Sorted Set (ZSet)
Множество с числовым score для каждого элемента. Автоматическая сортировка по score. Для рейтингов, приоритетных очередей, time series.
# Добавить (score, member)ZADD leaderboard 1500"alice"1200"bob"1800"carol"# Добавить с опциямиZADD leaderboard NX 1000"dave"# только если не существуетZADD leaderboard XX 1600"alice"# только если существует (обновить)ZADD leaderboard GT 1400"alice"# обновить только если новый score большеZADD leaderboard LT 1400"alice"# обновить только если новый score меньшеZINCRBY leaderboard 50"alice"# увеличить score на 50# По рангу (позиции)ZRANGE leaderboard 0 -1 # все, от низшего score к высшемуZRANGE leaderboard 0 -1 WITHSCORES # с score-амиZRANGE leaderboard 02# топ-3 (по возрастанию)ZREVRANGE leaderboard 02# топ-3 (по убыванию) — для рейтингаZRANGE leaderboard 09 REV WITHSCORES # топ-10 (Redis 6.2+ синтаксис)ZRANK leaderboard "alice"# позиция (0-based, от низшего)ZREVRANK leaderboard "alice"# позиция (от высшего) — место в рейтинге# По scoreZRANGEBYSCORE leaderboard 10001500# score от 1000 до 1500ZRANGEBYSCORE leaderboard -inf +inf # всеZRANGEBYSCORE leaderboard (10001500# score > 1000 (исключая)ZCOUNT leaderboard 10001500# количество в диапазоне# ИнформацияZSCORE leaderboard "alice"# score элементаZMSCORE leaderboard "alice""bob"# несколько score-овZCARD leaderboard # количество элементов# УдалитьZREM leaderboard "dave"ZREMRANGEBYSCORE leaderboard -inf 1000# удалить все с score <= 1000ZREMRANGEBYRANK leaderboard 02# удалить первые 3 (с наименьшим score)# Операции над множествамиZINTERSTORE result 2 zset1 zset2 WEIGHTS 12# пересечение (score = s1*1 + s2*2)ZUNIONSTORE result 2 zset1 zset2 AGGREGATE MIN # объединение (score = min)# ИтерацияZSCAN leaderboard 0 MATCH "a*" COUNT 100
Stream
Append-only лог с consumer groups. Для очередей событий, event sourcing, message broker.
# Добавить запись (* = автогенерация ID)XADD events * type "purchase" user_id "42" amount "99.99"XADD events * type "pageview" user_id "42" page "/home"# ID формат: <timestamp_ms>-<sequence> → "1710500000000-0"# Явный IDXADD events 1710500000000-0 type "custom" data "value"# Максимальная длина стримаXADD events MAXLEN ~ 1000000 * type "event" data "value"# ~приблизительно 1M записейXADD events MINID ~ 1710000000000-0 * type "event"# удалить старше ID# ЧтениеXLEN events # количество записейXRANGE events - + # все записи (от первой до последней)XRANGE events - + COUNT 10# первые 10XRANGE events 1710500000000-0 + # от конкретного IDXREVRANGE events + - COUNT 10# последние 10# Блокирующее чтение (ожидание новых записей)XREAD COUNT 10 BLOCK 5000 STREAMS events $
# $ = только новые записи, BLOCK 5000 = ждать 5 секунд# Чтение нескольких стримовXREAD COUNT 10 BLOCK 0 STREAMS events notifications $ $
# --- Consumer Groups ---# Создать группу потребителейXGROUP CREATE events mygroup $ MKSTREAM
# $ = читать только новые, 0 = читать с начала# Читать как потребитель в группеXREADGROUP GROUP mygroup consumer1 COUNT 10 BLOCK 5000 STREAMS events >
# > = только новые (не прочитанные группой) записи# Подтвердить обработкуXACK events mygroup 1710500000000-0 1710500000001-0
# Необработанные записи (pending)XPENDING events mygroup - + 10# список pending записейXPENDING events mygroup IDLE 60000 - + 10# зависшие > 60 секунд# Забрать зависшие записи у другого потребителяXCLAIM events mygroup consumer2 60000 1710500000000-0
# Автоматический claim (проще)XAUTOCLAIM events mygroup consumer2 60000 0-0 COUNT 10# ИнформацияXINFO STREAM events # информация о стримеXINFO GROUPS events # группы потребителейXINFO CONSUMERS events mygroup # потребители в группе# УдалитьXDEL events 1710500000000-0 # удалить записьXTRIM events MAXLEN 1000000# обрезатьXGROUP DESTROY events mygroup # удалить группу
Consumer Group гарантии
Каждое сообщение доставляется ровно одному consumer-у в группе
Необработанные сообщения можно перехватить (XCLAIM) при сбое consumer-а
XACK подтверждает обработку (как в RabbitMQ/Kafka)
HyperLogLog
Вероятностная структура для подсчёта уникальных элементов. Фиксированно 12 KB памяти, ~0.81% погрешность. Для unique visitors, unique events.
PFADD visitors "user:1""user:2""user:3"PFADD visitors "user:2""user:4"# дубликат user:2 не считаетсяPFCOUNT visitors # ~4# Объединение (за все дни)PFADD visitors:day1 "u1""u2""u3"PFADD visitors:day2 "u2""u3""u4"PFMERGE visitors:week visitors:day1 visitors:day2
PFCOUNT visitors:week # ~4 (уникальные за оба дня)
Bitmap
Строка, интерпретируемая как массив бит. Компактный способ хранить boolean-флаги.
# Установить битSETBIT active_users:2025-03-15 421# пользователь 42 был активенSETBIT active_users:2025-03-15 1001# пользователь 100 был активен# ПроверитьGETBIT active_users:2025-03-15 42# → 1# Посчитать единицы (активных пользователей)BITCOUNT active_users:2025-03-15 # → 2# Побитовые операции (пересечение дней = пользователи активные оба дня)BITOP AND active_both active_users:2025-03-15 active_users:2025-03-16
BITOP OR active_any active_users:2025-03-15 active_users:2025-03-16
BITCOUNT active_both # пользователи, активные оба дня
# Поиск ключейKEYS user:* # найти по паттерну (БЛОКИРУЕТ! не для prod)SCAN 0 MATCH user:* COUNT 100# итеративный поиск (safe для prod)# SCAN возвращает [cursor, keys]. Повторять с новым cursor, пока cursor != 0# ИнформацияEXISTS key # существует? → 0/1EXISTS k1 k2 k3 # сколько из них существуют → 0-3TYPE key # тип → string/hash/list/set/zset/streamOBJECT ENCODING key # внутреннее кодирование → ziplist/hashtable/...OBJECT IDLETIME key # секунды с последнего доступаMEMORY USAGE key # потребление памяти в байтахDEBUG OBJECT key # подробная информация# TTLEXPIRE key 60# TTL 60 секундPEXPIRE key 60000# TTL в миллисекундахEXPIREAT key 1735689600# до unix timestampTTL key # оставшееся время (-1 нет TTL, -2 не существует)PERSIST key # убрать TTL# ПереименованиеRENAME key newkey
RENAMENX key newkey # только если newkey не существует# УдалениеDEL key # синхронное удалениеUNLINK key # асинхронное (неблокирующее) удалениеDEL k1 k2 k3 # несколько ключей# КопированиеCOPY source dest # скопировать значениеCOPY source dest REPLACE # перезаписать если dest существует# СериализацияDUMP key # сериализовать значениеRESTORE key 0"\x00\x..."# восстановить (0 = без TTL)
KEYS: никогда в production. Блокирует сервер на время сканирования. Используй SCAN.
Паттерны именования ключей
Паттерн
Пример
object:id:field
user:42:name
object:id
session:abc123
object:field:value
user:email:alice@example.com (обратный индекс)
env:object:id
prod:cache:query:7f3a2b
Разделитель : является конвенцией. Используй осмысленные префиксы, чтобы было легко группировать и находить ключи.
Транзакции и атомарность
MULTI/EXEC
# Группа команд, выполняемых атомарноMULTI # начать транзакциюSET balance:1 900SET balance:2 1100EXEC # выполнить всё# ОтменаMULTI
SET key "value"DISCARD # отменить всё# Optimistic locking с WATCHWATCH balance:1 # следить за ключомval = GET balance:1 # прочитатьMULTI
SET balance:1 (val - 100)# изменитьEXEC # если balance:1 изменился между WATCH и EXEC → nil (откат)# Если EXEC вернул nil — повторить попытку
MULTI/EXEC работает НЕ как в SQL. Нет ROLLBACK при ошибке одной команды. Все команды выполнятся, ошибочные вернут ошибку. Это гарантия атомарности выполнения, а не транзакционной изоляции.
Lua-скрипты
Полностью атомарное выполнение на сервере. Замена транзакций для сложной логики:
# Выполнить Lua-скриптEVAL "return redis.call('GET', KEYS[1])"1 mykey
# Rate limiter (атомарный)EVAL "
local current = tonumber(redis.call('GET', KEYS[1]) or '0')
if current >= tonumber(ARGV[1]) then
return 0
end
redis.call('INCR', KEYS[1])
if current == 0 then
redis.call('EXPIRE', KEYS[1], ARGV[2])
end
return 1
"1 ratelimit:user:42 10060# KEYS[1] = ratelimit:user:42, ARGV[1] = limit (100), ARGV[2] = window (60s)# Загрузить скрипт (вернёт SHA)SCRIPT LOAD "return redis.call('GET', KEYS[1])"# → "a42059b356c875f0717db19a51f6aaa9161571a2"# Вызвать по SHA (быстрее — не парсить каждый раз)EVALSHA "a42059b356c875f0717db19a51f6aaa9161571a2"1 mykey
# Redis Functions (Redis 7+, замена EVAL)FUNCTION LOAD "#!lua name=mylib
redis.register_function('my_get', function(keys, args)
return redis.call('GET', keys[1])
end)
"FCALL my_get 1 mykey
Pub/Sub
# Подписаться на канал (блокирующее)SUBSCRIBE notifications
SUBSCRIBE channel1 channel2
# Подписка по паттернуPSUBSCRIBE user:*:events # все каналы user:XXX:events# Опубликовать (из другого клиента)PUBLISH notifications '{"type": "new_order", "id": 123}'PUBLISH user:42:events '{"type": "login"}'# ОтписатьсяUNSUBSCRIBE notifications
PUNSUBSCRIBE user:*:events
Pub/Sub работает по принципу fire-and-forget. Если подписчика нет, сообщение теряется. Для гарантированной доставки используй Streams.
Persistence (сохранение на диск)
RDB (снимки)
# redis.confsave 3600 1 # снимок каждый час, если >=1 изменениеsave 300 100 # каждые 5 минут, если >=100 измененийsave 60 10000 # каждую минуту, если >=10000 измененийdbfilename dump.rdbdir /data
BGSAVE # запустить снимок в фонеLASTSAVE # timestamp последнего снимка
+ Компактный файл, быстрое восстановление.
- Потеря данных между снимками (до N минут).
AOF (журнал операций)
# redis.confappendonly yesappendfilename "appendonly.aof"appendfsync everysec # fsync каждую секунду (баланс)# appendfsync always # fsync на каждую запись (медленно, надёжно)# appendfsync no # OS решает когда fsync (быстро, ненадёжно)
BGREWRITEAOF # переписать AOF (сжать)
+ Минимальная потеря данных (до 1 секунды).
- Файл больше RDB, восстановление медленнее.
Рекомендация
Включай оба: RDB для быстрого восстановления + AOF для минимальной потери данных. Redis при старте использует AOF (если включён), иначе RDB.
Паттерны
Кеш
# Cache-aside (основной паттерн)# 1. Проверить кешGET cache:user:42
# 2. Если miss → запросить БД → записать в кешSET cache:user:42 '{"name":"Alice",...}' EX 300# Кеш с автообновлением TTL при чтенииGET cache:query:abc
EXPIRE cache:query:abc 300# продлить TTL при каждом чтении
Distributed Lock
# Захват (атомарный SET NX с TTL)SET lock:order:123 "worker:1" NX EX 30# NX = только если не существует# Вернул OK → блокировка получена# Вернул nil → занято# Освобождение (Lua-скрипт — проверить владельца перед удалением)EVAL "
if redis.call('GET', KEYS[1]) == ARGV[1] then
return redis.call('DEL', KEYS[1])
else
return 0
end
"1 lock:order:123 "worker:1"
Для production distributed lock используй Redlock или библиотеки (redisson, redis-py lock).
Rate Limiter
# Fixed window (простой)EVAL "
local count = redis.call('INCR', KEYS[1])
if count == 1 then
redis.call('EXPIRE', KEYS[1], ARGV[1])
end
return count
"1 ratelimit:api:user:42 60# Если count > limit → отклонить запрос# Sliding window (точнее)EVAL "
local key = KEYS[1]
local now = tonumber(ARGV[1])
local window = tonumber(ARGV[2])
local limit = tonumber(ARGV[3])
redis.call('ZREMRANGEBYSCORE', key, 0, now - window)
local count = redis.call('ZCARD', key)
if count < limit then
redis.call('ZADD', key, now, now .. '-' .. math.random(1000000))
redis.call('EXPIRE', key, window)
return 1
end
return 0
"1 ratelimit:user:42 <timestamp_ms> 60000100
Сессии
# Создать сессиюHSET session:abc123 user_id 42 role "admin" created_at "2025-03-15T14:30:00Z"EXPIRE session:abc123 86400# 24 часа# Проверить/обновитьHGETALL session:abc123
EXPIRE session:abc123 86400# продлить при активности# Удалить (logout)DEL session:abc123
Leaderboard
# Добавить / обновить scoreZADD leaderboard 1500"player:alice"ZINCRBY leaderboard 10"player:alice"# +10 очков# Топ-10ZREVRANGE leaderboard 09 WITHSCORES
# Ранг игрока (место в рейтинге, 0-based)ZREVRANK leaderboard "player:alice"# Топ-10 за сегодня (отдельный ключ)ZADD leaderboard:2025-03-15 1500"player:alice"ZREVRANGE leaderboard:2025-03-15 09 WITHSCORES
EXPIRE leaderboard:2025-03-15 172800# TTL 2 дня
Очередь задач (простая)
# ProducerRPUSH jobs '{"type":"send_email","to":"alice@example.com"}'# Consumer (блокирующий)BLPOP jobs 0# ждать бесконечно# Reliable queue (с подтверждением)LMOVE jobs processing LEFT RIGHT # взять из jobs → положить в processing# ... обработать ...LREM processing 1'<job_data>'# удалить из processing после успеха# Если consumer упал — job остаётся в processing → переработать
Для серьёзных очередей используй Streams с Consumer Groups.
Счётчики и аналитика
# Простой счётчикINCR page:views:/home
# Счётчик по периодамINCR page:views:/home:2025-03-15
EXPIRE page:views:/home:2025-03-15 604800# неделя# Уникальные посетителиPFADD visitors:2025-03-15 "user:42""user:100"PFCOUNT visitors:2025-03-15
# Уникальные за неделюPFMERGE visitors:week visitors:2025-03-15 visitors:2025-03-16 ...
PFCOUNT visitors:week
# Online-пользователи (с автоочисткой)ZADD online_users <timestamp> "user:42"# score = время последнего heartbeatZRANGEBYSCORE online_users (now-300) +inf # активные за последние 5 минутZREMRANGEBYSCORE online_users -inf (now-300)# удалить неактивных
Pipeline и производительность
Pipeline
Отправка нескольких команд без ожидания ответа на каждую. Уменьшает RTT (round-trip time):
# redis-cli с pipelineecho -e "SET k1 v1\nSET k2 v2\nGET k1\nGET k2" | redis-cli --pipe
# В приложении (Python)pipe = r.pipeline()pipe.set('k1', 'v1')pipe.set('k2', 'v2')pipe.get('k1')pipe.get('k2')results = pipe.execute()# один round-trip вместо четырёх
Без pipeline: 4 команды × 0.1ms RTT = 0.4ms.
С pipeline: 1 round-trip × 0.1ms RTT = 0.1ms (+ время выполнения).
Оптимизация памяти
# Проверить потреблениеINFO memory
MEMORY DOCTOR # диагностикаMEMORY USAGE key # конкретный ключ# Настройки сжатия (ziplist для малых структур)# redis.conf:hash-max-ziplist-entries 128# Hash в ziplist до 128 полейhash-max-ziplist-value 64# Hash в ziplist до 64 байт на значениеlist-max-ziplist-size -2 # List в ziplist до 8KBset-max-intset-entries 512# Set из чисел в intset до 512 элементовzset-max-ziplist-entries 128# ZSet в ziplist до 128 элементов
Maxmemory и eviction
# redis.conf или CONFIG SETmaxmemory 256mb
# Политика вытеснения при достижении лимитаmaxmemory-policy allkeys-lru # основной для кеша
Политика
Описание
noeviction
ошибка при записи (по умолчанию)
allkeys-lru
удалять наименее используемые ключи (основной для кеша)
allkeys-lfu
удалять наименее частые (Redis 4.0+)
allkeys-random
удалять случайные
volatile-lru
LRU только среди ключей с TTL
volatile-lfu
LFU только среди ключей с TTL
volatile-ttl
удалять с наименьшим TTL
Latency
redis-cli --latency # средняя задержкаredis-cli --latency-history # история задержкиredis-cli --intrinsic-latency 10# baseline задержка системы (10 сек тест)# Slow logSLOWLOG GET 10# последние 10 медленных командCONFIG SET slowlog-log-slower-than 10000# порог в микросекундах (10ms)
Клиенты в приложениях
Python (redis-py)
import redis
r = redis.Redis(host='localhost', port=6379, password='secret', decode_responses=True)
# Базовые операцииr.set('key', 'value', ex=60)
value = r.get('key')
# Hashr.hset('user:1', mapping={'name': 'Alice', 'email': 'alice@example.com'})
user = r.hgetall('user:1')
# Pipelinewith r.pipeline() as pipe:
pipe.set('k1', 'v1')
pipe.set('k2', 'v2')
pipe.get('k1')
results = pipe.execute()
# Pub/Subpubsub = r.pubsub()
pubsub.subscribe('notifications')
for message in pubsub.listen():
if message['type'] =='message':
print(message['data'])
# Connection pool (автоматический в redis-py)pool = redis.ConnectionPool(host='localhost', port=6379, max_connections=20)
r = redis.Redis(connection_pool=pool)
В Cluster-режиме: команды с несколькими ключами работают только если ключи в одном слоте. Используй hash tags: {user:42}:profile и {user:42}:sessions гарантированно в одном слоте.
Частые проблемы
KEYS блокирует сервер:
Никогда не используй KEYS * в production. Сканирует все ключи, блокируя Redis.
# Вместо KEYS — используй SCANSCAN 0 MATCH "user:*" COUNT 100
Память закончилась (OOM):
INFO memory # проверить used_memoryCONFIG GET maxmemory # лимитCONFIG SET maxmemory-policy allkeys-lru # включить вытеснениеredis-cli --bigkeys # найти самые большие ключи# Найти ключи без TTL (потенциальная утечка)SCAN 0 COUNT 1000# Для каждого ключа: TTL key → если -1, то без TTL
Высокая latency:
redis-cli --latency-history # мониторингSLOWLOG GET 10# медленные команды# Частые причины:# 1. KEYS, SMEMBERS на огромных множествах → SCAN# 2. Огромные значения (>10KB) → разбить на части# 3. BGSAVE на слабом диске → настроить реже или AOF# 4. swap (Redis ушёл в swap) → увеличить RAM или уменьшить maxmemory
Too many connections:
INFO clients # текущие подключенияCONFIG GET maxclients # лимит (по умолчанию 10000)CLIENT LIST # список клиентов# В приложении: используй connection pool# Не создавай новое подключение на каждый запрос
Данные потерялись после рестарта:
Проверь persistence:
CONFIG GET save # RDB включён?CONFIG GET appendonly # AOF включён?
Если оба выключены, данные хранятся только в памяти. Включи хотя бы AOF для важных данных.
Hot key (один ключ перегружает сервер):
redis-cli --hotkeys # найти горячие ключи (нужен LFU)# Решения:# 1. Локальный кеш в приложении (L1 cache)# 2. Реплики для чтения# 3. Разбить ключ на несколько (counter:1, counter:2, ... → суммировать)