Миф: Redis всегда быстрее запроса в базу
«Redis быстрее базы, значит кешируем всё подряд» — рассуждение звучит логично, но опирается на сравнение, которое редко проверяют на практике. Redis действительно бывает быстрее, но не всегда и не автоматически: для простого индексированного запроса, чьи данные уже лежат в буферном кеше самой СУБД, разница может быть незначительной или вовсе не в пользу Redis, если добавляется лишний сетевой переход. Разберём, где это сравнение работает, а где превращается в необоснованное усложнение архитектуры.
Содержание
Откуда берётся миф
Интуиция понятна: Redis хранит данные в оперативной памяти в простых структурах и отдаёт их по ключу без парсинга SQL, без планировщика запросов, без блокировок строк. База данных — сложная система с диском (пусть и SSD), журналом транзакций, MVCC, индексами, которые нужно обойти. На бумаге сравнение выглядит однозначным: «память без накладных расходов» против «полноценная СУБД с диском». Отсюда шаг к выводу «раз Redis быстрее, кешировать нужно всё, что можно».
Проблема в том, что это сравнение сопоставляет не то с тем. Реальный вопрос не «Redis быстрее диска БД?», а «запрос к Redis по сети быстрее, чем запрос к БД, у которой нужные страницы уже в оперативной памяти буферного пула?». Это два разных вопроса с разными ответами. Современная СУБД — не наивная система, читающая с диска на каждый запрос: PostgreSQL и MySQL держат горячие данные в собственном буферном кеше в ОЗУ (shared_buffers в PostgreSQL, InnoDB Buffer Pool в MySQL), и для данных, которые запрашиваются часто, эти страницы почти никогда не уходят на диск между обращениями. Разбор того, почему этот механизм в принципе важнее скорости самого диска, — в статье про буферный пул базы данных.
Второй источник мифа — путаница между «Redis быстрее в лабораторных условиях, на одном сервере, без сети» и «Redis быстрее в вашей продакшн-архитектуре, где кеш стоит на отдельном инстансе». Первое почти всегда верно как микробенчмарк. Второе зависит от топологии, и именно об этом — следующий раздел.
Сетевой переход — не бесплатный
Если Redis развёрнут на том же сервере, что и приложение, через Unix-сокет, накладные расходы на обращение к нему минимальны. Но типовая продакшн-конфигурация — отдельный Redis-инстанс, часто на отдельной виртуальной машине или даже в другом дата-центре ради отказоустойчивости. В этом случае каждое обращение к кешу — это TCP-соединение (или переиспользуемое, но всё равно сетевое) round-trip: пакет уходит, обрабатывается, ответ возвращается.
Точных цифр задержки называть не будем — они сильно зависят от сети, загрузки, драйвера клиента и десятков других факторов, и любое конкретное число будет вводить в заблуждение вне контекста вашего окружения. Важен принцип: сетевой round-trip добавляет фиксированную задержку, которая не масштабируется с сложностью запроса. Для очень простого, уже быстрого локального запроса к БД (данные в буферном кеше, есть подходящий индекс, соединение уже открыто) эта фиксированная сетевая задержка кеша может составлять сопоставимую или даже большую долю общего времени ответа, чем сам запрос к базе.
Практическая проверка на своей инфраструктуре — не полагайтесь на общие цифры из статей, измерьте у себя:
# Задержка до Redis-инстанса
redis-cli -h redis.internal --latency
# Задержка до самой базы (тот же принцип, для PostgreSQL)
time psql -h db.internal -U app -c "SELECT 1;"
-- Реальное время выполнения конкретного запроса к БД,
-- включая сеть до клиента psql (EXPLAIN ANALYZE считает только серверное время)
EXPLAIN (ANALYZE, BUFFERS, TIMING)
SELECT id, status, updated_at FROM orders WHERE id = 42;
Если оба сервиса — БД и Redis — находятся в одной сети с сопоставимой задержкой до приложения, а запрос к БД простой и индексированный, вы сравниваете не «быстрое против медленного», а «быстрое плюс сеть против быстрого плюс сеть» — и выигрыш кеша в этом конкретном случае может оказаться минимальным или в пределах погрешности измерения. Разница станет заметной там, где запрос к БД объективно дороже одного round-trip к Redis — об этом дальше.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверЧто делает запрос к БД дешёвым сам по себе
Прежде чем оборачивать запрос в кеш, стоит честно оценить, насколько он и так уже дешёвый. Три фактора определяют это:
Данные в буферном кеше. Горячие таблицы и индексы, к которым идут частые обращения, СУБД держит в ОЗУ. Обращение к такой странице не требует чтения с диска — это операция в памяти процесса postgres или mysqld, сопоставимая по порядку величины с операцией в памяти redis-server. Проверить, что данные действительно горячие, можно через статистику попаданий в кеш:
-- PostgreSQL: доля попаданий буферного кеша по таблице
SELECT relname,
heap_blks_read,
heap_blks_hit,
round(heap_blks_hit::numeric / nullif(heap_blks_hit + heap_blks_read, 0) * 100, 2) AS hit_ratio
FROM pg_statio_user_tables
WHERE relname = 'orders';
Если hit_ratio устойчиво выше 99% для конкретной таблицы, значит нужные страницы почти всегда уже в памяти — и путь запроса до данных для этой таблицы принципиально не отличается от «данные в памяти», просто эта память принадлежит СУБД, а не Redis.
Простой план выполнения. Точечный SELECT по первичному ключу или по индексированному полю с LIMIT — это операция сложности, близкой к константной: индекс (обычно B-tree) находит нужную строку за несколько логических чтений вне зависимости от размера таблицы. Такой запрос принципиально дешёвый, и добавлять к нему кеш — не ускорение, а перенос одной дешёвой операции на другую дешёвую операцию плюс сеть.
Уже открытое соединение. Если приложение держит пул соединений к БД (что почти всегда так в продакшне), накладных расходов на установку TCP-сессии и аутентификацию при каждом запросе нет — они амортизированы. Разовая просадка на «холодный» запрос без пула — отдельная тема, разобранная в статье про то, почему первый запрос к базе всегда медленный, но для установившегося пула это не фактор при сравнении с Redis.
Совокупность этих трёх факторов означает: для класса запросов «точечный SELECT по индексу, к горячим данным, через открытое соединение» база данных — не медленный компонент по умолчанию. Она проектировалась именно для этого сценария десятилетиями, и производители СУБД вложили огромные усилия в оптимизацию именно этого пути.
Цена, которую вы платите за кеш вне зависимости от выигрыша
Даже если Redis объективно быстрее в вашем случае, у кеширования есть фиксированные издержки, которые нужно окупить этим выигрышем — они не исчезают просто потому, что кеш «в целом хорошая идея».
- Консистентность. Кеш и база — два источника данных, которые могут разойтись. Каждое место, где данные меняются, должно либо инвалидировать кеш, либо обновлять его, либо жить с TTL и временным окном устаревания. Пропущенный путь обновления — стухший кеш, который может прожить до истечения TTL или бессрочно.
- Дополнительная точка отказа. Если Redis недоступен, а код не предусматривает fallback на прямой запрос к БД, падает не только «ускорение» — падает функциональность целиком. Обработка отказа кеша — это код, который нужно написать, протестировать и поддерживать.
- Сериализация и десериализация. Данные из БД приходят уже типизированными (строка, число, дата). В Redis они обычно хранятся как строка или JSON — значит на пути туда и обратно появляется сериализация, которая тоже стоит времени и CPU, просто не там, где её обычно ищут при профилировании.
- Операционная нагрузка. Ещё один сервис — значит ещё один компонент в мониторинге, бэкапах (если персистентность важна), обновлениях, конфигурации
maxmemory-policy, разборе инцидентов вида «это в базе не так или в кеше устарело».
Если запрос и так дешёвый (горячие данные, индекс, открытое соединение), а выигрыш от Redis на этом фоне минимален или неочевиден без измерения — эти издержки не окупаются вообще ничем, и вы получаете более сложную систему без измеримой пользы. Это не абстрактный риск: именно так рождаются архитектуры, где Redis есть, но никто не может объяснить, что произойдёт, если его выключить.
Когда Redis-кеширование даёт реальный, а не воображаемый выигрыш
Миф не в том, что Redis бесполезен — он в том, что выигрыш применяется без разбора. Есть класс задач, где кеширование в Redis — оправданное и сильное решение, потому что альтернатива объективно дороже одного сетевого round-trip.
Дорогие агрегации и вычисления. Запрос, который делает GROUP BY с несколькими JOIN по таблицам на миллионы строк, оконные функции для расчёта рейтингов, или вызывает внешний API с собственной задержкой и лимитами запросов — здесь цена повторного вычисления на порядки выше цены сетевого перехода до Redis. Посчитать один раз и отдавать из кеша сотни раз — прямой выигрыш без всякого сравнения на грани погрешности.
Часто запрашиваемые, редко меняющиеся данные. Справочники, конфигурация фичефлагов, список категорий каталога, курсы валют, обновляемые раз в несколько минут — соотношение чтение/запись здесь сильно смещено в пользу чтения, и даже дешёвый по отдельности запрос к БД, помноженный на тысячи обращений в минуту, создаёт заметную суммарную нагрузку. Кеш с TTL снимает эту нагрузку почти без риска показать существенно устаревшие данные.
Данные, требующие похода в несколько источников. Если для формирования ответа приложение обращается не только к БД, но и к внешнему сервису, файловому хранилищу или делает несколько последовательных запросов — суммарная задержка этой цепочки почти всегда больше, чем один запрос к Redis. Кеш здесь заменяет не один быстрый SELECT, а целую последовательность операций.
Разгрузка соединений при высокой конкурентности. Даже для дешёвого запроса при очень высокой частоте обращений (тысячи в секунду на один и тот же ключ) суммарная нагрузка на пул соединений и CPU базы может стать узким местом раньше, чем задержка отдельного запроса. Здесь кеш работает не потому, что отдельный запрос медленный, а потому что база — общий ресурс, который приходится беречь от количества, а не от сложности запросов.
Общий признак этой группы: цена альтернативы (пересчёт, повторный поход в несколько систем, суммарная нагрузка на общий ресурс) заметно выше цены одного обращения к Redis по сети. Именно это условие, а не абстрактное «Redis в памяти, значит быстрее», определяет, стоит ли кешировать.
Как решить для конкретного запроса, не гадая
Порядок действий, который заменяет интуицию измерением:
- Проверьте план и буферный кеш.
EXPLAIN (ANALYZE, BUFFERS)покажет, идёт ли чтение с диска (Buffers: read) или всё из памяти (Buffers: hit), и сколько времени объективно занимает запрос уже сейчас.
- Оцените стоимость запроса. Точечный SELECT по индексу с горячими данными — низкая база для сравнения, кеш выиграет немного или не выиграет вообще. Агрегат с несколькими JOIN по холодным или большим данным — высокая база, кеш выигрывает заметно.
- Оцените частоту обращений и соотношение чтение/запись. Один запрос раз в секунду не создаёт нагрузки, которую стоит оптимизировать. Тысяча одинаковых запросов в секунду к данным, которые меняются раз в час, — явный кандидат.
- Измерьте, а не предполагайте. Разверните вариант с кешем на стейджинге или за флагом на небольшой доле трафика и сравните p50/p95 задержки с вариантом без кеша при реалистичной нагрузке. Ориентировочные цифры из статей (в том числе из этой) — не замена измерению на вашей конкретной инфраструктуре, сети и наборе данных.
- Посчитайте цену поддержки. Если выигрыш в задержке измеримый, но небольшой, сравните его с постоянной ценой инвалидации, дополнительной точки отказа и операционной нагрузки. Небольшой выигрыш иногда не стоит постоянной сложности.
- Кешируйте точечно. Даже если решение в пользу кеша — оборачивайте конкретный дорогой и переиспользуемый запрос, а не «на всякий случай» весь слой доступа к данным.
Если после этого разбора выясняется, что Redis нужен не для конкретного узкого места, а «на будущее» или «потому что у всех есть» — это ровно тот случай, когда миф уже сработал и добавил сложность без обоснования.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверНужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.
Частые вопросы
Если сомневаюсь — лучше добавить Redis на всякий случай или не добавлять?
Не добавлять, пока не измерили конкретное узкое место. Лишний сервис в архитектуре — это постоянные издержки (инвалидация, точка отказа, поддержка), которые нужно окупать реальным выигрышем, а не откладывать «на будущее». Добавить Redis для конкретного дорогого запроса, когда он понадобится, дешевле, чем поддерживать неиспользуемую сложность с первого дня.
Buffer pool в БД — это то же самое, что Redis-кеш?
Нет, но механика похожа: оба держат горячие данные в ОЗУ вместо диска. Разница в том, что буферный пул — встроенный механизм самой СУБД, не требующий отдельного сервиса, сети и инвалидации на уровне приложения, а Redis — отдельный сервис со своими сетевыми и операционными издержками. Подробнее — в статье про буферный пул базы данных.
Как измерить, окупается ли Redis в конкретном случае?
Сравните EXPLAIN (ANALYZE, BUFFERS) для запроса к БД (с учётом того, что данные уже в буферном кеше при повторных обращениях) с реальной задержкой round-trip до вашего Redis-инстанса через redis-cli --latency, при нагрузке, близкой к продакшн. Если разница на грани погрешности измерения — выигрыш, скорее всего, не окупит издержки на консистентность.
Кеширование в приложении (in-process, без Redis) — альтернатива?
Да, для данных, которые не обязательно делить между несколькими инстансами приложения: локальный кеш в памяти процесса (например, LRU-структура) убирает даже сетевой переход до Redis. Компромисс — данные не общие между репликами приложения, и при масштабировании горизонтально каждая реплика греет свой кеш заново. Подходит для очень горячих, компактных данных вроде конфигурации.
Redis или Memcached — если решили, что кеш всё-таки нужен?
Выбор зависит не от чистой скорости (она сопоставима для простых операций), а от нужного функционала: персистентность, структуры данных, pub/sub. Разбор — в статье Redis или Memcached: что выбрать для сервера.
Что если запрос сейчас дешёвый, но данные вырастут и он подорожает?
Тогда кешировать стоит по мере роста, опираясь на метрики (pg_stat_statements, hit ratio буферного пула), а не заранее на весь возможный рост. Преждевременная оптимизация под гипотетическую будущую нагрузку добавляет сложность сейчас ради выгоды, которая может не наступить или наступить в другом месте системы.
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →