Перейти к основному содержанию
Перейти к основному содержанию

Кэш запросов

Кэш запросов позволяет выполнять запросы SELECT только один раз и обслуживать последующие выполнения того же запроса напрямую из кэша. В зависимости от типа запросов это может существенно снизить время отклика и потребление ресурсов сервера ClickHouse.

Предпосылки, дизайн и ограничения

Кэши запросов в общем случае можно рассматривать как транзакционно согласованные или несогласованные.

  • В транзакционно согласованных кэшах база данных аннулирует (отбрасывает) результаты закэшированных запросов, если результат SELECT‑запроса изменяется или потенциально может измениться. В ClickHouse к операциям, которые изменяют данные, относятся вставки/обновления/удаления в/из таблиц или коллапсирующие слияния. Транзакционно согласованное кэширование особенно подходит для OLTP‑СУБД, например MySQL (в которой кэш запросов был удалён, начиная с версии v8.0) и Oracle.
  • В транзакционно несогласованных кэшах допускаются небольшие неточности в результатах запросов при предположении, что всем записям кэша назначен период валидности, после которого они истекают (например, 1 минута), и что исходные данные за это время изменяются незначительно. В целом такой подход больше подходит для OLAP‑СУБД. В качестве примера, где транзакционно несогласованное кэширование является достаточным, рассмотрим почасовой отчёт о продажах в отчётном инструменте, к которому одновременно обращаются несколько пользователей. Данные о продажах, как правило, изменяются достаточно медленно, чтобы базе данных нужно было вычислить отчёт только один раз (представленный первым SELECT‑запросом). Последующие запросы могут обслуживаться напрямую из кэша запросов. В этом примере разумным периодом валидности могут быть 30 минут.

Транзакционно несогласованное кэширование традиционно реализуется клиентскими инструментами или прокси‑решениями (например, chproxy), взаимодействующими с базой данных. В результате одна и та же логика кэширования и конфигурация часто дублируются. С кэшем запросов ClickHouse логика кэширования переносится на сторону сервера. Это снижает затраты на сопровождение и устраняет избыточность.

Параметры конфигурации и использование

Примечание

В ClickHouse Cloud необходимо использовать настройки на уровне запроса для изменения параметров кэша запросов. Редактирование настроек на уровне конфигурации в настоящее время не поддерживается.

Примечание

clickhouse-local выполняет один запрос за раз. Поскольку кэширование результатов запросов здесь не имеет смысла, кэш результатов запросов в clickhouse-local отключён.

Параметр use_query_cache можно использовать для управления тем, должен ли конкретный запрос или все запросы текущего сеанса использовать кэш запросов. Например, первое выполнение запроса

SELECT some_expensive_calculation(column_1, column_2)
FROM table
SETTINGS use_query_cache = true;

будет сохранять результат запроса в кэше запросов. Последующие выполнения того же запроса (также с параметром use_query_cache = true) будут считывать вычисленный результат из кэша и возвращать его немедленно.

Примечание

Установка use_query_cache и всех остальных настроек, связанных с кэшем запросов, влияет только на отдельные самостоятельные команды SELECT. В частности, результаты SELECT к представлениям, созданным с помощью CREATE VIEW AS SELECT [...] SETTINGS use_query_cache = true, не кэшируются, если только команда SELECT не выполняется с SETTINGS use_query_cache = true.

То, как используется кэш, может быть более детально настроено с помощью настроек enable_writes_to_query_cache и enable_reads_from_query_cache (обе настройки имеют значение true по умолчанию). Первая настройка управляет тем, сохраняются ли результаты запросов в кэше, тогда как вторая настройка определяет, должен ли сервер пытаться получать результаты запросов из кэша. Например, следующий запрос будет использовать кэш только пассивно, то есть пытаться читать из него, но не сохранять в него свой результат:

SELECT some_expensive_calculation(column_1, column_2)
FROM table
SETTINGS use_query_cache = true, enable_writes_to_query_cache = false;

Для максимального контроля, как правило, рекомендуется указывать настройки use_query_cache, enable_writes_to_query_cache и enable_reads_from_query_cache только для конкретных запросов. Также возможно включить кэширование на уровне пользователя или профиля (например, через SET use_query_cache = true), но при этом следует иметь в виду, что все запросы SELECT тогда могут возвращать результаты из кэша.

Кэш запросов можно очистить с помощью оператора SYSTEM DROP QUERY CACHE. Содержимое кэша запросов отображается в системной таблице system.query_cache. Количество попаданий и промахов кэша запросов с момента запуска базы данных показывается как события "QueryCacheHits" и "QueryCacheMisses" в системной таблице system.events. Оба счетчика обновляются только для запросов SELECT, которые выполняются с настройкой use_query_cache = true, другие запросы не влияют на "QueryCacheMisses". Поле query_cache_usage в системной таблице system.query_log показывает для каждого выполненного запроса, был ли результат запроса записан в кэш или прочитан из кэша запросов. Метрики QueryCacheEntries и QueryCacheBytes в системной таблице system.metrics показывают, сколько записей/байт в настоящее время содержит кэш запросов.

Кэш запросов создаётся отдельно для каждого процесса сервера ClickHouse. Однако по умолчанию результаты кэша не разделяются между пользователями. Это можно изменить (см. ниже), но делать это не рекомендуется по соображениям безопасности.

Результаты запросов в кэше запросов идентифицируются по Abstract Syntax Tree (AST) их запроса. Это означает, что кэширование не зависит от регистра, например, SELECT 1 и select 1 обрабатываются как один и тот же запрос. Чтобы сделать сопоставление более естественным, все настройки на уровне запроса, относящиеся к кэшу запросов и форматированию вывода, удаляются из AST.

Если запрос был прерван из-за исключения или отмены пользователем, запись в кэш запросов не создаётся.

Размер кэша запросов в байтах, максимальное число записей в кэше и максимальный размер отдельных записей кэша (в байтах и в записях) можно настроить с помощью различных параметров конфигурации сервера.

<query_cache>
    <max_size_in_bytes>1073741824</max_size_in_bytes>
    <max_entries>1024</max_entries>
    <max_entry_size_in_bytes>1048576</max_entry_size_in_bytes>
    <max_entry_size_in_rows>30000000</max_entry_size_in_rows>
</query_cache>

Также можно ограничить использование кэша отдельными пользователями с помощью профилей настроек и ограничений настроек. В частности, вы можете ограничить максимальный объём памяти (в байтах), который пользователь может выделить в кэше запросов, и максимальное количество сохранённых результатов запросов. Для этого сначала задайте значения параметров query_cache_max_size_in_bytes и query_cache_max_entries в профиле пользователя в users.xml, затем сделайте обе настройки доступными только для чтения:

<profiles>
    <default>
        <!-- Максимальный размер кеша в байтах для пользователя/профиля 'default' -->
        <query_cache_max_size_in_bytes>10000</query_cache_max_size_in_bytes>
        <!-- Максимальное количество результатов SELECT-запросов, сохраняемых в кеше для пользователя/профиля 'default' -->
        <query_cache_max_entries>100</query_cache_max_entries>
        <!-- Сделать обе настройки доступными только для чтения, чтобы пользователь не мог их изменять -->
        <constraints>
            <query_cache_max_size_in_bytes>
                <readonly/>
            </query_cache_max_size_in_bytes>
            <query_cache_max_entries>
                <readonly/>
            <query_cache_max_entries>
        </constraints>
    </default>
</profiles>

Чтобы задать минимальную длительность выполнения запроса, начиная с которой его результат может кэшироваться, вы можете использовать настройку query_cache_min_query_duration. Например, результат запроса

SELECT some_expensive_calculation(column_1, column_2)
FROM table
SETTINGS use_query_cache = true, query_cache_min_query_duration = 5000;

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

Записи в кэше запросов становятся устаревшими через определенный период времени (time-to-live). По умолчанию этот период составляет 60 секунд, но другое значение можно задать на уровне сессии, профиля или отдельного запроса, используя настройку query_cache_ttl. Кэш запросов удаляет записи «лениво», то есть когда запись становится устаревшей, она не удаляется из кэша немедленно. Вместо этого, когда в кэш запросов нужно вставить новую запись, база данных проверяет, достаточно ли в кэше свободного места для новой записи. Если это не так, база данных пытается удалить все устаревшие записи. Если в кэше по-прежнему недостаточно свободного места, новая запись не добавляется.

Если запрос выполняется через HTTP, то ClickHouse устанавливает заголовки Age и Expires с временем жизни (в секундах) и временной меткой истечения срока действия закэшированной записи.

Записи в кэше запросов по умолчанию сжимаются. Это уменьшает общее потребление памяти ценой более медленной записи в кэш и чтения из кэша запросов. Чтобы отключить сжатие, используйте настройку query_cache_compress_entries.

Иногда полезно хранить в кэше несколько результатов для одного и того же запроса. Это можно сделать с помощью настройки query_cache_tag, которая выступает в роли метки (или пространства имен) для записей кэша запросов. Кэш запросов считает результаты одного и того же запроса с разными тегами разными.

Пример создания трех разных записей в кэше запросов для одного и того же запроса:

SELECT 1 SETTINGS use_query_cache = true; -- query_cache_tag неявно '' (пустая строка)
SELECT 1 SETTINGS use_query_cache = true, query_cache_tag = 'tag 1';
SELECT 1 SETTINGS use_query_cache = true, query_cache_tag = 'tag 2';

Чтобы удалить из кэша запросов только записи с тегом tag, можно использовать оператор SYSTEM DROP QUERY CACHE TAG 'tag'.

ClickHouse читает данные таблиц блоками по max_block_size строк. Из‑за фильтрации, агрегации и т. д. результирующие блоки обычно значительно меньше, чем max_block_size, но встречаются и случаи, когда они существенно больше. Настройка query_cache_squash_partial_results (включена по умолчанию) управляет тем, будут ли результирующие блоки схлопываться (если они очень маленькие) или разбиваться (если они большие) на блоки размера max_block_size перед вставкой в кэш результатов запросов. Это снижает скорость записи в кэш запросов, но повышает степень сжатия элементов кэша и обеспечивает более естественную гранулярность блоков, когда результаты запросов затем отдаются из кэша.

В результате кэш запросов хранит для каждого запроса несколько (частичных) блоков результата. Хотя такое поведение является разумным вариантом по умолчанию, его можно отключить с помощью настройки query_cache_squash_partial_results.

Кроме того, результаты запросов с недетерминированными функциями по умолчанию не кэшируются. К таким функциям относятся:

Чтобы принудительно кэшировать результаты запросов с недетерминированными функциями, используйте настройку query_cache_nondeterministic_function_handling.

Результаты запросов, которые обращаются к системным таблицам (например, system.processes или information_schema.tables), по умолчанию не кэшируются. Чтобы принудительно кэшировать результаты запросов с системными таблицами, используйте настройку query_cache_system_table_handling.

Наконец, элементы кэша запросов не разделяются между пользователями по соображениям безопасности. Например, пользователь A не должен иметь возможности обойти политику по строкам для таблицы, выполняя тот же запрос, что и другой пользователь B, для которого такая политика не задана. Однако при необходимости элементы кэша могут быть помечены как доступные для других пользователей (то есть общие) с помощью настройки query_cache_share_between_users.