Перейти к содержанию
Learning Platform
Глоссарий Troubleshooting
Урок 13.06 · 25 мин
Продвинутый
mark cacheuncompressed cachecompiled expression cacheJITmark_cache_sizeuncompressed_cache_sizecompiled_expression_cache_sizecompile_expressions

Mark, Uncompressed и Compiled Expression Cache

ClickHouse использует три независимых уровня внутреннего кеширования, каждый из которых оптимизирует разные аспекты выполнения запросов. Понимание того, что именно кешируется на каждом уровне, позволяет правильно настроить сервер для конкретного типа нагрузки — от дашбордов до уникальных аналитических запросов.


Три уровня кеширования

Три уровня кеширования ClickHouse
Mark CacheMark cache: кеширует метки гранул из .mrk2 файлов. Настройка: mark_cache_size (по умолчанию 5 GB). Метрика: system.asynchronous_metrics WHERE metric LIKE 'MarkCache%'. Ускоряет начальный поиск гранул для каждого запроса — без кеша каждый запрос читает .mrk2 с диска.
Uncompressed CacheUncompressed cache: кеширует разжатые блоки данных из .bin файлов. Настройка: uncompressed_cache_size (по умолчанию 0 — выключен). Полезен только для повторных запросов к одним и тем же гранулам. Не эффективен для уникальных полных сканов — overhead без пользы.
Compiled Expression CacheCompiled expression cache: кеширует JIT-скомпилированные выражения. Настройка: compiled_expression_cache_size. Включается через compile_expressions=1. Ускоряет повторные вычисления одних и тех же WHERE/GROUP BY выражений. Не все выражения поддаются JIT-компиляции.

Mark cache: кеш меток гранул

Каждая колонка в MergeTree-таблице хранит метки (marks) в файлах .mrk2. Метка указывает смещение в .bin файле для каждой гранулы данных. При выполнении запроса ClickHouse читает метки, чтобы найти нужные гранулы.

Mark cache хранит эти метки в памяти, устраняя чтение .mrk2 файлов с диска при каждом запросе.

-- Мониторинг mark cache
SELECT
    metric,
    formatReadableSize(value) AS readable_value
FROM system.asynchronous_metrics
WHERE metric LIKE 'MarkCache%';

-- Ожидаемые метрики:
-- MarkCacheBytes    2147483648   (2.00 GiB)
-- MarkCacheFiles    14523        (количество файлов в кеше)

Настройка размера mark cache (в config.xml или через ALTER SYSTEM):

-- В config.xml:
-- <mark_cache_size>5368709120</mark_cache_size>  -- 5 GB (по умолчанию)

Mark cache особенно важен для cold start: первые запросы после перезапуска сервера читают .mrk2 файлы с диска и заполняют кеш. Последующие запросы к тем же таблицам работают быстрее.


Uncompressed cache: кеш разжатых блоков

При чтении данных ClickHouse читает сжатые блоки из .bin файлов и разжимает их. Uncompressed cache хранит уже разжатые блоки в памяти, избегая повторного чтения и разжатия.

-- Мониторинг uncompressed cache
SELECT
    metric,
    formatReadableSize(value) AS readable_value
FROM system.asynchronous_metrics
WHERE metric LIKE '%UncompressedCache%';

По умолчанию uncompressed_cache_size = 0 — кеш отключён.

WARNING

Uncompressed cache эффективен только для повторных запросов к одним и тем же гранулам. Для уникальных полных сканов больших объёмов — overhead без пользы: кеш заполняется данными, которые не будут запрошены повторно, вытесняя полезные записи. Типичный сценарий где он помогает: дашборды с одинаковыми фильтрами, повторяющиеся отчёты по одним и тем же временным периодам.

Пример конфигурации для дашборд-нагрузки:

-- В config.xml (включить uncompressed cache, 4 GB):
-- <uncompressed_cache_size>4294967296</uncompressed_cache_size>

Compiled expression cache: JIT-кеш выражений

ClickHouse может JIT-компилировать выражения из WHERE, GROUP BY, HAVING в нативный машинный код с SIMD-оптимизациями. Compiled expression cache хранит уже скомпилированные выражения и переиспользует их при повторных запросах с одинаковой структурой.

Для включения JIT-компиляции:

-- Включить JIT-компиляцию для текущей сессии
SET compile_expressions = 1;

-- Запросы с повторяющимися выражениями выполнятся быстрее
SELECT
    user_id,
    sum(revenue * discount_factor + shipping_cost) AS total
FROM orders
GROUP BY user_id;

Мониторинг compiled expression cache:

-- Проверить размер compiled expression cache
SELECT
    metric,
    formatReadableSize(value) AS readable_value
FROM system.asynchronous_metrics
WHERE metric LIKE '%CompiledExpression%';
WARNING

JIT-компиляция применяется не ко всем выражениям. ClickHouse компилирует только выражения, которые можно безопасно оптимизировать через LLVM. Сложные условия с подзапросами, внешними функциями или нетривиальными типами данных могут не компилироваться. Не рассчитывайте, что compile_expressions=1 ускорит все запросы.

JIT-расширение в ClickHouse 26.1

В исторических версиях JIT охватывал ограниченный набор путей: sum, count, min, max, avg, avgWeighted для числовых типов; expressions WHERE/GROUP BY с базовыми операциями; адаптер Nullable обрабатывался через wrapper-цикл, что добавляло indirection. В ClickHouse 26.1 JIT-инфраструктура была существенно расширена.

Что добавлено в 26.1:

  • JIT для Nullable-агрегатов без wrapper-overhead. Раньше Nullable-колонки требовали внешнего цикла-обёртки, проверявшего null-битмаску перед вызовом native-кода. В 26.1 null-проверка инлайнится в скомпилированный код через LLVM, и сам цикл целиком становится JIT-блоком.
  • JIT для Decimal128 / Decimal256. До 26.1 JIT покрывал только Decimal64 в ограниченных операциях. 26.1 добавил полные арифметические pipeline для Decimal128/256, включая sum, avg, min, max.
  • Расширенный набор aggregate functions. Помимо классической шестёрки, JIT теперь покрывает groupBitAnd, groupBitOr, groupBitXor, any, anyLast, argMin, argMax (для базовых числовых ключей).
  • JIT для -If combinator. Conditional агрегации вида sumIf, countIf, avgIf теперь компилируются в один проход без отдельного branch-предсказания.
-- Включение всего стека JIT в 26.1
SET compile_expressions = 1;
SET compile_aggregate_expressions = 1;
SET compile_sort_description = 1;
SET min_count_to_compile_expression = 3;
SET min_count_to_compile_aggregate_expression = 3;

-- Запрос, который раньше не получал JIT (Nullable Decimal128 + If-combinator)
SELECT
    user_id,
    sumIf(revenue, status = 'paid') AS paid_revenue,  -- Nullable Decimal128
    avgIf(processing_ms, region = 'EU') AS eu_latency
FROM transactions
GROUP BY user_id;

Performance numbers в публичных бенчмарках 26.1 показывают ускорение до 1.5-2x на Nullable-aggregations и до 1.3x на Decimal128 sum/avg по сравнению с тем же запросом на 25.x. Эффект особенно заметен на запросах с большим GROUP BY на Nullable-колонках, где раньше доминировал overhead wrapper-цикла.

INFO

Чтобы проверить, какие части плана действительно скомпилированы JIT, используйте EXPLAIN actions = 1. В выводе вы увидите атрибут compiled у соответствующих ExpressionStep / AggregatingStep — это подтверждение, что LLVM-pipeline сгенерировал native-код, а не fallback-интерпретатор.


Мониторинг всех кешей

-- Все метрики кешей в одном запросе
SELECT
    metric,
    formatReadableSize(value) AS readable_value
FROM system.asynchronous_metrics
WHERE metric LIKE '%Cache%'
ORDER BY metric;

Сводная таблица трёх кешей

КешНастройкаПо умолчаниюЧто кешируетКогда полезен
Mark cachemark_cache_size5 GBМетки гранул (.mrk2)Всегда — ускоряет поиск гранул для каждого запроса
Uncompressed cacheuncompressed_cache_size0 (выключен)Разжатые блоки (.bin)Повторные запросы к одним гранулам, дашборды
Compiled expression cachecompiled_expression_cache_sizeАвтоJIT-скомпилированные выраженияПовторяющиеся запросы с одинаковыми выражениями

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

  1. Mark cache (по умолчанию 5 GB) кеширует метки гранул и полезен для всех запросов — его рекомендуется оставлять включённым и достаточно большим.
  2. Uncompressed cache по умолчанию отключён. Включать только для рабочих нагрузок с повторяющимися запросами к одним и тем же гранулам (дашборды, отчёты). Для уникальных полных сканов — overhead без пользы.
  3. Compiled expression cache с compile_expressions=1 ускоряет повторяющиеся вычисления через JIT. В 26.1 JIT-покрытие расширено до Nullable-агрегатов без wrapper-overhead, Decimal128/256, -If-combinator и aggregate functions argMin/argMax/groupBit*. Включайте также compile_aggregate_expressions = 1 и проверяйте через EXPLAIN actions = 1 атрибут compiled на нужных steps.
  4. system.asynchronous_metrics WHERE metric LIKE '%Cache%' — основной инструмент мониторинга всех трёх кешей.
  5. formatReadableSize() переводит байтовые значения в читаемый формат (MiB, GiB) для удобного анализа.
Buffer Pool в PostgreSQL: shared_buffers, page cache и eviction policy Кодировки данных: dictionary encoding и compressed execution

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

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

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

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