У нового веб-проекта появляется первая архитектурная схема:
Nginx
↓
Application
↓
PostgreSQLЧерез некоторое время кто-то предлагает:
Нужно добавить Redis.
На вопрос:
Зачем?
обычно звучит:
Он быстрый.
Или:
Для кеша.
Или:
В нормальном production Redis должен быть.
После этого архитектура превращается в:
Nginx
↓
Application
↓
┌────────────┬─────────┐
│ │ │
PostgreSQL Redis WorkerТеперь нужно:
устанавливать Redis;
защищать его;
мониторить;
обновлять;
проверять память;
решать вопросы persistence;
обрабатывать его недоступность;
синхронизировать кеш с PostgreSQL.При этом приложение может обслуживать 500 пользователей, выполнять несколько десятков запросов в секунду и прекрасно работать только на PostgreSQL.
Получается странная ситуация:
Redis добавили раньше, чем появилась проблема, которую он должен решить.
Но существует и противоположная ошибка.
Проект вырос.
Одни и те же тяжёлые данные запрашиваются тысячи раз в секунду.
Rate limit постоянно пишет в PostgreSQL.
Сессии создают лишнюю нагрузку.
Realtime-события нужно быстро раздавать нескольким экземплярам API.
А команда продолжает:
PostgreSQL ведь всё может.
Технически — многое.
Архитектурно — не каждую задачу стоит заставлять решать одним инструментом.
Поэтому правильный вопрос звучит не:
«Что быстрее — Redis или PostgreSQL?»
А:
«Какое состояние мы храним, насколько долго оно должно жить и какие гарантии нам от него нужны?»
Именно с этого и стоит начинать выбор.
PostgreSQL и Redis решают разные задачи
PostgreSQL — прежде всего реляционная система управления данными.
Она хорошо подходит для хранения:
пользователей;
проектов;
заказов;
платежей;
ролей;
сообщений;
истории;
связей между сущностями.То есть данных, которые являются источником истины продукта.
Если PostgreSQL потерять, продукт потеряет бизнес-состояние.
Redis устроен иначе.
Он особенно удобен для быстро доступных структур данных, которым часто нужны:
очень низкая задержка;
TTL;
счётчики;
быстрые атомарные операции;
временное состояние;
pub/sub;
stream processing;
cache.Официальная документация Redis прямо выделяет среди типичных сценариев:
cache;
session storage;
rate limiting;
job queues;
pub/sub;
streams;
leaderboards.То есть сравнивать их только по принципу:
Redis быстрее
PostgreSQL медленнеене очень полезно.
У них разная роль.
Первый вопрос: является ли значение бизнес-данными или производным состоянием?
Представим SaaS.
Есть таблица:
projectsс проектом:
id = 1842
status = IN_PROGRESS
owner = 52Это бизнес-факт.
Его нельзя потерять только потому, что сервер Redis перезапустился.
Источник истины здесь очевиден:
PostgreSQL.Теперь другой пример.
Для проекта нужно показать:
Количество непрочитанных уведомлений:
17Это значение можно вычислить:
SELECT COUNT(*)
FROM notifications
WHERE user_id = 52
AND read_at IS NULL;Если таких запросов очень много, результат можно временно кешировать:
notifications:unread:52
→ 17
TTL 30 secДаже если Redis потеряет этот ключ:
ничего необратимого не произошло.Значение можно получить из PostgreSQL снова.
Вот это уже хороший кандидат для кеша.
Очень полезное правило
Перед тем как положить данные в Redis, спросите:
Если прямо сейчас удалить весь Redis, сможем ли мы восстановить это значение?
Если:
да, из PostgreSQL или другого источникаперед нами хороший кандидат на ephemeral/cache state.
Если:
нет, данные исчезнут навсегданужно очень внимательно подумать, действительно ли Redis должен быть единственным их хранилищем.
Но PostgreSQL тоже использует память
Иногда Redis добавляют из рассуждения:
PostgreSQL каждый раз читает данные с диска, а Redis — из RAM.
Это слишком упрощённая модель.
PostgreSQL имеет собственный buffer cache, а операционная система также кеширует файловые данные. В документации PostgreSQL shared_buffers как раз управляет объёмом памяти, выделенным под shared buffer cache; EXPLAIN умеет отдельно показывать buffer hits, когда физическое чтение блока удалось избежать благодаря кешу.
Поэтому хорошо индексированный запрос вроде:
SELECT id, name
FROM users
WHERE id = 1842;не обязательно означает постоянное физическое чтение SSD.
Redis не должен маскировать плохой SQL
Представим страницу:
/projectsоткрывается за:
2,8 секунды.Команда решает:
Добавим Redis.
После кеширования:
50 ms.Кажется, проблема решена.
Но EXPLAIN ANALYZE показывает:
Seq Scan on projectsпо таблице из нескольких миллионов записей.
А запрос постоянно фильтрует:
WHERE organization_id = $1
ORDER BY created_at DESC;и нужного индекса просто нет.
Правильное первое действие
Создать подходящий индекс, например концептуально:
CREATE INDEX
ON projects (
organization_id,
created_at DESC
);После этого запрос может стать достаточно быстрым без Redis.
Почему это важно
Redis в такой ситуации не исправляет query model.
Он добавляет ещё одну проблему:
Когда инвалидировать cache?Например проект изменился.
PostgreSQL уже содержит:
status = COMPLETEDRedis всё ещё:
status = IN_PROGRESSТеперь приложение имеет два состояния одного факта.
Cache invalidation — реальная архитектурная стоимость
Простой cache-aside выглядит так:
Request
↓
Redis?
┌─┴─┐
Yes No
│ ↓
│ PostgreSQL
│ ↓
│ Redis SET
└────┘
↓
ResponseКрасиво.
Но после:
UPDATE projectвозникает вопрос:
Что делать с cache?
Вариант 1 — удалить кеш после записи
UPDATE PostgreSQL
↓
DEL project:1842Следующий запрос заново заполнит cache.
Но что если PostgreSQL обновился, а Redis недоступен?
Получаем:
DB:
COMPLETED
Cache:
IN_PROGRESSПоэтому кеш должен проектироваться так, чтобы потеря или ошибка Redis не разрушала корректность бизнес-данных.
Это одна из причин использовать TTL
Например:
project:1842
TTL = 30 secДаже если invalidation потерялся, stale-состояние ограничено по времени.
Но допустимость этих 30 секунд определяется продуктом.
Для каталога товаров 30 секунд могут быть нормальными
Например:
описание;
изображение;
категория.Для остатка последней единицы товара — уже нет
Если Redis говорит:
stock = 1а PostgreSQL:
stock = 0на основании cache нельзя разрешать продажу.
Здесь authoritative business decision должен опираться на источник истины и корректную конкурентную транзакцию.
Поэтому кешировать чтение и принимать бизнес-решение — не одно и то же
Redis может помочь быстро показать:
примерное количество;
каталог;
публичный профиль;
настройки, редко меняющиеся.Но критичные операции:
списать деньги;
уменьшить stock;
выдать доступ;
подтвердить заказ.должны иметь чёткую authoritative модель.
Когда PostgreSQL вполне достаточно для кешируемого на первый взгляд запроса
Например dashboard показывает:
12 проектов
5 активных
3 ожидают оплатыТаблица содержит:
5 000 строк.Запрос занимает:
8 ms.И выполняется:
несколько раз в минуту.Redis здесь, скорее всего, ничего существенного не улучшит.
Он добавит
новое соединение;
новый service;
новые failure modes;
cache invalidation;
мониторинг.ради экономии нескольких миллисекунд.
Это сомнительный обмен.
Что делать, если dashboard действительно тяжёлый
Допустим агрегат:
SELECT ...
FROM orders
JOIN payments
JOIN users
...по миллионам строк занимает секунды.
Redis — один вариант.
Но не единственный.
PostgreSQL имеет materialized views, которые физически сохраняют результат запроса и позволяют обновлять его через REFRESH MATERIALIZED VIEW. Это особенно удобно для статистики и dashboard, которым допустима контролируемая задержка обновления.
Например
Вместо:
каждый пользовательский request
↓
огромный aggregateможно:
background refresh
↓
sales_summary
↓
быстрый SELECTИногда Redis после этого вообще не требуется.
Значит первый принцип такой
Не добавляйте cache до того, как поняли стоимость исходного запроса.
Сначала:
EXPLAIN ANALYZE;
indexes;
query shape;
pagination;
N+1;
materialized/read model.И только потом:
Redis.Теперь рассмотрим sessions
Здесь Redis становится намного интереснее.
Представим один backend:
Nginx
↓
API #1Можно хранить session в PostgreSQL:
sessions
id
user_id
expires_at
dataДля небольшого продукта это совершенно нормальная архитектура.
Пользователь входит
Session ID
↓
PostgreSQL
↓
UserПри нормальных индексах и умеренной нагрузке проблема может вообще никогда не появиться.
Зачем тогда Redis для sessions?
Например появляется:
API #1
API #2
API #3Все instance должны очень быстро получать session state.
Сессии:
часто читаются;
имеют TTL;
временные по природе;То есть очень хорошо соответствуют модели Redis.
Официальная документация Redis отдельно описывает session storage с автоматическим EXPIRE, включая sliding TTL и управление несколькими сессиями одного пользователя.
Но Redis для session всё равно не обязателен
Можно продолжать использовать:
PostgreSQL sessions.Особенно если:
QPS небольшой;
PostgreSQL далеко не перегружен;
простота инфраструктуры важнее микросекунд.Или использовать stateless-токены, если их свойства подходят конкретной модели безопасности.
То есть правило снова то же
Не:
Sessions → обязательно Redis.
А:
Какие требования у наших sessions?
Следующая популярная задача — rate limiting
Например API разрешает:
100 запросов в минуту
на пользователя.Нужно вести счётчик:
rate:user:1842с ограниченным сроком жизни.
Это почти идеальная модель для Redis:
counter
+
atomic increment
+
TTL.Redis и сам официально выделяет rate limiting как один из основных прикладных сценариев.
Можно ли сделать rate limit на PostgreSQL?
Конечно.
Например:
api_rate_limits
user_id
window
countи атомарно обновлять строку.
Для:
нескольких запросов в секундуэто вполне может работать.
Проблема появляется при высоком потоке
Например public API получает:
50 000 requests/sec.Постоянные:
INSERT/UPDATEсчётчиков создают бессмысленную для основного business database write-нагрузку.
Значения при этом живут:
60 секунд.И после этого больше не нужны.
Это типичный пример данных, которым не обязательно жить в PostgreSQL
Именно потому, что они:
короткоживущие;
очень часто изменяются;
легко пересоздаются;
не являются бизнес-историей.Redis здесь выглядит значительно естественнее.
Следующий сценарий — счётчики
Например:
online users;
просмотры;
количество действий в минуту;
temporary counters.PostgreSQL способен выполнять:
UPDATE counters
SET value = value + 1
WHERE key = '...';Но если один hot counter обновляется десятки тысяч раз в секунду, он превращается в точку contention.
Redis с атомарными counter operations может быть гораздо удобнее.
Но вопрос снова в масштабе
Если счётчик меняется:
20 раз в минуту,Redis добавлять ради него странно.
Онлайн-пользователи — хороший пример ephemeral-state
Пользователь отправляет heartbeat.
Мы хотим знать:
Кто был активен последние 2 минуты?
Такое состояние:
быстро устаревает;
не представляет исторической ценности;
имеет естественный TTL.Redis подходит очень хорошо.
А вот историю посещений уже лучше хранить иначе
Если бизнес хочет анализировать:
кто заходил;
когда;
сколько сессий;это уже исторические данные.
Их можно агрегировать/сохранять в PostgreSQL, аналитической системе или другом долговременном storage.
Один и тот же пользовательский факт может иметь:
операционное краткоживущее представлениеи:
долговременную историю.Теперь background jobs
Это одна из самых интересных областей.
Часто звучит:
У нас worker, значит нужен Redis.
Это неверно.
Очередь можно построить на PostgreSQL
Например:
jobs
id
type
payload
status
available_at
attemptsWorker выбирает задачу:
SELECT ...
FROM jobs
WHERE status = 'pending'
ORDER BY id
FOR UPDATE SKIP LOCKED
LIMIT 1;SKIP LOCKED позволяет нескольким workers не ждать строки, уже захваченные другим процессом; PostgreSQL прямо показывает этот подход для снижения lock contention в конкурентной обработке строк.
Получается архитектура
API
↓
PostgreSQL jobs
↓
Worker #1
Worker #2
Worker #3И отдельный Redis вообще не нужен.
У PostgreSQL-очереди есть сильное преимущество
Представим бизнес-операцию:
создать заказ
+
создать job SEND_ORDER_EMAILМожно выполнить:
BEGIN
INSERT order
INSERT job
COMMITТеперь либо существуют:
и Order
и Job,либо:
ни одного.Очень полезное свойство.
Если очередь находится в отдельном Redis
Получаем:
BEGIN PostgreSQL
INSERT order
COMMIT
↓
Redis enqueueЧто если:
PostgreSQL succeeded
Redis failed?Заказ существует.
Job отсутствует.
Теперь нужен:
outbox;
retry;
reconciliationили другой механизм согласования двух систем.
Поэтому для небольшого и среднего приложения PostgreSQL queue может быть очень сильным решением
Особенно если:
задач немного;
они тесно связаны с business transaction;
PostgreSQL уже является обязательной зависимостью.Мы уменьшаем количество moving parts.
Когда Redis queue становится интереснее
Например поток jobs очень большой.
Нужны специфические инструменты/библиотеки вокруг Redis.
Нужны:
очень быстрые transient queues;
consumer coordination;
специфичная модель обработки.Redis официально рассматривает job queues как отдельный use case, а Redis Streams предоставляют consumer groups, acknowledgements, pending entries и повторное назначение необработанных сообщений.
Но Redis Pub/Sub и надёжная очередь — не одно и то же
Это очень важное различие.
Представим:
Publisher
↓
Redis Pub/Sub
↓
SubscriberSubscriber в момент сообщения отключён.
Для обычной pub/sub-модели событие может быть просто потеряно.
Поэтому Pub/Sub хорош для другого класса событий
Например:
обновить UI;
сообщить WebSocket-instance;
инвалидировать локальный кеш;
разослать transient realtime signal.Где история не является обязательной.
Для durable jobs нужны другие механизмы
Например:
PostgreSQL jobs;
Redis Streams;
специализированная очередь.То есть:
Redisсам по себе ещё не означает:
надёжная очередь.Нужно определить используемую структуру и семантику доставки.
PostgreSQL тоже умеет уведомлять процессы
Есть:
LISTENи:
NOTIFYPostgreSQL позволяет клиентским процессам подписываться на channels и получать уведомления при NOTIFY. Документация прямо описывает это как простой механизм межпроцессного взаимодействия для приложений, работающих с одной БД.
Например PostgreSQL queue можно сделать гибридной
Durable job находится:
jobs table.После commit:
NOTIFY jobs;Worker не обязан постоянно выполнять:
SELECT
SELECT
SELECTОн получает сигнал:
Появилась работа.
Но источником истины остаётся таблица.
Это важное различие
NOTIFYможет служить сигналом.
jobs table— durable state.
Даже если worker пропустил сигнал, job остаётся в таблице и будет найдена позже.
Для многих небольших SaaS этого более чем достаточно
Архитектура:
Application
↓
PostgreSQL
├── business data
├── jobs
└── NOTIFY
Workerочень проста в эксплуатации.
А как быть с distributed locks?
Представим scheduler запущен сразу в двух API instance.
Оба хотят выполнить:
daily report.Нужно обеспечить:
только один исполнитель.Часто первая мысль:
Redis lock.
Но PostgreSQL уже имеет advisory locks.
Официальная документация PostgreSQL предоставляет session-level и transaction-level advisory locks для application-defined ресурсов.
Например концептуально
try acquire lock:
daily-report-2026-09-29Получил один process.
Второй:
lock unavailable
→ skip.Если приложение уже зависит от PostgreSQL, это может быть значительно проще добавления Redis только ради locks.
Когда Redis-lock всё-таки оправдан
Например координируются:
несколько независимых сервисов,не имеющих общей PostgreSQL.
И Redis уже является общей инфраструктурой.
Но distributed locking требует очень аккуратного определения:
TTL;
ownership;
failure semantics;
что произойдёт после network partition.Нельзя считать:
SET if not existsуниверсальной заменой продуманной модели конкурентности.
Особенно опасно использовать lock вместо database constraint
Например правило:
Один активный invoice на операцию.
Можно попытаться:
Redis lock
↓
check
↓
insertНо последней защитой всё равно полезно иметь:
UNIQUE constraintв PostgreSQL.
Почему?
Redis может быть недоступен.
Lock может истечь.
Процесс может зависнуть.
А бизнес-инвариант должен оставаться истинным.
Поэтому concurrency и caching не должны подменять database invariants
Redis:
ускоряет;
координирует;
временно хранит.PostgreSQL:
защищает durable business state.У них разные обязанности.
Теперь realtime
Представим приложение имеет:
API #1
API #2Пользователь A подключён по WebSocket к:
API #1.Администратор отправил сообщение через:
API #2.Как API #1 узнает:
Нужно отправить событие пользователю A?
На одном instance всё просто
sendToSocket(userId)Но при нескольких instances нужен межпроцессный канал.
Redis Pub/Sub здесь очень удобен
Например:
API #2
↓
publish user:1842
↓
Redis
↓
API #1
↓
WebSocketЕсли сообщение уже сохранено в PostgreSQL, Redis здесь отвечает только за оперативную доставку realtime-сигнала.
Если Pub/Sub-сигнал потерялся, данные не потеряны
Пользователь обновляет страницу.
API читает:
messageиз PostgreSQL.
То есть:
PostgreSQL
→ durable truth
Redis Pub/Sub
→ realtime acceleration.Это очень здоровая архитектурная граница.
Такой подход лучше, чем хранить единственную копию сообщения в Pub/Sub
Потому что realtime transport и история сообщений имеют разные требования.
Redis становится особенно ценным, когда экземпляров приложения много
Например:
API #1
API #2
API #3
API #4
API #5Нужны:
shared sessions;
pub/sub;
rate limit;
temporary coordination;
cache.Один Redis способен закрыть несколько действительно существующих задач.
Теперь его эксплуатационная стоимость уже начинает окупаться.
Это намного сильнее аргумента
Redis — стандартный элемент стека.
Когда PostgreSQL обычно достаточно
Представим обычную CRM:
200 пользователей;
1–2 API instance;
несколько workers;
умеренная нагрузка.Есть:
клиенты;
проекты;
сообщения;
файлы;
уведомления;
админка.PostgreSQL вполне может хранить:
business data;
sessions;
jobs;
counters;
locks.Задачи можно обрабатывать через PostgreSQL queue
Например:
email;
cleanup;
publication;
notification;
storage deletion.Locks — advisory locks или row locks
Сессии — обычная таблица с:
expires_at.Dashboard — индексы или:
materialized view.Realtime-сигналы при небольшой архитектуре:
LISTEN / NOTIFYили даже локальный event bus, если API один.
Добавлять Redis в такую систему можно
Но вопрос:
Что именно станет лучше?
Если нет убедительного ответа, возможно, пока не нужно.
Пример №1. Небольшой B2B-сервис
Архитектура:
Nginx
↓
Node.js
↓
PostgreSQL
↓
WorkerНагрузка:
20 requests/sec peak.Jobs:
500/day.Sessions:
300 active.Добавлять Redis просто ради:
sessions + queueвряд ли обязательно.
PostgreSQL справится без труда при нормальной схеме.
Пример №2. SaaS быстро вырос
Теперь:
API instances = 8
active sessions = 80 000
requests = thousands/secЕсть:
rate limiting;
realtime;
hot public cache;
temporary counters.Redis становится значительно интереснее.
Не потому, что:
пользователей стало много.
А потому что появился конкретный набор workloads, для которых memory-oriented shared state подходит лучше.
Пример №3. Каталог с большим read traffic
PostgreSQL хранит:
Product
Price
CategoryПубличная страница одного популярного товара получает:
20 000 requests/min.Товар меняется:
раз в несколько часов.Это хороший кандидат на cache
product:1842
TTL = 60 secКоличество обращений к PostgreSQL резко уменьшается.
Staleness в пределах минуты допустима.
Redis приносит понятную измеримую пользу.
Пример №4. Финансовый баланс
Есть:
account.balanceКто-то предлагает:
Давайте хранить баланс в Redis — быстрее.
Здесь нужно быть намного осторожнее.
Баланс — критичный бизнес-факт.
Он требует:
transactionality;
audit;
durability;
concurrency rules.Использование Redis как единственного источника истины резко меняет требования к архитектуре.
Для обычного SaaS PostgreSQL выглядит естественнее как authoritative storage.
Redis при этом может хранить производное представление
Например:
balance display cache.Но операция:
можно ли списать 10 000?должна опираться на authoritative финансовую модель.
Пример №5. Rate limit формы регистрации
Нагрузка:
5 регистраций в минуту.PostgreSQL спокойно справится.
Можно даже реализовать ограничение на application level и хранить события.
Через год public API получает миллионы запросов
Теперь rate-limit counters:
живут 1 минуту;
меняются постоянно;
не нужны в истории.Redis начинает быть почти идеальным инструментом.
Значит решение зависит не только от функции
Одна и та же функция:
rate limitпри одном масштабе может спокойно жить на PostgreSQL.
При другом — Redis даёт существенную пользу.
Необходимо измерять, а не угадывать
Перед добавлением Redis полезно ответить:
Какой QPS?Какая p95/p99 latency?Какие SQL-запросы самые дорогие?Есть ли lock contention?Сколько чтений повторяют одни данные?Какие значения имеют естественный TTL?Без этих цифр архитектурное решение строится на ощущениях.
Один из самых плохих аргументов
PostgreSQL уже загружен на 40%, давайте ставить Redis.
Почему именно 40%?
Что загружено?
CPU?
Disk I/O?
connections?
locks?
slow queries?Redis может вообще не влиять на проблемный ресурс.
Например bottleneck — плохой INSERT
Если система упирается в массовую запись:
audit eventsкеш SELECT ничего не изменит.
Если bottleneck:
connection pool,Redis-cache может немного уменьшить SQL traffic, но настоящая причина всё ещё останется.
Сначала нужно локализовать проблему
Типичная последовательность:
Metrics
↓
Slow queries
↓
EXPLAIN ANALYZE
↓
Indexes
↓
Query/model optimization
↓
Load test
↓
только затем cacheТак Redis становится решением измеренной проблемы.
Есть ещё in-process cache
Если API пока один:
API #1и нужно кешировать небольшой immutable/reference dataset, можно использовать память самого приложения.
Например:
countries;
static configuration;
feature metadata.Нет смысла поднимать network cache для данных:
10 KBтолько потому, что слово cache ассоциируется с Redis.
Но in-memory cache перестаёт быть общим при нескольких instances
Появилось:
API #1
API #2
API #3У каждого свой cache.
Теперь invalidation становится сложнее.
Здесь Redis может дать общий shared cache.
Ещё один риск Redis — скрытая зависимость
Сначала Redis используется:
только для кеша.То есть приложение умеет работать без него.
Потом туда добавляют:
sessions.Потом:
rate limit.Потом:
queue.Потом:
locks.Через год Redis становится:
критической точкой отказа.Хотя архитектурно команда продолжает считать:
Это просто кеш.
Поэтому для Redis тоже нужен dependency classification
Например:
Cache
→ degradation acceptableSessions
→ authentication unavailableQueue
→ background processing stopsОдин Redis instance может обслуживать несколько use cases, но последствия его потери у них разные.
Это важно для readiness
Если Redis используется только как cache:
Redis DOWNможет означать:
Ready = true
Degraded = trueПриложение просто идёт в PostgreSQL.
Если Redis хранит sessions
Redis DOWNозначает:
пользователи не могут нормально работать.Readiness и monitoring уже должны относиться к Redis как к критичной dependency.
Нельзя одновременно говорить
Redis неважен, это кеш.
и использовать его как:
единственное хранилище пользовательских сессий;
единственную очередь критичных jobs;
единственный источник temporary auth state.Архитектура должна честно отражать реальную роль компонента.
Cache stampede — ещё одна проблема
Представим популярный ключ:
catalog:homepageTTL истекает.
Одновременно приходят:
5 000 requests.Каждый видит:
cache missи идёт в PostgreSQL.
Мы получили:
5 000 тяжёлых запросоввместо одного.
Это cache stampede.
Значит качественный кеш — больше, чем GET/SET
Могут потребоваться:
jitter TTL;
locking/single-flight;
stale-while-revalidate;
background refresh.Redis уменьшает часть нагрузки, но сам вводит архитектурные решения.
Ещё одна проблема — слишком длинный TTL
Например:
price cache:
24 hours.Бизнес изменил цену.
Пользователь ещё сутки видит старую.
Слишком короткий TTL тоже может быть бессмысленным
TTL = 1 secпри:
10 requests/minute.Практически каждый запрос снова идёт в PostgreSQL.
Cache существует, а выгоды почти нет.
TTL должен следовать бизнес-требованию к свежести
Не:
Обычно ставим 5 минут.
А:
Сколько stale data допустимо для этой функции?
Cache hit ratio нужно измерять
Например:
Cache hits:
95%может означать существенную пользу.
А:
hits:
8%при сложной invalidation-системе заставляет спросить:
Нужен ли вообще этот cache?
Redis не должен становиться «корзиной для всего временного»
Очень легко начать складывать:
temporary state;
unfinished forms;
payment state;
one-time tokens;
background jobs;
user settings.потому что:
Redis уже есть.
Но каждый тип данных должен иметь собственные требования к:
durability;
TTL;
recovery;
audit.Техническая доступность хранилища не означает архитектурную пригодность.
Например password reset token
Можно хранить:
в PostgreSQLс:
hash;
expires_at;
used_at.Плюсы:
audit;
одна транзакционная система;
простая диагностика.Можно использовать Redis, если модель продукта и нагрузка это оправдывают.
Но само наличие TTL ещё не делает Redis обязательным.
Именно это отличает архитектурный выбор от шаблона
Плохой шаблон:
sessions → Redis
jobs → Redis
cache → Redis
locks → RedisХорошая архитектура спрашивает для каждого случая:
Нужна durability?
Нужен TTL?
Какой throughput?
Что при потере данных?
Нужна транзакция с business data?
Нужна история?
Нужны JOIN/поиск?
Какой failure mode?И уже потом выбирает storage.
Сравним PostgreSQL и Redis в типичных задачах
| Задача | PostgreSQL | Redis |
|---|---|---|
| Основные бизнес-данные | Отлично | Обычно не первый выбор |
| Relational data | Отлично | Не основная специализация |
| Транзакции между сущностями | Сильная сторона | Другая модель |
| Cache | Возможно, но не отдельный cache layer | Сильная сторона |
| TTL-состояние | Можно реализовать | Очень удобно |
| Sessions | Подходит | Очень удобно при высоком трафике |
| Rate limiting | Подходит при умеренной нагрузке | Сильная сторона |
| Hot counters | Может стать contention point | Очень удобно |
| Job queue | Отлично для многих систем | Тоже сильный вариант |
| Pub/Sub | LISTEN/NOTIFY для простых задач | Сильная сторона |
| Durable history | Отлично | Требует отдельного проектирования |
| Complex queries/JOIN | Сильная сторона | Не для этого |
| Advisory locks | Есть | Есть свои coordination-паттерны |
| Leaderboards | Можно реализовать | Sorted sets очень удобны |
| Temporary shared state | Можно | Очень естественный use case |
Практический выбор для нового веб-продукта
Для нового CRM или SaaS мы бы чаще начинали:
Application
+
PostgreSQLЕсли нужны background jobs:
Application
+
PostgreSQL
+
WorkerИ только после появления конкретной необходимости добавляли:
Redis.Почему такой порядок полезен
Потому что первая версия продукта обычно имеет больше неопределённости в бизнесе, чем проблем производительности.
Нам важнее быстро изменять:
workflow;
permissions;
billing;
projects;
CRM logic.чем заранее строить сложный cache layer.
Кроме того, Redis всегда можно добавить позже
В большинстве случаев переход:
PostgreSQL-only
↓
PostgreSQL + Redis cacheне требует переписывать всю domain model.
Особенно если application layer уже отделяет:
источник истиныот:
ускоряющего слоя.А удалить Redis позднее может оказаться сложнее
Если вокруг него уже построены:
sessions;
queue;
locks;
pub/sub;
cache;
rate limit,компонент становится глубоко встроен в инфраструктуру.
Поэтому добавлять его лучше сознательно.
Удобная эволюция может выглядеть так
Этап 1
Nginx
↓
Application
↓
PostgreSQLЭтап 2
Появились фоновые задачи:
Application
↓
PostgreSQL jobs
↓
WorkerЭтап 3
Появились несколько API instances и realtime:
Application × N
↓
PostgreSQL
Redis
→ Pub/SubЭтап 4
Read traffic вырос:
Redis
→ cacheЭтап 5
Rate limiting стал высокочастотным:
Redis
→ rate countersRedis появился не потому, что:
архитектура должна выглядеть профессионально.
А потому что продукт постепенно создал несколько задач, для которых он действительно полезен.
Когда мы бы точно задумались о Redis
Например:
одни и те же дорогие данные
читаются тысячи раз;нужен общий cache
между множеством API;есть high-frequency rate limiting;нужны большое количество
короткоживущих counters;нужен быстрый shared session store;несколько realtime instances
должны обмениваться transient events;требования очереди хорошо соответствуют
Redis Streams/экосистеме.Это конкретные причины.
Когда PostgreSQL пока достаточно
Например:
нагрузка умеренная;SQL после индексирования быстрый;один или два API;сессий немного;jobs тесно связаны с business transactions;rate limit не создаёт заметной write load;realtime прост или отсутствует.В этой ситуации второй stateful service может быть преждевременной оптимизацией.
Самый полезный вопрос перед установкой Redis
Мы бы попросили закончить фразу:
«Без Redis сейчас у нас существует проблема ________, которую мы измерили как ________. Redis решит её за счёт ________.»
Например:
Без Redis 85% запросов публичного каталога повторно выполняют один тяжёлый SQL. На пике это 4 000 одинаковых чтений в секунду. Redis cache-aside с TTL 60 секунд уменьшит количество обращений к PostgreSQL.
Это хороший архитектурный аргумент.
А вот плохой
Redis нужен, потому что он быстрый.
Что именно мы ускоряем?
До какой величины?
Какая текущая проблема?
Что произойдёт при его отказе?
Если ответа нет, компонент пока добавляется ради архитектурного ощущения.
Redis должен иметь собственный failure plan
Допустим мы его всё-таки добавили.
Нужно заранее определить:
Что если Redis недоступен?
Для cache:
fallback → PostgreSQL.Для sessions:
auth degraded/unavailable.Для rate limit:
fail-open или fail-closed?Для queue:
как восстанавливаются pending jobs?Для pub/sub:
можно ли потерять событие?Это пять разных ответов.
Особенно интересен rate limit
Если Redis упал, можно:
fail-openто есть временно пропускать запросы.
Плюсы:
основной сервис доступен.Минусы:
защита ослаблена.Или:
fail-closedто есть блокировать запросы.
Плюсы:
лимит нельзя обойти.Минусы:
отказ Redis
останавливает функцию.Правильный выбор зависит от того, что защищается.
Это показывает важную вещь
Redis — это не просто ускоритель.
Как только продукт начинает зависеть от него, Redis становится частью business availability model.
Его нужно включать в:
health-check;
readiness;
monitoring;
alerts;
capacity planning.Если Redis используется как cache, readiness может не зависеть от него
Например:
PostgreSQL ✓
Redis ✗Получаем:
Ready:
YES
Status:
DEGRADEDЕсли Redis хранит обязательные sessions
Та же ситуация:
PostgreSQL ✓
Redis ✗может дать:
Ready:
NOили частичную деградацию конкретных routes.
Таким образом даже health-check определяется архитектурным смыслом Redis, а не названием технологии.
Capacity planning тоже отличается
PostgreSQL в основном планируется вокруг:
disk;
RAM;
connections;
I/O;
query workload.Redis — особенно вокруг:
RAM;
key count;
TTL;
eviction policy;
traffic.Если команда не знает:
Что произойдёт, когда Redis заполнит память?
архитектура ещё не закончена.
Cache eviction не должна удалять единственную копию важных данных
Очевидное правило.
Но его легко нарушить постепенно.
Сначала:
cache only.Потом туда кладут что-то, чего в PostgreSQL уже нет.
Теперь eviction начинает означать потерю данных.
Поэтому источник истины должен быть определён явно.
Как мы бы организовали архитектуру
Например:
Application
/ \
/ \
▼ ▼
PostgreSQL Redis
│ │
│ ├── cache
│ ├── sessions
│ ├── rate limit
│ └── pub/sub
│
├── users
├── projects
├── payments
├── files metadata
└── durable jobsВ этой схеме понятно:
PostgreSQL
→ authoritative persistent stateRedis
→ specialised fast/ephemeral stateНо другая система может выглядеть иначе
Например Redis Streams используется как значимый message infrastructure.
Тогда:
Redisуже не просто optional cache.
Он становится полноценной критичной подсистемой.
Это нормально.
Главное — не притворяться, что архитектурная роль осталась прежней.
Redis не является обязательным этапом взросления продукта
Нет такой последовательности:
SQLite
↓
PostgreSQL
↓
Redis
↓
Kafka
↓
Kubernetesкак уровней компьютерной игры.
Каждая технология появляется только когда её свойства соответствуют новой задаче.
Можно построить серьёзный веб-продукт:
PostgreSQL
+
Workers
+
Object Storageи годами не нуждаться в Redis.
И можно нуждаться в Redis уже в небольшом продукте
Например продукт сам по себе — realtime leaderboard.
Даже при небольшой команде его основная задача отлично соответствует:
Redis Sorted Sets.То есть размер компании тоже не является универсальным критерием.
Выбор определяется workload
Именно это ключевой вывод.
Практический checklist перед добавлением Redis
Мы бы проверили:
- существует ли измеримая проблема, а не гипотетическая будущая нагрузка;
- оптимизированы ли SQL и индексы;
- проверен ли
EXPLAIN ANALYZEдорогих запросов; - можно ли решить задачу PostgreSQL materialized/read model;
- действительно ли данные имеют короткий lifecycle или естественный TTL;
- можно ли безопасно потерять Redis-состояние;
- известен ли authoritative source данных;
- определено ли поведение при cache miss;
- определено ли поведение при недоступности Redis;
- понятна ли стратегия cache invalidation;
- измеряется ли cache hit ratio;
- не используется ли Redis для маскировки N+1 или отсутствующего индекса;
- нужна ли одна транзакция между business data и job;
- подойдет ли PostgreSQL queue через
FOR UPDATE SKIP LOCKED; - можно ли решить coordination через PostgreSQL advisory locks;
- нужен ли именно durable stream или достаточно transient Pub/Sub;
- существует ли реальный scale, при котором PostgreSQL rate counters стали проблемой;
- готовы ли monitoring/readiness учитывать новый компонент;
- известно ли, что произойдёт при исчерпании Redis memory;
- оправдывает ли полученная выгода дополнительный stateful service.
Если на большинство вопросов пока нет ответа, возможно, Redis добавляется слишком рано.
Самая практичная стратегия — PostgreSQL first, Redis when justified
Это не означает:
Всегда избегайте Redis.
Напротив.
Redis — чрезвычайно полезный инструмент.
Но особенно хорошо он работает тогда, когда получает узкую и понятную ответственность.
Например:
PostgreSQL
→ business truth
Redis
→ hot cacheили:
PostgreSQL
→ users
Redis
→ high-frequency rate limit.или:
PostgreSQL
→ messages history
Redis Pub/Sub
→ realtime delivery.Граница сразу понятна.
Значительно хуже выглядит
PostgreSQL:
часть данных
Redis:
ещё какая-то часть данных
а что является источником истины —
зависит от endpoint.Так постепенно появляется система, в которой любое расхождение превращается в сложное расследование.
Вместо вывода
Redis действительно способен сделать веб-продукт быстрее и удобнее масштабировать.
Он отлично подходит для:
cache;
sessions;
rate limiting;
hot counters;
pub/sub;
streams;
некоторых типов queues;
короткоживущего shared state.Но из этого не следует:
Любому production-приложению нужен Redis.
PostgreSQL уже способен решать значительно больше задач, чем иногда предполагают.
Он может хранить:
sessions;
jobs;
counters;
locks;
notifications;
materialized data.Для очередей можно использовать конкурентное чтение через FOR UPDATE SKIP LOCKED.
Для application-level coordination существуют advisory locks.
Для простых межпроцессных сигналов — LISTEN/NOTIFY.
Для тяжёлых read-моделей — индексы, оптимизированные запросы и materialized views.
И всё это происходит внутри одной уже существующей транзакционной системы.
Поэтому для нового CRM, SaaS или B2B-продукта хорошая стартовая схема часто остаётся очень простой:
Application
↓
PostgreSQL
↓
WorkerА Redis появляется позже — в тот момент, когда можно закончить фразу:
«Мы добавляем Redis, потому что вот эта конкретная нагрузка или временное состояние уже стало проблемой, и Redis решает именно её».
Тогда Redis перестаёт быть обязательной строчкой на архитектурной диаграмме.
Он становится тем, чем и должна быть любая инфраструктурная технология:
инструментом для решения измеримой задачи.