Passer au contenu principal
Le cache des requêtes permet de calculer les requêtes SELECT une seule fois, puis de servir les exécutions suivantes de la même requête directement depuis le cache. Selon le type de requêtes, cela peut réduire considérablement la latence et la consommation de ressources du serveur ClickHouse.

Contexte, conception et limites

Les caches de requêtes peuvent généralement être considérés comme cohérents ou incohérents sur le plan transactionnel.
  • Dans les caches transactionnellement cohérents, la base de données invalide (écarte) les résultats de requête mis en cache si le résultat de la requête SELECT change ou est susceptible de changer. Dans ClickHouse, les opérations qui modifient les données incluent les insertions/mises à jour/suppressions dans les tables ou les merges avec collapsing. La mise en cache transactionnellement cohérente est particulièrement adaptée aux bases de données OLTP, par exemple MySQL (qui a supprimé le cache de requêtes après la version 8.0) et Oracle.
  • Dans les caches transactionnellement incohérents, de légères imprécisions dans les résultats de requête sont acceptées, en partant du principe que toutes les entrées de cache se voient attribuer une période de validité après laquelle elles expirent (par ex. 1 minute) et que les données sous-jacentes évoluent peu pendant cette période. Cette approche est globalement plus adaptée aux bases de données OLAP. Comme exemple où une mise en cache transactionnellement incohérente est suffisante, prenons un rapport de ventes horaire dans un outil de reporting consulté simultanément par plusieurs utilisateurs. Les données de ventes changent généralement assez lentement pour que la base de données n’ait besoin de calculer le rapport qu’une seule fois (ce que représente la première requête SELECT). Les requêtes suivantes peuvent ensuite être renvoyées directement depuis le cache de requêtes. Dans cet exemple, une période de validité raisonnable pourrait être de 30 min.
La mise en cache transactionnellement incohérente est traditionnellement fournie par des outils clients ou des paquets proxy (par ex. chproxy) qui interagissent avec la base de données. Par conséquent, la même logique de mise en cache et la même configuration sont souvent dupliquées. Avec le cache de requêtes de ClickHouse, la logique de mise en cache est déplacée côté serveur. Cela réduit les efforts de maintenance et évite les redondances.

Paramètres de configuration et utilisation

Dans ClickHouse Cloud, vous devez utiliser les paramètres au niveau de la requête pour modifier les paramètres du cache de requêtes. La modification des paramètres au niveau de la configuration n’est actuellement pas prise en charge.
clickhouse-local n’exécute qu’une seule requête à la fois. Comme la mise en cache des résultats de requête n’a pas de sens dans ce cas, le cache des résultats de requête est désactivé dans clickhouse-local.
Le paramètre use_query_cache permet de contrôler si une requête donnée ou toutes les requêtes de la session en cours doivent utiliser le cache de requêtes. Par exemple, la première exécution de la requête
SELECT some_expensive_calculation(column_1, column_2)
FROM table
SETTINGS use_query_cache = true;
stockera le résultat de la requête dans le cache de requêtes. Les exécutions suivantes de la même requête (également avec le paramètre use_query_cache = true) liront le résultat calculé depuis le cache et le renverront immédiatement.
Le paramètre use_query_cache et tous les autres paramètres liés au cache de requêtes ne prennent effet que pour des instructions SELECT autonomes. En particulier, les résultats des SELECT sur des vues créées par CREATE VIEW AS SELECT [...] SETTINGS use_query_cache = true ne sont pas mis en cache, sauf si l’instruction SELECT est exécutée avec SETTINGS use_query_cache = true.
La manière dont le cache est utilisé peut être configurée plus finement à l’aide des paramètres enable_writes_to_query_cache et enable_reads_from_query_cache (tous deux à true par défaut). Le premier paramètre contrôle si les résultats des requêtes sont stockés dans le cache, tandis que le second détermine si la base de données doit tenter de récupérer les résultats des requêtes depuis le cache. Par exemple, la requête suivante n’utilisera le cache que passivement, c’est-à-dire qu’elle tentera d’y lire sans y stocker son résultat :
SELECT some_expensive_calculation(column_1, column_2)
FROM table
SETTINGS use_query_cache = true, enable_writes_to_query_cache = false;
Pour un contrôle maximal, il est généralement recommandé de n’utiliser les paramètres use_query_cache, enable_writes_to_query_cache et enable_reads_from_query_cache qu’avec des requêtes spécifiques. Il est également possible d’activer la mise en cache au niveau de l’utilisateur ou du profil (par ex. via SET use_query_cache = true), mais il faut garder à l’esprit que toutes les requêtes SELECT peuvent alors renvoyer des résultats en cache. Le cache de requêtes peut être vidé à l’aide de l’instruction SYSTEM CLEAR QUERY CACHE. Le contenu du cache de requêtes est affiché dans la table système system.query_cache. Le nombre de succès et d’échecs du cache de requêtes depuis le démarrage de la base de données est affiché sous forme d’événements “QueryCacheHits” et “QueryCacheMisses” dans la table système system.events. Ces deux compteurs ne sont mis à jour que pour les requêtes SELECT exécutées avec le paramètre use_query_cache = true ; les autres requêtes n’affectent pas “QueryCacheMisses”. Le champ query_cache_usage dans la table système system.query_log indique, pour chaque requête exécutée, si le résultat de la requête a été écrit dans le cache de requêtes ou lu depuis celui-ci. Les métriques QueryCacheEntries et QueryCacheBytes dans la table système system.metrics indiquent combien d’entrées / d’octets le cache de requêtes contient actuellement. Le cache de requêtes existe en un seul exemplaire par processus serveur ClickHouse. Cependant, les résultats en cache ne sont, par défaut, pas partagés entre les utilisateurs. Cela peut être modifié (voir ci-dessous), mais ce n’est pas recommandé pour des raisons de sécurité. Les résultats de requête sont référencés dans le cache de requêtes par l’Abstract Syntax Tree (AST) de la requête. Cela signifie que la mise en cache est insensible à la casse ; par exemple, SELECT 1 et select 1 sont traités comme une seule et même requête. Pour rendre la correspondance plus naturelle, tous les paramètres au niveau de la requête liés au cache de requêtes et au formatage de sortie) sont supprimés de l’AST. Si la requête a été interrompue en raison d’une exception ou annulée par l’utilisateur, aucune entrée n’est écrite dans le cache de requêtes. La taille du cache de requêtes en octets, le nombre maximal d’entrées de cache et la taille maximale des entrées individuelles (en octets et en enregistrements) peuvent être configurés à l’aide de différentes options de configuration du serveur.
<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>
Il est également possible de limiter l’utilisation du cache pour chaque utilisateur à l’aide des profils de paramètres et des contraintes sur les paramètres. Plus précisément, vous pouvez restreindre la quantité maximale de mémoire (en octets) qu’un utilisateur peut allouer au cache des requêtes ainsi que le nombre maximal de résultats de requête stockés. Pour cela, définissez d’abord les paramètres query_cache_max_size_in_bytes et query_cache_max_entries dans un profil utilisateur dans users.xml, puis rendez les deux paramètres readonly:
<profiles>
    <default>
        <!-- The maximum cache size in bytes for user/profile 'default' -->
        <query_cache_max_size_in_bytes>10000</query_cache_max_size_in_bytes>
        <!-- The maximum number of SELECT query results stored in the cache for user/profile 'default' -->
        <query_cache_max_entries>100</query_cache_max_entries>
        <!-- Make both settings read-only so the user cannot change them -->
        <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>
Pour définir la durée minimale pendant laquelle une requête doit s’exécuter pour que son résultat puisse être mis en cache, vous pouvez utiliser le paramètre query_cache_min_query_duration. Par exemple, le résultat de la requête
SELECT some_expensive_calculation(column_1, column_2)
FROM table
SETTINGS use_query_cache = true, query_cache_min_query_duration = 5000;
n’est mis en cache que si la requête s’exécute pendant plus de 5 secondes. Il est également possible de spécifier à quelle fréquence une requête doit s’exécuter pour que son résultat soit mis en cache — pour cela, utilisez le paramètre query_cache_min_query_runs. Les entrées du cache de requêtes deviennent obsolètes après une certaine période (time-to-live). Par défaut, cette période est de 60 secondes, mais une autre valeur peut être spécifiée au niveau de la session, du profil ou de la requête à l’aide du paramètre query_cache_ttl. Le cache de requêtes évince les entrées de manière “paresseuse”, c.-à-d. que lorsqu’une entrée devient obsolète, elle n’est pas immédiatement supprimée du cache. Au lieu de cela, lorsqu’une nouvelle entrée doit être insérée dans le cache de requêtes, la base de données vérifie si le cache dispose de suffisamment d’espace libre pour la nouvelle entrée. Si ce n’est pas le cas, la base de données essaie de supprimer toutes les entrées obsolètes. Si le cache ne dispose toujours pas d’assez d’espace libre, la nouvelle entrée n’est pas insérée. Si la requête est exécutée via HTTP, alors ClickHouse définit les en-têtes Age et Expires avec l’âge (en secondes) et l’horodatage d’expiration de l’entrée mise en cache. Les entrées du cache de requêtes sont compressées par défaut. Cela réduit la consommation globale de mémoire au prix d’écritures plus lentes dans / de lectures plus lentes depuis le cache de requêtes. Pour désactiver la compression, utilisez le paramètre query_cache_compress_entries. Il est parfois utile de conserver en cache plusieurs résultats pour la même requête. Cela peut être réalisé à l’aide du paramètre query_cache_tag, qui agit comme une étiquette (ou un espace de noms) pour les entrées du cache de requêtes. Le cache de requêtes considère comme différents les résultats de la même requête avec des tags différents. Exemple de création de trois entrées différentes dans le cache de requêtes pour la même requête :
SELECT 1 SETTINGS use_query_cache = true; -- query_cache_tag is implicitly '' (empty string)
SELECT 1 SETTINGS use_query_cache = true, query_cache_tag = 'tag 1';
SELECT 1 SETTINGS use_query_cache = true, query_cache_tag = 'tag 2';
Pour supprimer uniquement les entrées portant le tag tag du cache de requêtes, vous pouvez utiliser l’instruction SYSTEM CLEAR QUERY CACHE TAG 'tag'.

Mise en cache des sous-requêtes

Par défaut, use_query_cache dans la requête externe n’est pas appliqué aux sous-requêtes. Cela signifie que chaque sous-requête doit activer explicitement la mise en cache :
SELECT *
FROM (SELECT number FROM system.numbers LIMIT 1000 SETTINGS use_query_cache = true)
WHERE number > 500;
Dans cet exemple, seul le résultat de la sous-requête interne est mis en cache. La requête externe ne l’est pas. Pour activer en une seule fois la mise en cache de toutes les sous-requêtes, utilisez le paramètre query_cache_for_subqueries :
SELECT *
FROM (SELECT number FROM system.numbers LIMIT 1000)
WHERE number > 500
SETTINGS use_query_cache = true, query_cache_for_subqueries = true;
Pour désactiver explicitement la mise en cache pour une sous-requête spécifique lorsque la propagation globale est activée, définissez use_query_cache = false pour cette sous-requête :
SELECT *
FROM (SELECT number FROM system.numbers LIMIT 1000 SETTINGS use_query_cache = false)
WHERE number > 500
SETTINGS use_query_cache = true, query_cache_for_subqueries = true;
Les entrées du cache de sous-requête sont visibles dans system.query_cache avec is_subquery = 1. Le paramètre query_cache_ttl s’applique également aux entrées du cache de sous-requête et peut être défini pour chaque sous-requête. ClickHouse lit les données des tables en blocs de max_block_size lignes. En raison du filtrage, de l’agrégation, etc., les blocs de résultat sont généralement bien plus petits que ‘max_block_size’, mais dans certains cas, ils peuvent aussi être bien plus grands. Le paramètre query_cache_squash_partial_results (activé par défaut) détermine si les blocs de résultat sont fusionnés (s’ils sont très petits) ou divisés (s’ils sont volumineux) en blocs de taille ‘max_block_size’ avant leur insertion dans le cache de résultat de la requête. Cela réduit les performances d’écriture dans le cache de requêtes, mais améliore le taux de compression des entrées du cache et offre une granularité de blocs plus naturelle lorsque les résultats des requêtes sont ensuite servis depuis le cache de requêtes. Par conséquent, le cache de requêtes stocke pour chaque requête plusieurs blocs de résultat partiels. Bien que ce comportement constitue un bon choix par défaut, il peut être désactivé à l’aide du paramètre query_cache_squash_partial_results. De plus, les résultats des requêtes contenant des fonctions non déterministes ne sont pas mis en cache par défaut. Ces fonctions incluent Pour forcer malgré tout la mise en cache des résultats des requêtes contenant des fonctions non déterministes, utilisez le paramètre query_cache_nondeterministic_function_handling. Les résultats des requêtes qui impliquent des tables système (par ex. system.processes` ou information_schema.tables) ne sont pas mis en cache par défaut. Pour forcer malgré tout la mise en cache des résultats des requêtes avec des tables système, utilisez le paramètre query_cache_system_table_handling. Enfin, les entrées du cache de requêtes ne sont pas partagées entre les utilisateurs pour des raisons de sécurité. Par exemple, l’utilisateur A ne doit pas pouvoir contourner une ROW POLICY sur une table en exécutant la même requête qu’un autre utilisateur B, pour lequel aucune telle règle n’existe. Cependant, si nécessaire, les entrées du cache peuvent être rendues accessibles à d’autres utilisateurs (c’est-à-dire partagées) en spécifiant le paramètre query_cache_share_between_users.
Dernière modification le 25 juin 2026