Passer au contenu principal
ClickHouse traite les requêtes extrêmement rapidement, mais l’exécution d’une requête n’a rien de simple. Essayons de comprendre comment une requête SELECT est exécutée. Pour l’illustrer, ajoutons des données dans une Table dans ClickHouse :
Maintenant que nous avons des données dans ClickHouse, nous voulons exécuter quelques requêtes et comprendre comment elles s’exécutent. L’exécution d’une requête se décompose en de nombreuses étapes. Chaque étape de l’exécution d’une requête peut être analysée et dépannée à l’aide de la requête EXPLAIN correspondante. Ces étapes sont résumées dans le schéma ci-dessous : Voyons maintenant chaque composant à l’œuvre lors de l’exécution d’une requête. Nous allons prendre quelques requêtes, puis les examiner à l’aide de l’instruction EXPLAIN.

Analyseur syntaxique

L’objectif d’un analyseur syntaxique est de transformer le texte de la requête en un AST (arbre syntaxique abstrait). Cette étape peut être visualisée à l’aide de EXPLAIN AST :
Le résultat est un arbre syntaxique abstrait qui peut être visualisé comme ci-dessous : Chaque nœud a des nœuds enfants, et l’ensemble de l’arbre représente la structure globale de votre requête. Il s’agit d’une structure logique qui facilite le traitement d’une requête. Du point de vue de l’utilisateur final (sauf s’il s’intéresse à l’exécution des requêtes), ce n’est pas très utile ; cet outil est principalement destiné aux développeurs.

Analyseur

ClickHouse dispose actuellement de deux architectures pour l’analyseur. Vous pouvez utiliser l’ancienne architecture en définissant : enable_analyzer=0. La nouvelle architecture est activée par défaut. Nous ne décrirons ici que la nouvelle architecture, puisque l’ancienne sera dépréciée lorsque le nouvel analyseur sera officiellement disponible.
La nouvelle architecture devrait nous offrir un meilleur cadre pour améliorer les performances de ClickHouse. Cependant, comme il s’agit d’un composant fondamental du traitement des requêtes, elle peut aussi avoir un impact négatif sur certaines requêtes, et il existe des incompatibilités connues. Vous pouvez revenir à l’ancien analyseur en modifiant le paramètre enable_analyzer au niveau de la requête ou de l’utilisateur.
L’analyseur est une étape importante de l’exécution des requêtes. Il prend un AST et le transforme en arbre de requête. Le principal avantage d’un arbre de requête par rapport à un AST est que de nombreux composants y sont résolus, comme le stockage, par exemple. On sait également dans quelle table lire, les alias sont aussi résolus, et l’arbre connaît les différents types de données utilisés. Grâce à tout cela, l’analyseur peut appliquer des optimisations. Ces optimisations fonctionnent au moyen de « passes ». Chaque passe recherche différents types d’optimisation. Vous pouvez voir toutes les passes ici ; voyons cela en pratique avec notre requête précédente :
Entre les deux exécutions, vous pouvez voir comment les alias et les projections sont résolus.

Planificateur

Le planificateur prend un arbre de requête et en construit un plan de requête. L’arbre de requête indique ce que l’on veut faire avec une requête donnée, tandis que le plan de requête indique comment cela sera fait. Des optimisations supplémentaires sont également effectuées au niveau du plan de requête. Vous pouvez utiliser EXPLAIN PLAN ou EXPLAIN pour afficher le plan de requête (EXPLAIN exécutera EXPLAIN PLAN).
Même si cela nous donne déjà quelques informations, nous pouvons en obtenir davantage. Par exemple, nous voulons peut-être connaître le nom de la colonne pour laquelle nous avons besoin des projections. Vous pouvez ajouter l’en-tête à la requête :
Vous connaissez maintenant les noms des colonnes à créer pour la dernière Projection (minimum_date, maximum_date et percentage), mais vous pouvez aussi souhaiter obtenir le détail de toutes les actions à exécuter. Pour cela, définissez actions=1.
Vous pouvez maintenant voir toutes les entrées, fonctions, alias et types de données utilisés. Vous pouvez également voir ici certaines des optimisations que le planificateur va appliquer.

Pipeline d’exécution de la requête

Un pipeline d’exécution de la requête est généré à partir du plan de requête. Il est très similaire à ce dernier, à la différence qu’il ne s’agit pas d’un arbre, mais d’un graphe. Il montre comment ClickHouse va exécuter une requête et quelles ressources seront utilisées. L’analyse du pipeline d’exécution de la requête est très utile pour identifier le goulot d’étranglement au niveau des entrées/sorties. Reprenons notre requête précédente et examinons l’exécution du pipeline d’exécution de la requête :
Entre parenthèses figurent l’étape du plan de requête et, à côté, le processeur. C’est une information très utile, mais puisqu’il s’agit d’un graphe, il serait utile de le visualiser comme tel. Nous disposons d’un paramètre graph que nous pouvons définir à 1 et indiquer le format de sortie TSV :
Vous pouvez ensuite copier cette sortie et la coller ici, ce qui générera le graph suivant : Un rectangle blanc correspond à un nœud du pipeline, le rectangle gris aux étapes du plan de requête, et le x suivi d’un nombre indique le nombre d’entrées/sorties utilisées. Si vous ne souhaitez pas les afficher sous une forme compacte, vous pouvez toujours ajouter compact=0 :
Pourquoi ClickHouse ne lit-il pas la table avec plusieurs threads ? Essayons d’ajouter davantage de données à notre table :
Relançons maintenant notre requête EXPLAIN :
L’Executor a donc décidé de ne pas paralléliser les opérations, car le volume de données n’était pas suffisant. En ajoutant davantage de lignes, l’Executor a ensuite décidé d’utiliser plusieurs threads, comme le montre le graphe.

Executor

Enfin, la dernière étape de l’exécution de la requête est prise en charge par l’executor. Celui-ci récupère le pipeline d’exécution de la requête et l’exécute. Il existe différents types d’executors, selon que vous effectuez un SELECT, un INSERT ou un INSERT SELECT.
Dernière modification le 2 juillet 2026