Learning Platform
Глоссарий Troubleshooting
Урок 04.07 · 20 мин
Средний
MemoryLogNullSetJoinBuffer

Не-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 (микробатчинг на стороне сервера).

WARNING

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

Не-MergeTree движки: когда какой
Use CaseБизнес-сценарий или технический use case
EngineРекомендуемый не-MergeTree движок
ПочемуПочему именно этот движок решает задачу
Staging area для ETLETL pipeline: промежуточные результаты, которые не нужно хранить после завершения пайплайна
Memory / LogMemory (если помещается в RAM) или Log (если нужна persistence на диске). Оба варианта без overhead MergeTree.
Минимальный overheadМинимальный overhead: нет индексов, нет merges. Memory -- максимальная скорость. Log -- persistence без сложности MergeTree.
Lookup-таблица для JOINDimension table для обогащения фактов: user_id -> name, product_id -> category. Lookup по ключу.
Join + joinGet()Join engine с joinGet() функцией. Данные в RAM, lookup за O(1). Подходит для таблиц до 1M записей.
O(1) lookup по ключуjoinGet() -- точечный lookup без полного JOIN. Быстрее, чем JOIN с MergeTree-таблицей для малых dimensions.
Blacklist/whitelist filterBlacklist/whitelist: набор IP-адресов, user_id, domain-ов для фильтрации WHERE ... IN
SetSet engine. Оптимизирован для IN clause. Данные персистентны (RAM + disk). Подходит для наборов до нескольких миллионов записей.
O(1) проверка INWHERE column IN table -- O(1) проверка вхождения. Быстрее, чем подзапрос или JOIN для фильтрации по набору значений.
MV trigger без хранения sourceMaterialized View trigger: INSERT должен вызвать MV, но исходные данные хранить не нужно
NullNull engine. Отбрасывает все данные, но MV на INSERT срабатывает. Zero storage cost для source.
Zero storage, MV работаетMV триггерится на INSERT в source таблицу. Null engine -- 'фантомный' source: данные не хранятся, но MV получает каждую строку.
Temp table в сессииВременная таблица в рамках одной сессии: промежуточные вычисления, тестовые данные
MemoryMemory engine. Максимальная скорость, данные теряются при рестарте. Идеально для одноразовых вычислений.
Максимальная скорость, нет persistenceНет disk I/O, нет persistence. Создание и уничтожение таблицы мгновенное. Подходит для ad-hoc анализа и тестов.

Ключевые выводы

  1. Memory — RAM-only, нет persistence. Идеален для temp tables, промежуточных результатов и small lookups (до 100K строк).
  2. Log family (Log, TinyLog, StripeLog) — append-only disk storage без индексов. Для staging и маленьких dimension tables.
  3. Null — отбрасывает данные, но триггерит Materialized View. Паттерн: Null source -> MV -> AggregatingMergeTree target.
  4. Set — precomputed set для WHERE ... IN. Blacklists, whitelists, filter sets.
  5. Join — precomputed right-side для JOIN. joinGet() для O(1) point lookups по dimension tables.
  6. Buffer deprecated — используйте async_insert=1 для буферизации мелких INSERT. Серверная настройка, не требующая смены движка.

Проверьте понимание

Результат: 0 из 0
Прикладной
Вопрос 1 из 5. Инженер хочет создать Materialized View, которая обрабатывает каждый INSERT, но исходные данные хранить не нужно. Какой движок для source-таблицы?

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 7