Перейти к содержимому

Данные: PostgreSQL и Redis

Данные — сердце любого приложения. Пользователь, заказ, сессия, лидерборд — всё это состояние, которое нужно где-то хранить, быстро читать и надёжно не терять. В этом разделе мы разбираем два главных хранилища современного бэкенда: PostgreSQL как систему записи (source of truth) и Redis как ускоритель и координатор.

В краткой версии учебника ты уже видел обзор: нормализация по формам, B-Tree/GIN-индексы, транзакции ACID, Prisma против Drizzle, PgBouncer, cache-aside и лидерборды на Sorted Sets. Здесь мы копаем глубже — до уровня «могу объяснить на собеседовании, как PostgreSQL физически хранит строки и почему LIKE '%foo' не использует индекс».

Почему данные — это отдельная дисциплина

Заголовок раздела «Почему данные — это отдельная дисциплина»

Многие разработчики относятся к БД как к чёрному ящику: «ORM сама всё сделает». До первого инцидента. Типичная история из практики: релиз прошёл, нагрузка выросла в десять раз, и в 3 часа ночи Grafana показывает, что p95 запроса вырос с 20 мс до 8 секунд. Оказывается, ORM генерировала запрос с LIKE '%...%' по непроиндексированной колонке, а транзакция, открытая на один HTTP-запрос, держала row-lock на горячей строке и каскадом валила все остальные запросы.

Инженер, который понимает устройство своих данных, такой инцидент предотвращает на этапе код-ревью. Он видит: здесь нужен составной индекс с правильным порядком колонок, здесь — частичный индекс для «только неудалённых», здесь транзакцию надо сократить до двух UPDATE, а здесь вообще убрать из неё HTTP-вызов.

Главы выстроены от фундамента к продакшену, каждая опирается на предыдущую:

  1. Моделирование данных в PostgreSQL — типы данных и их компромиссы (numeric против float, timestamptz против timestamp), нормализация от 1НФ до 3НФ на полных примерах схем, когда денормализация оправдана, суррогатные ключи, внешние ключи и ограничения CHECK/UNIQUE. В конце — ER-проектирование pet-проекта и миграции схемы.

  2. Индексы в PostgreSQL — как устроен B-Tree под капотом и когда он не работает, составные индексы и правило leftmost prefix, покрывающие индексы через INCLUDE, GIN для jsonb и полнотекста, частичные индексы, разбор плана через EXPLAIN ANALYZE, а также VACUUM, autovacuum и bloat — то, о чём молчат туториалы.

  3. Транзакции и блокировки — ACID под капотом: WAL и MVCC. Все уровни изоляции с воспроизводимыми аномалиями на примерах с двумя сессиями, разница READ COMMITTED и REPEATABLE READ в PostgreSQL, SERIALIZABLE и SSI, блокировки строк и таблиц, чтение разбора дедлока, FOR UPDATE / SKIP LOCKED для очередей, advisory locks.

  4. Prisma, Drizzle и пулинг соединений — Prisma: схема, генерация клиента, миграции и drift, проблема N+1, interactive transactions, ограничения ORM. Drizzle: SQL-like синтаксис и миграции. Дальше — pooling: почему max_connections — это ловушка, PgBouncer в режимах session и transaction, интеграция с Prisma, сиды.

  5. Кэш-паттерны в Redis — cache-aside с TTL и джиттером, защита от cache stampede через mutex и permValue, write-through и write-behind, стратегии инвалидации, сессии, rate limiting: token bucket на Lua-скриптах и sliding window, кэширование API-ответов, антипаттерны.

  6. Структуры данных Redis — строки, хэши, списки, сеты, Sorted Sets с полными примерами команд, лидерборд на ZADD/ZREVRANGE/ZRANGEBYSCORE, Pub/Sub против Streams (XADD/XREAD, consumer groups), Redis Streams как очередь, RedisJSON и RediSearch кратко, персистентность RDB против AOF, eviction-политики.

Pet-проект учебника — платформа с заказами: пользователи, товары, корзина, заказы, платежи, уведомления. К концу раздела ты спроектируешь для него схему в 3НФ, построишь индексы под реальные запросы, реализуешь перевод денег с защитой от гонок, подключишь Prisma с миграциями через PgBouncer и вынесешь сессии, кэш каталога и лидерборд в Redis. Это тот же стек, который ты встретишь в реальной работе — просто маленький и безопасный для экспериментов.