Прямой ответ
Key-value хранилище поддерживает две основные операции: положить значение по ключу и прочитать значение по ключу. Никаких соединений, схем и вторичных индексов. Простота модели даёт предсказуемую производительность: стоимость операции не зависит от объёма данных.
Два основных представителя решают разные задачи:
| memcached | Redis | |
|---|---|---|
| Модель | Только строки как значения | Строки, хеши, списки, множества, sorted set, потоки |
| Хранение | Только в памяти | В памяти с опциональной записью на диск (RDB, AOF) |
| Кластеризация | На стороне клиента | Встроенная (Redis Cluster) |
| Типичная роль | Кэш поверх базы | Кэш, очереди, счётчики, распределённые примитивы |
Если нужен только кэш с вытеснением — memcached проще и предсказуемее. Если нужны структуры данных или сохранение при перезапуске — Redis.
Ключевые механизмы:
- TTL — время жизни ключа. Основной способ управления объёмом кэша:
SET key value EX 300. - Политика вытеснения. При нехватке памяти ключи удаляются по правилу:
allkeys-lru,volatile-lru,allkeys-lfuи другие. Выбор политики определяет, какие данные исчезнут первыми. - Атомарные операции.
INCR,LPUSH,SETNXвыполняются атомарно, что позволяет строить счётчики и примитивы синхронизации без внешних блокировок.
# кэш с временем жизни
redis-cli SET session:abc123 '{"user":42}' EX 900
# атомарный счётчик ограничений
redis-cli INCR ratelimit:42:2026-10-21
redis-cli EXPIRE ratelimit:42:2026-10-21 86400
# блокировка с автоматическим освобождением
redis-cli SET lock:order:42 "worker-7" NX PX 30000
Важно понимать, что значение непрозрачно для хранилища. Оно не может отфильтровать по полю внутри JSON, построить агрегат или найти записи по вторичному признаку. Любой такой запрос означает перебор всех ключей, что в продакшене недопустимо.
Практика: где применять и как не ошибиться
1. Кэш результатов чтения. Самый частый сценарий: тяжёлый запрос к базе, результат на короткое время. Ключ строится из параметров запроса, TTL выбирается по допустимой устаревости данных.
2. Сессии и состояние. Хранилище сессий с TTL даёт автоматическое завершение по неактивности. О безопасной работе с сессиями — в статье про cookie и сессии.
3. Счётчики и ограничения. Атомарный инкремент — основа rate limiting, квот, лайков. Попытка реализовать счётчик через «прочитать, прибавить, записать» даёт гонки.
4. Очереди и отложенные задачи. Списки и sorted set покрывают умеренные объёмы; структура и ограничения разобраны в статье про структуры Redis.
5. Распределённые примитивы. Блокировки, лидеры, флаги. Требуют аккуратности: блокировка с TTL может истечь раньше завершения работы, поэтому нужен механизм продления или проверка владельца.
Что не стоит делать:
- Хранить то, что нельзя потерять. Кэш с вытеснением — не хранилище. Даже включённая персистентность не даёт гарантий уровня СУБД.
- Искать по значению. Если нужен поиск по полю внутри значения, это признак, что таким данным место в базе с индексами.
- Создавать большие значения. Значение в несколько мегабайт блокирует обработчик дольше и увеличивает расход сети. Практический ориентир — десятки килобайт.
- Держать всё в одном большом ключе. Хеш на миллион полей хуже, чем много мелких ключей: операции над большим ключом занимают обработчик надолго.
Рекомендации:
- Всегда задавайте TTL. Ключ без срока жизни — будущая утечка памяти. Исключения должны быть осознанными и немногочисленными.
- Выбирайте политику вытеснения под роль. Для кэша подходят
allkeys-lruилиallkeys-lfu; если в том же экземпляре живут очереди, вытеснение может удалить задачу, поэтому лучше разделить роли по разным базам или экземплярам. - Измеряйте долю попаданий в кэш. Низкая доля означает, что кэш не работает: либо TTL слишком короткий, либо ключи слишком специфичны.
- Планируйте отказ. Поведение приложения при недоступном хранилище должно быть определено: деградация к базе, а не ошибка пользователю.
- Следите за памятью. Рост числа ключей виден по метрикам раньше, чем по замедлению; заполнение памяти запускает вытеснение, которое выглядит как «кэш перестал работать».
Практический сценарий: кэш перестал помогать после роста трафика
Контекст: сервис кэшировал результаты тяжёлого запроса в memcached. Доля попаданий упала с 92 до 34 %, нагрузка на PostgreSQL выросла втрое.
- Диагностика. Проверка показала: экземпляр достиг лимита памяти, включилось вытеснение LRU. Ключи с коротким TTL вытеснялись раньше, чем истекал их срок жизни.
- Причина 1. Ключ кэша включал полный набор параметров фильтра, включая порядок сортировки и номер страницы. Для одного и того же набора данных создавались сотни ключей.
- Причина 2. Часть ключей не имела TTL: их создали на этапе отладки и не убрали. За год они заняли около 40 % памяти.
- Действия. (а) Ключ нормализовали: параметры сортируются, номер страницы исключён из ключа кэшируемого набора; (б) все ключи получили TTL; (в) крупные значения сжали, среднее значение уменьшилось с 180 до 40 КБ; (г) лимит памяти увеличили и добавили алерт на долю попаданий.
- Результат. Доля попаданий вернулась к 90 %, нагрузка на базу снизилась до прежнего уровня, потребление памяти сократилось на треть при большем числе запросов.
Вывод: key-value хранилище работает, пока ключи спроектированы под реальные шаблоны чтения. Рост трафика обнажает ошибки проектирования ключей быстрее, чем любые другие дефекты.
FAQ
Чем key-value хранилище отличается от документной базы? Документная база понимает структуру значения: может индексировать вложенные поля и выполнять агрегации. Key-value хранилище трактует значение как набор байт и не выполняет запросы по его содержимому.
Можно ли использовать Redis как основную базу? Технически да для узких задач: счётчики, рейтинги, короткие очереди. Для данных, которые нельзя потерять и по которым нужны сложные запросы, лучше подходит СУБД. О критериях выбора — в статье про выбор хранилища.
Что происходит при перезапуске memcached? Все данные теряются: это кэш без персистентности. Redis может восстанавливать состояние из снимка RDB или журнала AOF, но с потерями за последние секунды в зависимости от настроек.
Нужен ли кластер, если данных немного? Обычно нет. Одна нода с достаточным объёмом памяти проще в эксплуатации. Кластеризация нужна при исчерпании памяти одной машины или при требованиях к доступности, которые не покрывает репликация.
Источники: Redis docs (data types), memcached internals, Wikipedia (key-value database) — ссылки в шапке.