MergeTree: фундамент хранилища
Все движки семейства MergeTree — ReplacingMergeTree, SummingMergeTree, AggregatingMergeTree, CollapsingMergeTree — наследуют от базового MergeTree. Понять MergeTree — значит понять фундамент, на котором стоит всё хранилище ClickHouse.
Append-only семантика
MergeTree — append-only движок. Каждый INSERT создаёт новый immutable part на диске. Данные никогда не изменяются in-place:
- Нет row-level UPDATE как в PostgreSQL (ALTER TABLE UPDATE создаёт мутацию — тяжёлую фоновую перезапись parts)
- Нет row-level DELETE (lightweight DELETE добавляет маску
_row_exists=0, физическое удаление — при следующем merge) - Нет WAL, нет MVCC, нет row-level locking — immutable parts и есть модель данных
CREATE TABLE events (
event_date Date,
user_id UInt32,
event_type String,
payload String
) ENGINE = MergeTree()
ORDER BY (event_date, user_id);
-- Каждый INSERT создаёт отдельный part на диске
INSERT INTO events VALUES ('2024-01-15', 1, 'click', '{"page":"/home"}');
INSERT INTO events VALUES ('2024-01-15', 2, 'view', '{"page":"/docs"}');
-- Два INSERT = два part на диске (до фонового слияния)
SELECT name, rows, bytes_on_disk
FROM system.parts
WHERE table = 'events' AND active = 1;
Это фундаментальное отличие от PostgreSQL: в PostgreSQL INSERT изменяет heap page in-place, а WAL гарантирует durability. В ClickHouse INSERT — это запись нового файла. Нет конкуренции за страницы, нет блокировок на запись.
Свойства MergeTree из модуля 01
В Module 01 мы разобрали анатомию part и механику слияний. Вот как эти свойства выглядят с точки зрения движка:
- Parts — самодостаточные директории с .bin, .mrk2, primary.idx, checksums.txt
- Merges — фоновый процесс объединения parts. Merge-sort по ORDER BY ключу
- Sparse index — primary.idx в RAM, бинарный поиск по гранулам (не по строкам)
- Granules — логические блоки по 8192 строк (адаптивный index_granularity)
Когда использовать MergeTree
MergeTree — правильный выбор для чистой аналитики, когда данные пишутся один раз и не требуют обновлений:
- Логи — access logs, application logs, error tracking
- Clickstream — page views, clicks, user actions
- Time-series — метрики серверов, IoT-сенсоры, финансовые тики
- Events — audit trail, notification history, CDC events
Общий паттерн: данные неизменяемы по бизнес-логике. Лог-запись не обновляется. Метрика сенсора не редактируется. Событие — факт, который произошёл.
Контраст с PostgreSQL
| Свойство | PostgreSQL | ClickHouse MergeTree |
|---|---|---|
| Модель записи | Heap pages + WAL | Immutable parts (append-only) |
| UPDATE | In-place (HOT, heap rewrite) | ALTER TABLE UPDATE (мутация — фоновая перезапись parts) |
| DELETE | Пометка + VACUUM | Lightweight DELETE (маска) + merge |
| Конкуренция | Row-level locking, MVCC | Нет блокировок — parts immutable |
| Индексирование | B-tree (запись на строку) | Sparse index (запись на 8192 строк) |
| Хранение | Row-oriented (heap) | Column-oriented (.bin per column) |
MergeTree не конкурирует с PostgreSQL — это архитектурно другая модель. PostgreSQL оптимизирован для OLTP (маленькие транзакции, row lookups). MergeTree оптимизирован для OLAP (массовые аналитические запросы, columnar scans).
SQL: минимальный рабочий пример
-- Создание таблицы
CREATE TABLE page_events (
timestamp DateTime,
user_id UInt32,
page String,
duration_ms UInt32
) ENGINE = MergeTree()
ORDER BY (timestamp, user_id);
-- Вставка данных (пакетом -- не по одной строке!)
INSERT INTO page_events VALUES
('2024-01-15 10:00:00', 1, '/home', 250),
('2024-01-15 10:00:01', 2, '/docs', 1200),
('2024-01-15 10:00:02', 1, '/pricing', 800);
-- Аналитический запрос
SELECT
page,
count() AS views,
avg(duration_ms) AS avg_duration
FROM page_events
WHERE timestamp >= '2024-01-15'
GROUP BY page
ORDER BY views DESC;
MergeTree — правильный выбор по умолчанию. Используйте специализированные движки (ReplacingMergeTree, SummingMergeTree и др.) только когда базовый MergeTree не подходит: нужна дедупликация, автоматическая агрегация или collapse-семантика.
Ключевые выводы
- MergeTree — фундамент всех движков семейства. ReplacingMergeTree, SummingMergeTree, AggregatingMergeTree наследуют его поведение.
- Append-only: каждый INSERT создаёт новый immutable part. Нет in-place updates, нет row-level deletes.
- Фоновые слияния объединяют мелкие parts в крупные, уменьшая число файлов и улучшая производительность запросов.
- Sparse index (primary.idx) помещается в RAM и обеспечивает бинарный поиск по гранулам за микросекунды.
- Используйте MergeTree для чистой аналитики: логи, clickstream, time-series, events — данные, которые пишутся один раз и не требуют обновлений.