Learning Platform
Глоссарий Troubleshooting
Урок 13.04 · 25 мин
Продвинутый
query cacheuse_query_cachedeterministic queriesQueryCacheHitsQueryCacheMisses

Query Result Cache

Некоторые тяжёлые аналитические запросы выполняются снова и снова с теми же результатами: дашборды, регулярные отчёты, повторяющиеся агрегации. Для таких сценариев ClickHouse предоставляет Query Result Cache — server-side кеширование результатов SELECT-запросов. При повторном запросе с идентичным SQL кешированный результат возвращается без обращения к данным.


Поток данных с Query Result Cache

Query Result Cache: поток данных
SELECT ... SETTINGS use_query_cache = 1SELECT запрос с use_query_cache=1: клиент отправляет запрос. ClickHouse вычисляет хеш нормализованного SQL для поиска в кеше.
проверка кеша
Cache lookup (HIT / MISS)Cache lookup: ClickHouse ищет результат по хешу запроса. Cache HIT — результат возвращается немедленно без чтения данных (QueryCacheHits++). Cache MISS — запрос выполняется обычным образом, результат сохраняется в кеше (QueryCacheMisses++).
cache miss: выполнить + сохранить
system.query_cache (in-memory)system.query_cache: in-memory таблица с кешированными результатами. Поля: query, result_size, stale, shared, expires_at, key_hash. Данные хранятся в RAM. При INSERT в исходную таблицу соответствующие записи инвалидируются автоматически.

Включение Query Result Cache

Кеш включается на уровне конкретного запроса через SETTINGS:

-- Включить кеш для конкретного запроса
SELECT
    toStartOfDay(event_time) AS day,
    count() AS events
FROM user_events
WHERE event_time >= now() - INTERVAL 7 DAY
GROUP BY day
ORDER BY day
SETTINGS use_query_cache = 1;

Или на уровне сессии для всех последующих запросов:

-- Включить кеш для всей сессии
SET use_query_cache = 1;

-- Теперь все SELECT запросы в этой сессии кешируются
SELECT count() FROM large_table;
SELECT sum(revenue) FROM orders WHERE date = '2026-01-01';

Инспекция кеша

-- Посмотреть текущее содержимое кеша
SELECT
    query,
    formatReadableSize(result_size) AS size,
    stale,
    shared,
    expires_at,
    key_hash
FROM system.query_cache
ORDER BY expires_at DESC;

Поле stale указывает, что данные могли устареть (INSERT в исходную таблицу). expires_at — когда запись будет автоматически удалена из кеша.


Мониторинг hits/misses

-- Проверить эффективность кеша
SELECT event, value
FROM system.events
WHERE event LIKE 'QueryCache%';

-- Ожидаемый вывод:
-- QueryCacheHits     1523
-- QueryCacheMisses   47

Высокое соотношение QueryCacheHits / QueryCacheMisses указывает на эффективное использование кеша. Если Misses не уменьшаются — возможно, запросы используют non-deterministic функции (см. предупреждение ниже).


Ограничение: non-deterministic функции

WARNING

Query result cache не работает для запросов с non-deterministic функциями: now(), today(), rand(), currentUser(), generateUUIDv4(). ClickHouse не кеширует такие запросы — результат может меняться между вызовами. При использовании SELECT now(), count() FROM t SETTINGS use_query_cache=1 вы увидите, что QueryCacheMisses продолжает расти при каждом выполнении.

Обходное решение: вычисляйте временные границы вне запроса или используйте явные значения:

-- Не кешируется: now() non-deterministic
SELECT count() FROM events
WHERE event_time >= now() - INTERVAL 1 HOUR
SETTINGS use_query_cache = 1;

-- Кешируется: явные значения
SELECT count() FROM events
WHERE event_time >= '2026-04-17 10:00:00'
  AND event_time <  '2026-04-17 11:00:00'
SETTINGS use_query_cache = 1;

Настройки Query Result Cache

НастройкаПо умолчаниюОписание
query_cache_max_size_in_bytes1 GiBМаксимальный размер всего кеша в памяти
query_cache_max_entries1024Максимальное число кешированных результатов
query_cache_min_query_duration0Минимальная длительность запроса для кеширования (мс)
query_cache_min_query_runs0Минимальное число выполнений перед кешированием
query_cache_ttl60TTL кешированного результата (секунды)

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

-- Кешировать только запросы дольше 500 мс, TTL 5 минут
SELECT count(), sum(revenue)
FROM orders
WHERE date >= today() - 30
SETTINGS
    use_query_cache = 1,
    query_cache_min_query_duration = 500,
    query_cache_ttl = 300;

Инвалидация кеша

Кеш автоматически инвалидируется при INSERT в исходную таблицу:

-- Кешируем результат
SELECT count() FROM orders SETTINGS use_query_cache = 1;

-- После INSERT в orders кеш автоматически помечается как stale
INSERT INTO orders VALUES (...);

-- Следующий запрос увидит stale=1 и выполнится заново
SELECT count() FROM orders SETTINGS use_query_cache = 1;

Для ручной очистки всего кеша:

SYSTEM DROP QUERY CACHE;

Когда использовать Query Result Cache

Подходит для:

  • Дашборды с высокой read-нагрузкой и одинаковыми запросами
  • Повторяющиеся аналитические отчёты (ежечасные, ежедневные)
  • Read-heavy workloads с редкими обновлениями данных

Не подходит для:

  • Сценарии с требованием data freshness (real-time мониторинг)
  • Таблицы с высокочастотными INSERT (кеш будет постоянно инвалидироваться)
  • Запросы с non-deterministic функциями (now(), rand())

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

  1. SETTINGS use_query_cache = 1 включает server-side кеширование для конкретного запроса или сессии. Повторный идентичный запрос возвращается без чтения данных.
  2. Non-deterministic функции (now(), rand(), currentUser()) блокируют кеширование — QueryCacheMisses будет расти при каждом выполнении.
  3. SELECT event, value FROM system.events WHERE event LIKE 'QueryCache%' показывает hits/misses — основная метрика эффективности кеша.
  4. Кеш автоматически инвалидируется при INSERT в исходную таблицу. SYSTEM DROP QUERY CACHE — ручная очистка.
  5. query_cache_min_query_duration позволяет кешировать только тяжёлые запросы, избегая overhead для быстрых.
PostgreSQL cost model: plan cache и execution plan invalidation Caching patterns в data pipelines: result cache, materialization и TTL

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

Результат: 0 из 0
Прикладной
Вопрос 1 из 3. Запрос `SELECT now(), count() FROM events SETTINGS use_query_cache=1` выполняется повторно каждую минуту. QueryCacheMisses продолжает расти. Почему кеш не работает?

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

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

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

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