В этом модуле мы прошли путь: volcano model → vectorized → column store → Postgres columnar extensions. Теперь последний шаг: как принять решение в реальной команде, не сваливаясь ни в «всё на Postgres» (часто кончается миллиардной таблицей и грустным DBA), ни в «срочно ставим ClickHouse» (часто кончается двумя источниками правды и операционным адом).
Этот урок — не про код. Это про инженерный судебный процесс над собственной нагрузкой.
Профиль нагрузки: первая ось
Прежде чем выбирать инструмент, опиши нагрузку формально. Минимум три цифры:
- Read pattern: точечные lookup’ы (PK / индексные) или full-table aggregation?
- Write pattern: row-by-row INSERT или bulk-load / append-only stream?
- Latency SLA: P50 / P99 для каждой категории запросов.
И ещё одна — про данные:
- Hot data size: сколько данных активно читается за последние N дней.
- Cold archive size: сколько лежит исторически.
Только после этого можно говорить, кто справится с этим лучше — Postgres, расширение или отдельный движок.
Грубая карта. По вертикали — отношение read/write точек, по горизонтали — размер активной выборки на типичный запрос.
Что часто упускают
Перед деревом — три типичных «слепых пятна»:
- Hot path != cold path. Аналитика смотрит на «общий объём данных», но реально дневной запрос обычно трогает 1-7 дней. 5 TiB суммарных данных — это часто 50 GiB активного диапазона. Часто хватает индекса по
time DESCи partitioning по неделе. - Concurrency как параметр. Один запрос за 10 секунд — норма. 100 параллельных запросов за 10 секунд — это уже 1000 секунд работы CPU, которая делит ресурсы. Многие движки выбираются по single-query benchmark, а в проде падают на concurrency.
- Write amplification. UPDATE одной строки в column store трогает N файлов. UPDATE в Postgres — копия одной строки + WAL. На write-heavy нагрузке column store катастрофически проседает.
Эти три измерения часто перевешивают «100x на TPC-H».
Decision tree
Дерево, которым реально можно пользоваться на ретро или на разговоре с архитектором:
Шаг 1: основная нагрузка — транзакционная?
Если 80%+ запросов — это SELECT/INSERT/UPDATE/DELETE по индексу, на десятки или сотни строк, и нагрузка измеряется в QPS (тысячи в секунду), а не в bytes scanned:
→ Postgres / MySQL. Тут нет конкурентов. ACID, row store, MVCC под индексные lookup’ы — это их работа. Не пытайся переехать на ClickHouse «потому что аналитики хвалят»: операционно потеряешь, latency на single-row read будет хуже.
Шаг 2: основная нагрузка — аналитическая?
Если ты считаешь нагрузку в «GB сканированных за запрос» и «секундах ответа на dashboard», и точечных update’ов мало (или нет):
- < 100 GiB hot, embedded аналитика → DuckDB. В процессе сервиса, в Jupyter, в pipeline. Не требует server.
- 100 GiB — 10 TiB, real-time queries → ClickHouse. Vectorized + columnar + distributed. Лучшее соотношение цена/скорость на свой класс.
- 10 TiB+, cloud, federated → Snowflake / BigQuery / Databricks. Платишь за compute раздельно от storage, не управляешь кластерами.
- Историческое архивы Parquet в S3 → Trino / Athena / Spark. SQL-движок поверх существующего lake.
Шаг 3: смешанная нагрузка (большинство случаев)
Это типично: продукт пишет в Postgres транзакции, и те же данные хочется анализировать. Опции:
-
Postgres + read replica + умные индексы. Работает до ~100 GiB cold + ~10 GiB hot. После этого Seq Scan не помещается в shared buffers, и каждый дашборд душит OLTP.
-
Postgres + Citus columnar / Hydra / TimescaleDB. Аналитический workload селит в columnar-таблицы в том же Postgres. Работает до ~1 TiB cold data. Не требует второго стека.
-
Postgres OLTP + CDC → ClickHouse / DuckDB / Snowflake. Изменения транзакционной БД реплицируются в OLAP-движок (через Debezium, native PG logical replication, или Airbyte/Fivetran). Два источника правды, но с eventual consistency. Это самый частый production-паттерн на 2026 год.
-
HTAP (TiDB, SAP HANA, AlloyDB). Один движок и для транзакций, и для аналитики. Под капотом — две копии данных. Дорогое решение, имеет смысл при операционной экономии.
Шаг 4: edge cases
- Time-series с append-only: TimescaleDB / InfluxDB / QuestDB. Они умеют compression, downsampling, retention policies встроенно.
- Графы: Neo4j, или Apache AGE / Memgraph.
- Search: Elasticsearch / OpenSearch (но не для всего, что «найти» — половина задач решается обычным B-tree + trigram-index в Postgres).
- Embedded vector search: PGVector (extension Postgres), Qdrant, Weaviate.
Анти-паттерны архитектуры данных
Помимо «правильного выбора движка» есть набор архитектурных решений, которые ломают любой движок:
- Один движок под всё на бесконечной кривой роста. Постгресс справляется до определённой точки, потом кончается. Если ты видишь, что через 6 месяцев данных будет в 5 раз больше, начни планировать переход сейчас, не когда упадёшь.
- Преждевременная микросервисная разрезка БД. Каждый сервис со своей PG. Аналитика обязана джойнить — но не может. Решение через CDC в общий DWH, но это +ETL.
- EAV-таблицы (entity-attribute-value). Когда «удобство схемы» приводит к таблице из 4 колонок
(entity_id, attr_name, value_text, value_int)на 1B строк. Любой запрос — JOIN на самого себя. Ни Postgres, ни ClickHouse это не лечат — это просто плохая модель. - JSON везде. JSONB удобен, но если 80% колонок live внутри jsonb, ты теряешь column store benefits. Аналитика на jsonb — это всегда medlennее, чем на отдельных колонках.
Эти проблемы не решаются движком. Их решает refactor data model.
Чего стоит избегать
1. «Поставим ClickHouse, потому что быстрее»
Без понимания, что ClickHouse — это другая модель данных, другой SQL, другая операционка. Не поддерживает FK, UNIQUE, классических транзакций, sub-second freshness. Если ты делаешь banking — не сюда. Если делаешь дашборд по логам — да.
2. «Один Postgres под всё, до миллиарда строк»
Это работает, пока read paths остаются индексными. Как только аналитики начинают писать SELECT date_trunc('hour', ts), count(*) FROM events WHERE ts > NOW() - INTERVAL '30 days' GROUP BY 1 на 500M строках без partition — Postgres начинает регулярно валиться в Seq Scan на десятки минут. И это не «плохой Postgres», это архитектурно не его задача.
3. «Спарк нужен всем»
Spark — это distributed compute поверх HDFS/S3. Имеет смысл при > 10 TiB и/или сложных ML-пайплайнах. На 100 GiB Spark проигрывает DuckDB по latency и стоит в 10 раз больше operational effort. Спарк — это часто overengineering для middle-stage компании.
4. «Сначала data lake, потом увидим»
Pre-mature lake — это data swamp. Без чёткого SQL-доступа и каталога схем (Iceberg / Delta / Hudi) — данные есть, но недоступны. Сначала запрос, потом storage.
Cost модель: что добавляет ещё один движок
Кроме technical performance, переход на отдельный OLAP-движок добавляет operational tax:
Каждый из этих пунктов — реальная нагрузка на инжиниринговую команду. Считай в person-months и multiply на zарплату.
Поэтому правило: второй движок — это бизнес-решение, не техническое. Если 10x ускорение дашборда стоит 3-6 человекомесяцев — может стоить, может нет.
Альтернатива: вертикальное масштабирование PG
Перед переходом на другой движок часто недооценивают, насколько может вытащить просто более жирный сервер + правильная конфигурация Postgres.
- 256 GiB RAM + NVMe SSD + 32-64 ядра — это диапазон, в котором одна Postgres-инстанция может обрабатывать терабайтные нагрузки.
shared_buffers = 25% RAM,effective_cache_size = 75% RAM,work_memper-query tuning,max_parallel_workers_per_gatherпод число ядер.- BRIN-индексы на time-колонках вместо B-tree — резко меньший размер и быстрее на range scan.
- Materialized views с инкрементальным refresh для аналитических dashboards.
- Partition pruning по диапазону времени — даёт chunk-level skip аналогично column store.
Это часто покупает 6-12 месяцев перед тем, как реально нужно второе хранилище. И главное — это не требует операционной перестройки команды.
Симптомы, что пора переходить
Не теоретические, а наблюдаемые:
- autovacuum не успевает на горячих таблицах. dead_tuples > live_tuples × 2.
- Аналитический запрос блокирует OLTP (через shared buffers, через locks на partitions).
EXPLAIN ANALYZEпоказывает 90%+ времени в Seq Scan иparallel workers planned > N, но фактически работает 1 worker (shared resources исчерпаны).- Дашборды показывают вчерашние данные, потому что построение занимает > 1 часа.
- Аналитики бегут в Pandas — выгружают CSV, потому что SQL невыносим. Это сильный сигнал, что движок не справляется.
Если узнаёшь себя в 3+ симптомах — обсуждай переход с командой.
Смотрим на типичный пациент в sandbox
Large dataset (50K customers + 500K orders). Тяжёлый OLAP-запрос: join + filter + group by + sort. Это масштаб, на котором Postgres всё ещё нормально работает; представь умножение строк в 1000 раз и пересчёт времён.
Смотри на план: HashAggregate поверх Hash Join поверх Seq Scan. На 500K строк это работает за сотни мс. На 500M — за минуты. На 5B — Postgres сдаётся. На vectorized engine 5B строк такой запрос проходит за секунды.
И второй — точечный lookup, ради контраста, где Postgres вне конкуренции:
Точечный запрос. Index scan на primary key, миллисекундная latency. Это случай, когда никакой column store не догонит row store. Постгресс — про OLTP.
Эти два запроса в одной и той же БД — это и есть аргумент за гибридную архитектуру: PG для второго запроса, OLAP-движок для первого, CDC посередине.
Алгоритм за 60 секунд
Если на ревью архитектуры тебя спрашивают «оставаться на Postgres или брать другой движок?» — пройди по чек-листу:
- Сколько GiB активной выборки на типичный запрос? Если < 1 GiB — PG хватит.
- Какой P99 latency на дашбордах нужен? Если > 5 секунд приемлемо — PG (с индексами) хватит.
- Сколько TiB cold data? Если < 100 GiB — PG + columnar extension хватит.
- Аналитика — primary workload? Если да и (1)–(3) проигнорированы — нужен отдельный движок.
- Команда из 3 человек или 30? Если 3 — добавочный stack дорого. Если 30 — норма.
- SLA транзакций критичен? Если да — OLTP остаётся на PG, аналитика идёт куда-то ещё.
Этот checklist не даст идеального ответа, но даст разговор, в котором можно прийти к ответу.
Несколько паттернов из жизни
Несколько типичных архитектур, к которым команды реально приходят:
- Стартап на ранней стадии: один Postgres, всё там. Когда тяжёлая аналитика появляется — DuckDB локально в сервисе для BI-задач. Никаких CDC, никакого второго движка.
- Mid-stage SaaS (Series A-B): Postgres OLTP + read replica для BI. Когда replica начинает захлёбываться — TimescaleDB / Citus columnar для time-series и архивов. Аналитики продолжают писать SQL на PG.
- Late-stage / enterprise: Postgres OLTP + ClickHouse / Snowflake для аналитики + Debezium / native PG logical replication для CDC. dbt-модели на Snowflake. Дашборды на Metabase / Looker против OLAP-движка.
- Pure data company (Databricks-like): всё на S3 Parquet + Iceberg каталог + Trino/Spark/DuckDB по необходимости. PG — только для metadata services.
Эти паттерны проявляются естественно по мере роста. Главное — не пропустить момент перехода и не сделать его раньше, чем реально нужно.
Чек-лист модуля
- Профиль нагрузки определяется read/write pattern + latency SLA + размер данных. Без этих цифр любой разговор о выборе движка — ритуал.
- Postgres для OLTP — нет конкурентов. ACID, row store, MVCC, индексы.
- DuckDB для embedded OLAP до 100 GiB. ClickHouse для real-time OLAP до 10 TiB. Snowflake/BigQuery для cloud-scale. Spark/Trino для huge data lakes.
- Смешанная нагрузка → обычно два движка с CDC. HTAP — нишевый компромисс.
- Каждый дополнительный движок добавляет 3-6 person-months operational tax + продолжающиеся затраты.
- Симптомы перехода: autovacuum не справляется, OLAP блокирует OLTP, dashboard latency растёт, аналитики выгружают данные в Pandas.
- Симптомы преждевременного перехода: «звучит современно», «у всех есть ClickHouse», без анализа реальной нагрузки.
- Решение про второй движок — бизнес-решение, не техническое.