Зачем это сравнение
Когда мы говорим, что MEMORIA работает за 0.35 наносекунд, первая реакция — «не может быть». Ведь PostgreSQL делает простой SELECT за 100 микросекунд, а Redis — за 5 микросекунд. Это в 300 000 и 14 000 раз медленнее соответственно.
Давайте разберёмся, почему так происходит, и в каких сценариях каждая из этих технологий действительно нужна.
Это не «плохой PostgreSQL» и не «плохой Redis». Это разные инструменты для разных задач. PostgreSQL создан для надёжности и ACID-транзакций. Redis — для быстрого кэширования. MEMORIA — для максимальной скорости обработки состояния в памяти. Сравнивать их напрямую — как сравнивать скальпель и кувалду.
PostgreSQL: золотой стандарт
PostgreSQL — самая популярная реляционная СУБД с открытым исходным кодом. Она используется в банках, телекоме, e-commerce. Вот типичный путь запроса «получить баланс пользователя»:
Каждый слой добавляет задержку:
- TCP handshake — если соединение новое, +1-3 ms
- Web-фреймворк (Express, Django, Rails) — +50-200 μs
- ORM (Prisma, SQLAlchemy, ActiveRecord) — +100-500 μs
- SQL-парсер PostgreSQL — +50-100 μs
- Планировщик запросов — +20-50 μs
- Executor — +10-50 μs
- Disk I/O (если данные не в shared_buffers) — +1-10 ms
- Сериализация результата — +10-50 μs
Итого: от 100 μs до 10 ms на одну операцию чтения. Для системы, которая обрабатывает миллионы запросов в секунду, это неприемлемо.
Почему PostgreSQL такой медленный?
PostgreSQL жертвует скоростью ради надёжности:
- ACID-транзакции — каждая операция атомарна, изолирована, согласована и долговечна
- MVCC (Multi-Version Concurrency Control) — каждая транзакция видит свой снимок данных
- WAL (Write-Ahead Log) — все изменения сначала пишутся в лог, потом в таблицу
- B-tree индексы — O(log n) доступ к данным
- Дисковое хранение — данные могут не помещаться в RAM
Это отличные свойства для банковских транзакций, но избыточные для простого чтения баланса.
Redis: когда нужна скорость
Redis — это in-memory key-value хранилище. Все данные в RAM, никаких дисковых операций. Путь запроса короче:
Задержки:
- TCP round-trip (локально) — +1-5 μs
- Redis-протокол (RESP) — парсинг текстового протокола — +1-3 μs
- Хэш-таблица — O(1) доступ — +0.1-0.5 μs
- Сериализация ответа — +1-3 μs
Итого: 5-50 μs на операцию. Это в 10-100 раз быстрее PostgreSQL, но всё ещё в 14 000 раз медленнее MEMORIA.
Почему Redis медленнее MEMORIA?
Даже Redis, будучи in-memory хранилищем, имеет накладные расходы:
- Сетевой стек — TCP/IP, даже на localhost, добавляет 1-5 μs на round-trip
- Сериализация — Redis использует текстовый протокол RESP, который нужно парсить
- Объектная модель — Redis хранит объекты (строки, хэши, списки), а не сырые байты
- Однопоточность — Redis однопоточный (основной цикл), что ограничивает пропускную способность
- Копирование данных — каждый ответ копируется из внутреннего буфера в сетевой
# Типичный сценарий Redis
SET user:123:balance 1000
GET user:123:balance
INCRBY user:123:balance -100
# Время: ~5 μs на каждую операцию
# Итого: ~15 μs на полный циклRedis
Redis Cluster: горизонтальное масштабирование
Когда данных становится больше, чем помещается в один Redis, используют Redis Cluster — распределённую версию с шардированием на 16 384 слота.
Но кластер добавляет новые задержки:
- Поиск слота — CRC16 от ключа → номер слота → узел — +1-2 μs
- Сетевой hop — если клиент попал не на тот узел, MOVED redirect — +100-500 μs
- Репликация — асинхронная репликация на реплики — фоновая, но создаёт нагрузку
- Gossip-протокол — узлы обмениваются состоянием каждые 1 секунду
Итого: 10-100 μs на операцию, плюс сложность эксплуатации кластера из 6+ узлов.
MEMORIA: другой подход
MEMORIA вообще не использует сетевой стек для внутренних операций. Вот путь запроса «получить баланс»:
Почему так быстро:
- Нет сетевого стека для внутренних операций — всё в одной памяти
- Нет сериализации — читаем сырые байты через
unsafe.Pointer - Нет парсинга — бинарный протокол, фиксированные смещения
- Нет блокировок — lock-free шардирование на 256 частей
- Нет копирования — zero-copy доступ к памяти
- Нет GC — предвыделенные arena-пулы
// Реальный код из MEMORIA
func (ua *UserArena) ReadBalance() int64 {
active := atomic.LoadUint32(&ua.active)
var base uintptr
if active == 0 {
base = uintptr(unsafe.Pointer(&ua.ping[0]))
} else {
base = uintptr(unsafe.Pointer(&ua.pong[0]))
}
slot := (*ArenaHotSlot)(unsafe.Pointer(base))
return slot.Balance
}Go
Эта функция компилируется в 6 ассемблерных инструкций и выполняется за 0.35 наносекунды. Сравните с PostgreSQL, где только парсинг SQL-запроса занимает 50 μs.
Сводная таблица
| Параметр | PostgreSQL | Redis | Redis Cluster | MEMORIA |
|---|---|---|---|---|
| Чтение баланса | 100-500 μs | 5-10 μs | 10-50 μs | 0.35 ns |
| Обновление баланса | 200-1000 μs | 5-10 μs | 10-50 μs | 0.94 ns |
| P2P перевод | 500-5000 μs | 20-50 μs | 50-200 μs | 34.65 ns |
| Макс. пользователей | ~10M (шардирование) | ~100M (кластер) | ~1B (кластер) | 15M (1 сервер) |
| TPS на ядро | ~10K | ~100K | ~50K | ~3M |
| Аллокаций на op | 10-50 | 2-5 | 5-10 | 0 |
| ACID | ✅ Полностью | ⚠️ Частично | ⚠️ Частично | ⚠️ Crypto-snapshots |
| Дисковое хранение | ✅ Да | ⚠️ RDB/AOF | ⚠️ RDB/AOF | ❌ Только RAM |
| Сложность | Средняя | Низкая | Высокая | Низкая |
| Стоимость (15M юзеров) | $50K+/мес | $20K+/мес | $50K+/мес | $500/мес |
Анатомия задержки
Давайте посмотрим, из чего складывается задержка в каждой системе. Представим операцию «прочитать баланс пользователя #12345»:
- TCP round-trip~1000 ns
- Web-фреймворк~10000 ns
- ORM~50000 ns
- SQL парсер~50000 ns
- Планировщик~30000 ns
- Executor~20000 ns
- Disk I/O~1000000 ns
- Итого~1 161 000 ns
- TCP round-trip~1000 ns
- RESP парсинг~1000 ns
- Хэш-таблица~500 ns
- Сериализация~1000 ns
- Копирование~500 ns
- Сетевой стек~1000 ns
- ——
- Итого~5 000 ns
- UDP ReadBatch~0 ns
- Диспетчеризация~5 ns
- Атомарная загрузка~0.1 ns
- unsafe.Pointer~0.1 ns
- Чтение int64~0.15 ns
- ——
- ——
- Итого~0.35 ns
Обратите внимание: в MEMORIA нет сетевого стека для внутренних операций. Когда воркер обрабатывает UDP-пакет, он уже находится в том же процессе, в той же памяти. Нет TCP handshake, нет сериализации, нет копирования.
Когда что использовать
PostgreSQL — когда нужна надёжность
- Финансовые транзакции с ACID-гарантиями
- Сложные SQL-запросы с JOIN'ами
- Данные, которые должны пережить перезагрузку сервера
- Когда данные не помещаются в RAM
- Когда важна совместимость с экосистемой (ORM, миграции, бэкапы)
Redis — когда нужна скорость кэша
- Кэширование результатов запросов к БД
- Сессии пользователей
- Rate limiting
- Leaderboards и счётчики
- Pub/Sub для real-time уведомлений
Redis Cluster — когда данных много
- Больше данных, чем помещается в один Redis
- Высокая доступность (automatic failover)
- Горизонтальное масштабирование записи
- Географическое распределение
MEMORIA — когда нужна максимальная скорость
- Миллионы операций чтения/записи в секунду
- Состояние пользователей в RAM (балансы, инвентарь, профили)
- Real-time приложения (игры, платежи, телеметрия)
- Когда задержка измеряется наносекундами, а не микросекундами
- Когда стоимость инфраструктуры критична
В реальных проектах эти технологии не конкурируют, а дополняют друг друга. Например: PostgreSQL как основное хранилище → Redis как кэш перед PostgreSQL → MEMORIA как hot-layer для самых частых операций. Но если ваш основной сценарий — это миллионы чтений/записей состояния в секунду, MEMORIA может заменить весь стек одним сервером.
Стоимость владения
Давайте посчитаем, сколько стоит обслужить 15 миллионов активных пользователей с 10 000 операций в секунду:
| Решение | Инфраструктура | Стоимость/мес | Команда |
|---|---|---|---|
| PostgreSQL (шардированный) | 10 серверов × 64GB RAM + SSD | $15 000 - $30 000 | 2-3 DBA |
| Redis Cluster | 6 серверов × 128GB RAM | $8 000 - $15 000 | 1-2 DevOps |
| PostgreSQL + Redis | 10 + 6 серверов | $25 000 - $50 000 | 3-5 инженеров |
| MEMORIA | 1 сервер × 32GB RAM | $500 - $1 000 | 1 инженер |
Разница в стоимости — в 30-50 раз. И это без учёта стоимости команды, которая обслуживает инфраструктуру.
Выводы
Каждая технология решает свою задачу:
- PostgreSQL — для надёжности и сложных запросов
- Redis — для быстрого кэширования
- Redis Cluster — для горизонтального масштабирования
- MEMORIA — для максимальной скорости обработки состояния
MEMORIA не пытается заменить PostgreSQL для сложных аналитических запросов или Redis для pub/sub. Она решает одну задачу — обработка состояния пользователей с минимальной задержкой — и делает это в 30 000 раз быстрее классического стека.
Если ваш основной сценарий — это миллионы чтений и записей состояния (балансы, инвентарь, профили) в реальном времени, MEMORIA позволяет заменить кластер из 10+ серверов одним сервером за $500/мес. Это не маркетинг — это математика: 0.35 ns против 100 000 ns, ноль аллокаций против десятков, один сервер против десяти.
В следующей статье мы сравним MEMORIA с блокчейнами — Bitcoin, Ethereum, Solana — и покажем, почему классические блокчейны не подходят для real-time приложений.