Не-MergeTree движки: специальные задачи
MergeTree и его варианты — основа ClickHouse для аналитических workloads. Но ClickHouse предлагает набор специализированных движков для задач, где MergeTree избыточен или неприменим: временные таблицы, lookup-справочники, MV-триггеры, precomputed filter sets.
Memory: RAM-only таблица
Memory хранит данные исключительно в оперативной памяти. При перезапуске сервера все данные теряются.
CREATE TABLE temp_results (
session_id String,
result Float64
) ENGINE = Memory;
Характеристики:
- Максимальная скорость чтения и записи (нет disk I/O)
- Нет persistence — данные теряются при рестарте сервера
- Нет индексов, нет сжатия, нет фоновых процессов
- Подходит для таблиц до 100K строк (ограничение — RAM)
Use cases:
- Промежуточные результаты в многоэтапных ETL-пайплайнах
- Временные таблицы для unit-тестов
- Lookup-таблицы с малым объёмом данных (справочники до 100K записей)
Log family: append-only disk storage
Три движка Log family хранят данные на диске без индексов и фоновых слияний. Данные записываются последовательно (append-only), чтение — полное сканирование.
Log
CREATE TABLE staging_data (
id UInt32,
payload String
) ENGINE = Log;
- Последовательная запись на диск, каждый столбец в отдельном файле
- Нет индексов — каждый SELECT сканирует всю таблицу
- Не поддерживает конкурентные чтение и запись (блокировка при записи)
- Подходит для staging-таблиц, dimension-таблиц среднего размера
TinyLog
CREATE TABLE countries (
code FixedString(2),
name String
) ENGINE = TinyLog;
- Простейший движок: один файл на столбец, нет marks, нет индексов
- Подходит для очень маленьких справочников (до 10K строк)
- Минимальный overhead при создании таблицы
StripeLog
CREATE TABLE wide_staging (
id UInt32,
col1 String,
col2 String,
col3 String
-- ...десятки столбцов
) ENGINE = StripeLog;
- Все столбцы в одном файле (stripe) вместо отдельных файлов
- Лучше TinyLog для таблиц с большим числом столбцов (меньше файловых дескрипторов)
- Подходит для staging-таблиц с широкой схемой
Null: /dev/null для ClickHouse
Null engine отбрасывает все записанные данные. SELECT всегда возвращает пустой результат. Зачем это нужно? Для MV trigger source — Materialized View срабатывает на INSERT, даже если сами данные не сохраняются.
-- Source: данные не хранятся, но MV срабатывает
CREATE TABLE raw_events (
timestamp DateTime,
user_id UInt64,
event_type String,
duration_ms UInt32
) ENGINE = Null;
-- MV обрабатывает каждый INSERT в raw_events
CREATE MATERIALIZED VIEW hourly_stats_mv TO hourly_stats AS
SELECT
toStartOfHour(timestamp) AS hour,
sumState(1) AS event_count,
uniqState(user_id) AS unique_users
FROM raw_events
GROUP BY hour;
-- Target: хранит только агрегаты
CREATE TABLE hourly_stats (
hour DateTime,
event_count AggregateFunction(sum, UInt32),
unique_users AggregateFunction(uniq, UInt64)
) ENGINE = AggregatingMergeTree()
ORDER BY hour;
Паттерн: raw data не нужна для хранения (слишком дорого или нет use case для ad-hoc запросов), но MV должна обрабатывать каждый INSERT. Null engine — “фантомный” source, который триггерит MV без затрат на хранение.
Set: precomputed filter
Set engine хранит уникальный набор значений, оптимизированный для оператора IN. Данные персистентны (RAM + disk).
-- Создание blacklist
CREATE TABLE blocked_ips (ip IPv4) ENGINE = Set;
-- Заполнение
INSERT INTO blocked_ips VALUES ('1.2.3.4'), ('10.0.0.1'), ('192.168.1.100');
-- Использование в WHERE ... IN
SELECT *
FROM access_log
WHERE ip IN blocked_ips;
Характеристики:
- Данные загружаются в RAM при старте сервера
- INSERT добавляет значения в set (нет UPDATE/DELETE)
- Оптимизирован для
WHERE column IN table— lookup за O(1) - Подходит для blacklists, whitelists, filter sets до нескольких миллионов записей
SELECT напрямую из Set-таблицы невозможен — она существует только как операнд для IN.
Join: precomputed right-side для JOIN
Join engine хранит данные, оптимизированные для использования как правая сторона JOIN или через функцию joinGet().
-- Dimension table
CREATE TABLE users_dim (
user_id UInt32,
name String,
department String
) ENGINE = Join(ANY, LEFT, user_id);
-- Заполнение
INSERT INTO users_dim VALUES (1, 'Alice', 'Engineering');
INSERT INTO users_dim VALUES (2, 'Bob', 'Marketing');
-- Использование через joinGet()
SELECT
event_type,
joinGet('users_dim', 'name', user_id) AS user_name,
joinGet('users_dim', 'department', user_id) AS dept
FROM events;
Характеристики:
- Данные загружаются в RAM при старте сервера
joinGet(table, column, key)— точечный lookup по ключу, O(1)- Подходит для небольших dimension-таблиц (до 1M записей), где нужен быстрый lookup
- Параметры
ANY, LEFT, user_idопределяют тип JOIN и ключ соединения
Buffer: deprecated
Buffer engine буферизует INSERT в RAM и периодически flush-ит в целевую таблицу. Это был механизм для оптимизации мелких INSERT (микробатчинг на стороне сервера).
Buffer engine считается устаревшим. Для буферизации мелких INSERT используйте async_insert=1 — серверная настройка, не требующая смены движка.
-- async_insert -- современная замена Buffer engine
SET async_insert = 1;
SET wait_for_async_insert = 1;
-- INSERT-ы автоматически буферизуются и flush-ятся батчами
INSERT INTO events VALUES (...);
async_insert=1 включает серверную буферизацию: мелкие INSERT накапливаются в буфере и flush-ятся батчем по достижении порога (async_insert_max_data_size, async_insert_busy_timeout_ms). Это транспарентно для приложения — не нужно менять DDL или driver code.
Матрица use case -> engine
Ключевые выводы
- Memory — RAM-only, нет persistence. Идеален для temp tables, промежуточных результатов и small lookups (до 100K строк).
- Log family (Log, TinyLog, StripeLog) — append-only disk storage без индексов. Для staging и маленьких dimension tables.
- Null — отбрасывает данные, но триггерит Materialized View. Паттерн: Null source -> MV -> AggregatingMergeTree target.
- Set — precomputed set для
WHERE ... IN. Blacklists, whitelists, filter sets. - Join — precomputed right-side для JOIN.
joinGet()для O(1) point lookups по dimension tables. - Buffer deprecated — используйте
async_insert=1для буферизации мелких INSERT. Серверная настройка, не требующая смены движка.