Модуль 1. Введение: три мира данных
Цель модуля: получить карту территории до погружений — понимать, зачем в одной системе живут две СУБД, куда какая задача ложится, и какие паттерны связывают их в единое целое. Дальше каждый модуль открывает свой участок этой карты.
Перед началом: SQL (SELECT, JOIN, GROUP BY), Docker, Go на уровне go-basic/go-intermediate трека. Инструменты: docker-compose (приложится к каждому модулю), Go 1.22+. Трудоёмкость: ~4 ч (теория 2 ч · практика 2 ч · квиз 0.5 ч).
🔥 Инцидент: отчёт, который положил прод
Понедельник, час пик в OrderFlow: операторы разгребают заказы, платежи идут
потоком, маркетинг готовит квартальную сводку по выручке. Аналитик запускает
тяжёлый SELECT по таблице заказов — прямо на мастер-PostgreSQL, в ту же базу,
где живут заказы и платежи. Так делали и раньше, но теперь данных стало в разы
больше.
Через пару минут диск упирается в потолок: в Grafana iowait под 100%, p99
оформления заказа вырастает со 120 мс до нескольких секунд, свободные коннекты
кончаются, а операторы видят растущую очередь заказов. Отчёт при этом падает по
таймауту: квартальная выручка не посчитана, заказы пострадали.
Первым делом запрос убили через pg_terminate_backend — сервис ожил за минуты.
Дальше решили «просто оптимизировать отчёт»: добавили индексы и перенесли запуск
на ночь. Помогло ровно на месяц: отчётов становится больше, данные растут, и
аналитика снова конкурирует с заказами за один и тот же диск и буферный пул.
Настоящая причина — не в плохом запросе, а в том, что три мира данных свалены в одну СУБД. В модуле соберём карту территории: где кончается OLTP и начинается OLAP, зачем нужен третий, векторный мир, и как CQRS и проекции связывают миры в единую систему.
📖 1.1. OLTP vs OLAP vs Vector: зачем две СУБД вместо одной
Сначала расшифруем аббревиатуры
- OLTP — Online Transaction Processing, обработка транзакций в реальном времени: короткие операции «создать заказ», «списать деньги», «какой статус у заказа 42?» — миллисекунды, десятки строк, ACID.
- OLAP — Online Analytical Processing, аналитическая обработка: «выручка по дням за квартал», «p99 времени оплаты» — секунды и минуты, миллионы и миллиарды строк, агрегаты.
- Vector (векторный поиск) — третий профиль: «найди k ближайших соседей эмбеддинга» — для RAG и рекомендаций (Модуль 10).
Это не маркетинговые ярлыки, а профили нагрузки: разные формы запросов, разные требования к записи, разная физическая организация данных. Дальше по курсу OLTP = PostgreSQL, OLAP = ClickHouse — но важно помнить, что это классы задач, а не названия продуктов (OLTP-баз много, OLAP-движков тоже).
Два профиля нагрузки
Одна и та же «таблица заказов» живёт в двух совершенно разных мирах:
| OLTP (PostgreSQL) | OLAP (ClickHouse) | |
|---|---|---|
| Что делает | много мелких транзакций | мало тяжёлых аналитических запросов |
| Размер запроса | миллисекунды, десятки строк | секунды, миллиарды строк |
| Запись | построчно, точечно, с индексами | большими батчами, append-heavy |
| Изменения | UPDATE/DELETE — норма | почти нет: insert-only + ретроспектива |
| Хранение | row-oriented: строка целиком | column-oriented: колонки отдельно |
| Инвариант | ACID, консистентность здесь и сейчас | throughput и скорость агрегатов |
| Типовой вопрос | «какой статус у заказа 42?» | «выручка по дням за квартал, p99 оплаты» |
Почему row-oriented не тянет аналитику: чтобы посчитать сумму одной колонки по миллиарду строк, PostgreSQL прочитает все колонки всех строк (каждая страница диска — целиком), а ClickHouse прочитает только нужную колонку, сжатую специально под её тип данных, и распараллелит по ядрам. Разница — не в 2 раза, а в 100x. Обратное тоже верно: точечный UPDATE по индексу — работа PostgreSQL, ClickHouse на ней беспомощен.
Третий мир: vector
Отдельный профиль — векторный поиск (эмбеддинги для AI/RAG): тысячамерные плавающие векторы, поиск ближайших соседей. PostgreSQL закрывает его расширением pgvector (опциональный Модуль 10), а на больших масштабах — специализированные Vector DB. Три мира — три инструмента; курс строит первые два и трогает третий.
Цена «одной СУБД на всё»
Соблазн взять одну систему «и OLTP, и аналитику» обходится дорого в обе стороны:
- Аналитика на OLTP: тяжёлые запросы вымывают shared_buffers, создают bloat через долгие транзакции (Модуль 2), реплики отстают; точечные операции деградируют.
- OLTP на OLAP: нет честных транзакций и точечных обновлений; «изменить статус заказа» превращается в перезапись и мутации, latency — не та.
- Векторный поиск без индекса: точное сравнение со всеми строками — O(N).
Правило курса: данные живут там, где их профиль нагрузки. Границы между хранилищами — не костыль, а архитектура.
📖 1.2. Архитектурные паттерны: CQRS, Event-Driven, проекции
CQRS: разделение модели записи и чтения
Command Query Responsibility Segregation — запись идёт в одну модель (нормализованную, под транзакции), чтение — в другую (денормализованную, под запросы):
text[Команды: создать заказ] ──► [PostgreSQL: write model] ──► (синхронизация) ──► [ClickHouse: read model] [Запросы: дашборд, отчёты] ◄────────────────────────────────────────────────────────────────┘
Полный CQRS — это разные БД, разные схемы и событийная синхронизация. Прагматичный (наш случай) — две СУБД, связанные CDC-потоком: PostgreSQL остаётся единственным источником правды, ClickHouse — производная проекция для аналитики.
Event-Driven: изменения как события
Связующее звено между мирами — не «ночной ETL», а поток событий об изменениях. Каждый INSERT/UPDATE/DELETE в PostgreSQL превращается в сообщение (CDC — Change Data Capture, Модуль 6), едет через брокер (Kafka — Модуль 6 брокерского курса) и материализуется в ClickHouse. Отсюда свойства, которые будут проверяться в MVP:
- Казуальность: порядок событий на сущность сохранён (ключ = id сущности).
- Идемпотентность: повтор события не меняет результат (ReplacingMergeTree).
- Eventual consistency: аналитика отстаёт от OLTP на секунды — и это осознанный trade-off, а не баг.
Проекции: производные данные как первосортная сущность
Агрегаты в ClickHouse (материализованные представления) — не «кэш», а честные производные таблицы: посчитаны инкрементально при вставке, живут своей жизнью, восстанавливаются перезаливкой из raw-слоя. Этот стиль — «raw facts + проекции» — пройдет через весь курс (Модуль 7).
📖 1.3. Обзор стека курса
Каждый компонент появляется не «потому что мода», а под конкретную роль:
| Компонент | Роль в курсе | Модули |
|---|---|---|
| PostgreSQL | OLTP: заказы, платежи, состояние | 2–3, 8–9, 11 |
| Расширения PG | распределённость: Citus (шардирование), Patroni (HA), pg_partman, pg_failover_slots | 3, 11 |
| Kafka + Debezium | CDC-транспорт: изменения PG как события | 6, 11 |
| ClickHouse | OLAP: события, агрегаты, дашборды | 4–7, 9, 11 |
| Grafana | визуализация: метрики обеих СУБД и аналитика из CH | 8, 11 |
| Go | прикладной слой: pgx, clickhouse-go, тесты через testcontainers | везде |
| pgvector | (опционально) векторный поиск / RAG | 10 |
Инфраструктура курса — единый docker-compose с профилями: pg (Patroni-кластер),
kafka (+Debezium), ch (2×2 + Keeper), obs (Prometheus + Grafana). Профили
включаются по мере прохождения модулей — как в брокерском курсе.
📖 1.4. Карта расширений PostgreSQL одним взглядом
Детальный разбор — весь Модуль 3 ⭐ (сердце курса), здесь — ориентир:
| Группа | Плагины | Одной фразой |
|---|---|---|
| Шардирование | Citus, pg_partman | «делим данные, когда один узел исчерпан» |
| Репликация/HA | Patroni, pglogical, pg_failover_slots, Odyssey | «мастер умрёт — система не заметит» |
| CDC | pgoutput, wal2json, Debezium | «каждое изменение — событие» |
| Наблюдаемость | pg_stat_statements, auto_explain, pgAudit | «видеть, что БД делает на самом деле» |
| Эксплуатация | pg_repack, pgBackRest/WAL-G, pg_cron | «жить под нагрузкой годами» |
| Специализированные | pgvector, PostGIS, TimescaleDB, pgcrypto | «особые типы данных» |
Каждая строка этой таблицы — урок Модуля 3 с кодом и лабораторной. ClickHouse расширениями не обрастает — у него другая философия: движки таблиц и встроенная репликация (Модули 4–5).
💻 Практика
- Стек на месте: поднимите docker-compose курса (pg + ch + kafka/debezium +
grafana), убедитесь, что
psql,clickhouse-clientи Kafka UI отвечают. - Почувствуйте разницу: создайте в PostgreSQL таблицу событий на 10 млн строк
(generate_series) и выполните
SELECT date_trunc('day', ts), count(*) ... GROUP BY; затем то же в ClickHouse черезgenerateRandomи вставку батчем. Сравните время — цифры станут личным опытом, а не лозунгом. - Профиль запроса: в PostgreSQL добавьте
pg_stat_statements(Модуль 3 расскажет подробно) и найдите самый дорогой запрос из пункта 2.
🧩 Quiz "Проверь себя"
✅ Итоги модуля
- Объяснить, почему OLTP и OLAP — разные инструменты, и что даёт колоночное хранение.
- Нарисовать CQRS-схему курса: PG (write) → CDC → CH (read).
- Назвать три инварианта пайплайна: казуальность, идемпотентность, eventual consistency.
- Назвать компоненты стека и роль каждого.
- Показать на своих цифрах разницу агрегата в PG против ClickHouse.
Шпаргалка модуля
| Вопрос | Ответ |
|---|---|
| Что такое OLTP? | Online Transaction Processing: транзакции в реальном времени, мс, десятки строк, ACID → PostgreSQL |
| Что такое OLAP? | Online Analytical Processing: аналитические агрегаты, секунды, миллиарды строк → ClickHouse |
| Где живут данные? | Там, где их профиль нагрузки: OLTP→PG, агрегаты→CH, эмбеддинги→pgvector/Vector DB |
| Как связаны? | События изменений (CDC) через брокер, не dual-write |
| Кто источник правды? | PostgreSQL; ClickHouse — производная проекция |
| Что если аналитика отстаёт? | Норма (секунды); следим за лагом, а не за «мгновенностью» |
| Что с изменениями в CH? | Нет UPDATE/DELETE — есть версии и ретроспектива (Модуль 6) |
🔗 Мостик к Модулю 2
Карта на руках. Дальше — фундамент, без которого всё остальное рушится: внутренности PostgreSQL — MVCC и vacuum, индексы, планы, блокировки и пулы соединений. Приложим SQL-арсенал (окна, CTE, JSONB) и научимся читать EXPLAIN как открытую книгу.