Два разных мира
Мы разобрали OLTP и OLAP по отдельности. Теперь поставим их рядом и увидим: это две принципиально разные системы, оптимизированные под разные задачи. Понимание этой разницы — фундамент решений в data architecture.
Сравнительная таблица
Каждая строка — фундаментальный design decision, который отличает два мира
Полная сравнительная таблица:
| Параметр | OLTP | OLAP |
|---|---|---|
| Цель | Бизнес-операции в real-time | Аналитика, отчёты, ML |
| Типичный запрос | INSERT/UPDATE/точечный SELECT | Агрегация миллиардов строк |
| Latency | Миллисекунды (P95 меньше 50ms) | Секунды-минуты |
| Throughput | 10K-100K TPS | 10-1000 QPS |
| Объём строк на запрос | 1-100 | Миллионы-миллиарды |
| Объём колонок на запрос | Все (SELECT *) | Несколько из десятков |
| Storage модель | Row-based | Columnar |
| Schema | Нормализованная (3NF) | Денормализованная (star, OBT) |
| Индексы | Много B-tree | Минимум, в основном clustering |
| Транзакции | ACID критичны | ACID есть, но реже нужен |
| Параллелизм | На уровне сессий (concurrent transactions) | MPP внутри одного запроса |
| Update workload | Постоянные мелкие UPDATE | Редкие batch loads |
| Размер БД | GB - десятки TB | TB - PB |
| Hardware | OLTP-оптимизированное (быстрый IOPS, RAM) | OLAP-оптимизированное (CPU, network, columnar) |
| Каноничные системы | Postgres, MySQL, Oracle | Snowflake, BigQuery, ClickHouse |
| Compression ratio | 1.5-3x | 5-20x |
| Cost per TB | Дорого (всё на быстрых SSD) | Дешевле (S3 + compute on-demand) |
Профиль запросов: иллюстрация
Типичный OLTP-запрос
-- Пользователь авторизуется
SELECT id, password_hash, role
FROM users
WHERE email = '[email protected]';
-- 1 строка, 3 колонки, B-tree lookup, ~1ms
-- Создание заказа в транзакции
BEGIN;
INSERT INTO orders (customer_id, amount, status) VALUES (42, 100, 'pending');
UPDATE inventory SET qty = qty - 1 WHERE product_id = 123;
COMMIT;
-- 2 операции, 2 строки, ACID транзакция, ~5ms
Типичный OLAP-запрос
-- Аналитик исследует продажи
SELECT
DATE_TRUNC('month', created_at) AS month,
country,
product_category,
COUNT(*) AS orders_count,
SUM(amount) AS revenue,
AVG(amount) AS avg_check
FROM orders_flat
WHERE created_at >= '2025-01-01'
GROUP BY 1, 2, 3
ORDER BY revenue DESC
LIMIT 100;
-- Скан 5 млрд строк, агрегация по 3 dim, ~3 секунды в ClickHouse, ~15 в Snowflake medium
-- ML feature вычисление
WITH user_lifetime AS (
SELECT
customer_id,
COUNT(*) AS lifetime_orders,
SUM(amount) AS lifetime_value,
AVG(amount) AS avg_order,
MAX(created_at) AS last_order_date,
MIN(created_at) AS first_order_date
FROM orders_flat
GROUP BY customer_id
)
SELECT * FROM user_lifetime WHERE lifetime_value > 1000;
-- Скан всех заказов всех клиентов, ~10 секунд
Видно: OLTP запросы — это операции (с данными что-то делается, чтобы изменить состояние мира). OLAP — это аналитика (читаем много, чтобы что-то понять).
Hardware профиль: где живут деньги
OLTP оптимизирован под точечные I/O и низкую latency:
- NVMe SSD с миллионами IOPS.
- Много RAM для shared buffers (обычно 25-50% от размера БД).
- Не слишком много CPU (типично 4-32 ядра).
- Локальный disk, минимум сетевой латентности.
OLAP оптимизирован под массивный throughput и параллелизм:
- Storage отделён от compute (S3/GCS). Дешёвый PB-storage.
- Compute эластичный: 4-64 ноды поднимаются на час запроса.
- Много CPU (100+ ядер в кластере), отлаженные для SIMD.
- Высокая сетевая пропускная способность для shuffle.
В облаке (Snowflake, BigQuery) ты не управляешь железом напрямую — но architecture видна через ценообразование: storage отдельно ($23/TB/мес для S3-like), compute отдельно (credit-per-second на запрос). Можно держать 1 PB данных и запускать compute раз в день — это нормальный паттерн.
Когда что использовать: правила
Правило 1. Каждый user-facing операционный запрос (логин, заказ, чтение профиля) — OLTP.
Правило 2. Каждый аналитический запрос, дэшборд, отчёт, ML feature engineering — OLAP.
Правило 3. Если в проекте сразу есть и то, и другое — это две системы, синхронизируемые через ETL/CDC. Это норма, не исключение.
Правило 4. Маленький стартап на ранней стадии может обойтись одним Postgres для всего — pg_stat_statements, materialized views, лёгкая аналитика. Это нормально до 50-100 GB и низкой частоты аналитических запросов.
Правило 5. Не пытайтесь делать аналитику на production OLTP реплике — это разрушит OLTP-производительность. Лучше отдельная OLAP, синхронизация через batch ETL или CDC.
Debezium CDC: синхронизация OLTP -> OLAP в real-timeСамое частое заблуждение junior data engineer-ов: “Postgres — это всё”. На объёмах сотен GB это работает, на сотнях TB — нет. Чем раньше команда выстроит правильное разделение OLTP/OLAP, тем меньше боли потом.
Архитектурный паттерн: разделение
OLTP обслуживает операции, OLAP — аналитику, ETL/CDC синхронизирует
User-facingWeb App / Mobile: user-facing application, делает CRUD-операции через OLTP базу. Каждая операция — миллисекунды, тысячи в секунду
Postgres/MySQLOLTP: Postgres/MySQL. Live transactional data, постоянные мелкие операции. Сложные аналитические запросы не запускаются здесь
Airflow / DebeziumETL/CDC: периодическая (или real-time через Debezium) репликация в OLAP. Конвертация форматов (row -> columnar), денормализация, обогащение
Snowflake / ClickHouseOLAP: Snowflake/BigQuery/ClickHouse. Денормализованные wide tables, columnar, partitioned. Аналитика, отчёты, ML
Metabase, LookerBI tools: Metabase, Looker, Tableau. Получают данные из OLAP. Здесь живут дэшборды, отчёты, exploratory analytics
Feature eng, trainingML pipelines: Feature engineering, model training, batch predictions. Читают из OLAP, пишут модели обратно. Возможно — обратно в OLTP для real-time inference
Это стандартный паттерн для любой нетривиальной системы. OLTP остаётся “хирургически” тонкой и быстрой. OLAP принимает на себя вес аналитики и не мешает основной операционной нагрузке.
Что НЕ должно происходить
Антипаттерны разделения:
Антипаттерн 1. Аналитические запросы напрямую на production OLTP. Деградация всего.
Антипаттерн 2. Реплика Postgres для аналитики. Long-running queries блокируют MVCC, реплика отстаёт. Допустимо только для коротких отчётов с tight isolation.
Антипаттерн 3. “У нас один Snowflake на всё, включая OLTP”. Snowflake не оптимизирован под точечные UPDATE, latency десятки-сотни ms на простую операцию. Не подходит для web backend.
Антипаттерн 4. Долгая синхронизация OLTP -> OLAP. Аналитики смотрят данные суточной давности, маркетологи злятся. Решение: real-time CDC через Debezium + Kafka.
Реальный пример: e-commerce
Возьмём интернет-магазин, у которого 10M пользователей и 100M заказов за всю историю.
OLTP (Postgres):
- Таблицы:
users,products,orders,cart,payments. Нормализованные. - Размер: ~500 GB, в основном последние 6 месяцев активных данных.
- Запросы: 50K транзакций/сек в пиковые часы.
- Заказы старше года — архивируются (удаляются из OLTP, остаются только в OLAP).
OLAP (Snowflake):
- Таблицы:
dim_users,dim_products,fact_orders(star schema) +orders_flat(OBT для real-time дашбордов). - Размер: 10 TB полной истории.
- Запросы: 200 запросов в час от BI, ad-hoc от аналитиков, suzbatch ML jobs.
- Синхронизация: Airflow DAG каждый час перегружает свежие orders + Debezium CDC для real-time для критичных таблиц.
BI (Metabase):
- Подключён к Snowflake.
- 50 дэшбордов для маркетинга, финансов, ops.
Это типичная архитектура. Два мира, своя оптимизация, синхронизация через ETL.
Попробуй сам
- Возьми свой существующий project (или придумай). Перечисли все запросы — каждый отметь как OLTP или OLAP по характеристикам (latency требования, объём данных, частота).
- Скачай TPC-H benchmark. Запусти Q1 и Q3 на Postgres и DuckDB. Замерь время. Объясни разницу через сравнительную таблицу.
- Найди в реальном проекте антипаттерн (аналитика на OLTP реплике, или Snowflake для transactional нагрузки). Подумай, как мигрировать.
- Спроектируй architecture для гипотетического приложения: ride-hailing (Uber-like). Где OLTP, где OLAP, какие данные синхронизируются и как?