Learning Platform
Урок 15.05 · 23 мин
Продвинутый
ArchitectureDecision makingOLAPOLTPClickHouseDuckDBSparkTrino

В этом модуле мы прошли путь: 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 точек, по горизонтали — размер активной выборки на типичный запрос.

single-row read/write, < 1K строк на запросOLTP → Postgres / MySQL
aggregation 1M-100M строк, batch писателиOLAP → ClickHouse / DuckDB / Snowflake
time-series, append-only, range queryTimescaleDB / InfluxDB / QuestDB
data lake > 1 TiB, ad-hoc analyst SQLSpark / Trino / Athena поверх Parquet/Iceberg
комбо OLTP + OLAPвсегда два движка + CDC; HTAP — нишевый компромисс

Что часто упускают

Перед деревом — три типичных «слепых пятна»:

  • 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 queriesClickHouse. Vectorized + columnar + distributed. Лучшее соотношение цена/скорость на свой класс.
  • 10 TiB+, cloud, federatedSnowflake / BigQuery / Databricks. Платишь за compute раздельно от storage, не управляешь кластерами.
  • Историческое архивы Parquet в S3Trino / Athena / Spark. SQL-движок поверх существующего lake.

Шаг 3: смешанная нагрузка (большинство случаев)

Это типично: продукт пишет в Postgres транзакции, и те же данные хочется анализировать. Опции:

  1. Postgres + read replica + умные индексы. Работает до ~100 GiB cold + ~10 GiB hot. После этого Seq Scan не помещается в shared buffers, и каждый дашборд душит OLTP.

  2. Postgres + Citus columnar / Hydra / TimescaleDB. Аналитический workload селит в columnar-таблицы в том же Postgres. Работает до ~1 TiB cold data. Не требует второго стека.

  3. Postgres OLTP + CDC → ClickHouse / DuckDB / Snowflake. Изменения транзакционной БД реплицируются в OLAP-движок (через Debezium, native PG logical replication, или Airbyte/Fivetran). Два источника правды, но с eventual consistency. Это самый частый production-паттерн на 2026 год.

  4. 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:

TCO добавочного движка: то, о чём редко говорят на демо

Каждый из этих пунктов — реальная нагрузка на инжиниринговую команду. Считай в person-months и multiply на zарплату.

ETL/CDC pipelineинфра + поддержка + monitoring
schema syncALTER в OLTP → миграция в OLAP
two sources of truthкогда они расходятся — кому верить?
operations: backup, alerting, upgrade× 2
SQL dialectClickHouse SQL ≠ Postgres SQL
бизнес-логикаreports, dbt-models переписать
итог~3-6 person-months на стандартный setup ClickHouse + CDC + 2-3 dashboard миграции

Поэтому правило: второй движок — это бизнес-решение, не техническое. Если 10x ускорение дашборда стоит 3-6 человекомесяцев — может стоить, может нет.

Альтернатива: вертикальное масштабирование PG

Перед переходом на другой движок часто недооценивают, насколько может вытащить просто более жирный сервер + правильная конфигурация Postgres.

  • 256 GiB RAM + NVMe SSD + 32-64 ядра — это диапазон, в котором одна Postgres-инстанция может обрабатывать терабайтные нагрузки.
  • shared_buffers = 25% RAM, effective_cache_size = 75% RAM, work_mem per-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 раз и пересчёт времён.

PostgreSQL

Смотри на план: HashAggregate поверх Hash Join поверх Seq Scan. На 500K строк это работает за сотни мс. На 500M — за минуты. На 5B — Postgres сдаётся. На vectorized engine 5B строк такой запрос проходит за секунды.

И второй — точечный lookup, ради контраста, где Postgres вне конкуренции:

Точечный запрос. Index scan на primary key, миллисекундная latency. Это случай, когда никакой column store не догонит row store. Постгресс — про OLTP.

PostgreSQL

Эти два запроса в одной и той же БД — это и есть аргумент за гибридную архитектуру: PG для второго запроса, OLAP-движок для первого, CDC посередине.

Алгоритм за 60 секунд

Если на ревью архитектуры тебя спрашивают «оставаться на Postgres или брать другой движок?» — пройди по чек-листу:

  1. Сколько GiB активной выборки на типичный запрос? Если < 1 GiB — PG хватит.
  2. Какой P99 latency на дашбордах нужен? Если > 5 секунд приемлемо — PG (с индексами) хватит.
  3. Сколько TiB cold data? Если < 100 GiB — PG + columnar extension хватит.
  4. Аналитика — primary workload? Если да и (1)–(3) проигнорированы — нужен отдельный движок.
  5. Команда из 3 человек или 30? Если 3 — добавочный stack дорого. Если 30 — норма.
  6. SLA транзакций критичен? Если да — OLTP остаётся на PG, аналитика идёт куда-то ещё.

Этот checklist не даст идеального ответа, но даст разговор, в котором можно прийти к ответу.

Проверка знанийKnowledge check
Стартап на seed-стадии: 3 backend-инженера, продукт — SaaS для логистики. БД Postgres 100 GiB, типичные запросы — single-row read/write для приложения + 5 дашбордов для customers и команды. Самый медленный дашборд — еженедельный cohort-анализ за последний месяц, занимает 90 секунд. Аналитики жалуются. CTO предлагает поставить ClickHouse. Согласишься, или предложишь что-то другое?
ОтветAnswer
Не соглашусь сразу. Аргументы: (1) Размер данных 100 GiB не критичен — это умещается в memory приличного сервера (32-64 GiB RAM + индексы в shared buffers). 90 секунд на cohort — скорее проблема плана, чем архитектуры. (2) Команда 3 человека. Добавление ClickHouse — это +1 движок, +CDC pipeline, +operational overhead, +миграция dashboard'ов на ClickHouse SQL. 3-6 person-months. На seed-стадии это огромный отвлечение от продукта. (3) Шаги, которые я бы попробовал перед ClickHouse: - EXPLAIN ANALYZE cohort-запроса, найти узкое место. Часто — отсутствие индекса по cohort_date или неоптимальный hash join. - Добавить partial index или covering index на нужные колонки. - Materialized view с инкрементальным refresh. Cohort часто можно пред-агрегировать. - work_mem повышение для аналитических соединений. - Если time-series — TimescaleDB hypertable, compress old chunks. (4) Если после этого 90 секунд не падают до 5-10 секунд (это разумный SLA для weekly dashboard), и нагрузка растёт — тогда уже думаем про CDC + ClickHouse. Но обычно (1)-(3) хватает на этой стадии. Принцип: переход на отдельный движок — это последнее средство. Команды часто хотят сделать его первым, потому что 'современнее звучит'. Это операционная мина с долгим эффектом.

Несколько паттернов из жизни

Несколько типичных архитектур, к которым команды реально приходят:

  • Стартап на ранней стадии: один 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», без анализа реальной нагрузки.
  • Решение про второй движок — бизнес-решение, не техническое.
Архитектуры на ClickHouse: PostHog, Cloudflare, GitLab Фреймворк выбора формата по workload

Проверьте понимание

Результат: 0 из 0
Концептуальный
Вопрос 1 из 4. Какие три цифры обязательно должны быть в описании нагрузки перед выбором движка (Postgres vs ClickHouse vs Snowflake vs Spark)?

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 5