Итоги модуля: материализованные представления и проекции
Модуль 04 покрывает шесть тем: от базовой инкрементальной MV до фреймворка выбора между четырьмя подходами. Этот урок — краткая справка для повторения перед экзаменом.
Ключевые выводы по урокам
Урок 1: Инкрементальная MV — триггер на INSERT
- MV срабатывает на каждый INSERT-блок, не на отдельные строки
- MV обрабатывает только новые строки из INSERT, не видит состояние таблицы
- TO clause направляет данные в отдельную target-таблицу
- Null engine source — данные отбрасываются, MV получает копию
- Каскадные MV: MV A пишет в таблицу B, MV C срабатывает на B
Урок 2: MV + AggregatingMergeTree — канонический паттерн
- AggregateFunction(func, type) — бинарный столбец для промежуточного состояния
- -State комбинатор при INSERT: sumState(), uniqState(), avgState()
- -Merge комбинатор при SELECT: sumMerge(), uniqMerge(), avgMerge()
- SimpleAggregateFunction для простых функций (sum, min, max, any) — компактнее
- MV + AggregatingMergeTree — стандартный паттерн для pre-aggregation
Урок 3: Подводные камни MV
- Backfilling: MV не видит данные до своего создания — ручной INSERT INTO target SELECT FROM source
- ALTER TABLE (ADD/DROP COLUMN) не вызывает MV — схема MV и source расходятся
- Distributed MV: MV на Distributed таблице выполняется на каждом шарде
- Engine mismatch: target-таблица должна использовать правильный engine (не MergeTree для агрегатов)
- Дедупликация: insert_deduplicate работает для target-таблицы, но может неожиданно отбрасывать строки
Урок 4: Проекции — автоматическая оптимизация
- PROJECTION определяется внутри CREATE TABLE как часть DDL
- ClickHouse автоматически переключает запрос на проекцию, если она читает меньше гранул
- Проекция хранит дополнительную копию данных в каждом part — удвоение storage
- GROUP BY в проекции создаёт неявный AggregatingMergeTree
- Ограничение: проекции не работают с SELECT … FINAL
Урок 5: Refreshable MV — периодическое обновление
- Refreshable MV не имеет триггера на INSERT — это периодический полный пересчёт
- REFRESH EVERY: фиксированный интервал от полуночи UTC
- REFRESH AFTER: относительный интервал от завершения предыдущего пересчёта
- DEPENDS ON: каскадное обновление (B ждёт завершения A)
- APPEND: добавление строк без удаления. По умолчанию — полная замена (REPLACE)
- Полная свобода SQL: JOINs, UNION, table functions, url()
Урок 6: Фреймворк выбора
- Real-time агрегация — инкрементальная MV
- Альтернативная сортировка — проекция
- Периодические отчёты с JOINs — refreshable MV
- Сложные ETL с CI/CD — dbt
- Подходы комбинируются на одной source-таблице
Краткая справка по синтаксису
Инкрементальная MV:
CREATE MATERIALIZED VIEW mv_name
TO target_table AS
SELECT ... FROM source_table;
MV + AggregatingMergeTree:
CREATE TABLE target (
key_col Date,
agg_col AggregateFunction(sum, UInt64)
) ENGINE = AggregatingMergeTree()
ORDER BY key_col;
CREATE MATERIALIZED VIEW mv_name
TO target AS
SELECT key_col, sumState(val) AS agg_col
FROM source GROUP BY key_col;
-- Чтение:
SELECT key_col, sumMerge(agg_col) FROM target GROUP BY key_col;
Проекция:
CREATE TABLE events (
...
PROJECTION by_user (
SELECT * ORDER BY user_id
)
) ENGINE = MergeTree() ORDER BY (date, event_type);
-- Backfill существующих данных:
ALTER TABLE events MATERIALIZE PROJECTION by_user;
Refreshable MV:
CREATE MATERIALIZED VIEW mv_name
REFRESH EVERY 1 HOUR
DEPENDS ON upstream_mv
TO target_table AS
SELECT ... FROM source JOIN dim ON ...;
Что дальше
Модуль 05 переходит к skip indexes и dictionaries — ещё два способа ускорить запросы без изменения данных. Skip indexes уменьшают объём сканирования, а dictionaries заменяют тяжёлые JOINs на быстрый lookup.