Avant de commencer
nyc_taxi.trips_small_inferred. Pour les exécuter tels quels, créez et chargez la table si ce n’est pas déjà fait :
Configurer l’exemple de jeu de données
Configurer l’exemple de jeu de données
Le fichier Parquet source fait environ 5,8 Go. Son chargement peut prendre plusieurs minutes, selon votre réseau et les ressources disponibles.
Choisir une approche
Si les éléments recueillis ne correspondent à aucune de ces catégories, revenez au plan de requête plutôt que de faire entrer de force la requête dans l’une de ces approches.
Réduire les données lues
- À utiliser lorsque : la requête lit des colonnes larges ou dont elle n’a pas besoin.
- Modification : réduisez la taille ou le nombre de colonnes lues par la requête.
- Validation : comparez
read_bytes, l’utilisation de la mémoire et la durée dans les mêmes conditions.
Examiner les types de colonnes
String générique pour ces valeurs, et choisissez le plus petit type numérique signé ou non signé capable de représenter la plage attendue en toute sécurité. Pour les colonnes temporelles, utilisez Date ou DateTime, sauf si vous avez besoin de la plage étendue ou de la précision fractionnaire offertes par Date32 ou DateTime64.
Utiliser les colonnes nullable de manière réfléchie
Une colonne Nullable stocke, en plus de ses valeurs, un masque distinct indiquant les valeurs nulles, que ClickHouse doit également lire et traiter. Utilisez-la lorsque la distinction entre une valeur nulle et la valeur par défaut du type est significative. Si une colonne est garantie de toujours contenir une valeur, un type non nullable évite cette surcharge.
Avant de modifier une colonne, vérifiez les données sources et le chemin d’ingestion plutôt que de supposer que des données observées comme non nulles le resteront toujours. L’exemple d’optimisation détaillé montre comment identifier les colonnes contenant des valeurs nulles et mesurer l’effet d’une modification du schéma.
Utiliser l’encodage par dictionnaire pour les valeurs répétées
LowCardinality utilise l’encodage par dictionnaire et est souvent efficace pour les colonnes de type String, telles que les valeurs de statut, les codes pays ou d’autres dimensions comportant nettement moins de valeurs distinctes que de lignes. Environ 10 000 valeurs distinctes constituent un bon point de départ pour identifier des candidats, mais ne représentent pas une limite fixe. Évitez les identifiants et les autres colonnes dont les valeurs sont majoritairement uniques, et comparez les mesures avant et après avoir modifié le type.
Consultez Sélection des types de données pour des recommandations plus détaillées.
Lire uniquement les colonnes nécessaires
SELECT *, en particulier pour les tables comportant de nombreuses colonnes ou les requêtes qui ne renvoient qu’un petit sous-ensemble de chaque ligne.
Utilisez read_bytes de system.query_log pour comparer le volume de données lu avant et après avoir réduit le nombre de colonnes sélectionnées. Si read_bytes reste élevé, examinez le plan de requête afin d’identifier les expressions, filtres, jointures ou requêtes imbriquées qui nécessitent encore des colonnes supplémentaires.
Par exemple, si un dashboard ne nécessite que l’heure de prise en charge, le type de paiement et le montant total, sélectionnez ces colonnes plutôt que la ligne complète :
SELECT *. Le nombre de lignes renvoyées reste inchangé, mais read_bytes devrait refléter le nombre réduit de colonnes lues.
Adapter l’organisation des données à la requête
- À utiliser lorsque : Un filtre sélectif lit encore de nombreuses parties ou granules.
- Modifier : Adaptez l’organisation physique aux filtres utilisés par les requêtes récurrentes.
- Valider : Comparez les parties et les granules sélectionnés par
EXPLAIN indexes = 1, puis vérifiezread_rows,read_byteset la durée.
Commencez par la clé de tri
MergeTree, la clé de tri détermine l’organisation des lignes sur le disque. Par défaut, elle sert également de clé primaire et définit l’index primaire creux. Contrairement à une clé primaire dans une base de données OLTP, une clé primaire ClickHouse ne garantit pas l’unicité. Son intérêt en termes de performances réside dans sa capacité à permettre à ClickHouse d’ignorer les granules qui ne peuvent pas répondre aux filtres d’une requête.
Privilégiez les colonnes qui apparaissent fréquemment dans des filtres sélectifs, ainsi que leur ordre dans la clé. Regrouper des valeurs associées peut également améliorer la compression. Lorsque l’ordre de regroupement ou de tri d’une requête correspond à la clé, ClickHouse peut appliquer des optimisations dans l’ordre pour GROUP BY ou ORDER BY.
Comparez les parties et les granules sélectionnés par EXPLAIN indexes = 1 avant et après avoir testé une autre clé de tri. Comparez également read_rows, read_bytes et la durée dans les mêmes conditions. Consultez Choisir une clé primaire pour des conseils détaillés sur le choix de la clé.
La table d’exemple utilise ORDER BY (). Le filtre de date sélectif suivant ne dispose donc d’aucune clé de tri permettant d’éliminer des granules :
Dans ClickHouse 25.9 et les versions ultérieures, ces paramètres garantissent que
EXPLAIN indique les index utilisés ainsi que les parties et les granules qu’ils éliminent.pickup_datetime, puis exécutez le même EXPLAIN sur cette table. La section de clé primaire du plan devrait afficher moins de granules sélectionnés, avant d’utiliser des mesures de durée ou de mémoire pour évaluer la modification dans son ensemble.
Évaluer d’autres options d’indexation et d’organisation des données
EXPLAIN indexes = 1 pour confirmer que la requête élague effectivement les partitions.
Ajouter un index d’évitement des données pour un filtre localisé
Un index d’évitement des données stocke des métadonnées qui permettent à ClickHouse d’éviter de lire les blocs qui ne peuvent pas correspondre à un filtre. Il est particulièrement utile lorsque la clé de tri ne prend pas en charge un filtre important et que les valeurs correspondantes sont suffisamment regroupées au sein des blocs.
Par exemple, un index bloom filter peut faciliter les recherches par égalité lorsque la plupart des blocs ne contiennent pas la valeur recherchée. Utilisez les index d’évitement après avoir examiné les types de données et la clé de tri. Un index qui exclut rarement un bloc ajoute une surcharge de stockage et d’évaluation sans réduire significativement la charge de travail. Testez le type d’index et la granularité avec des données représentatives, puis utilisez EXPLAIN indexes = 1 pour comparer les granules sélectionnés et vérifier read_rows, read_bytes et la durée.
Utiliser les projections de manière sélective
Les projections stockent des organisations alternatives des données parallèlement à une table. Elles peuvent fournir une autre clé de tri ou un résultat précalculé, et ClickHouse peut sélectionner une projection applicable sans que la requête doive y faire directement référence.
Par exemple, une projection triée par payment_type peut prendre en charge un filtre récurrent que le tri de la table de base ne prend pas en charge. Utilisez un nombre limité de projections pour les schémas d’accès importants que le tri de base ne peut pas gérer efficacement.
Les projections stockent des données d’index ou de colonnes supplémentaires et ajoutent du travail lors de l’insertion et de la fusion ; une projection couvrant toutes les colonnes duplique les colonnes qu’elle stocke. Une utilisation intensive des projections peut également accroître le travail nécessaire pour choisir une projection optimale au moment de l’exécution de la requête. Pour les déploiements importants comportant de nombreux schémas d’accès distincts, un nombre réduit de projections ou des tables distinctes conçues à cet effet sont souvent plus faciles à exploiter. Consultez Vues matérialisées ou projections pour choisir entre ces mécanismes.
Ajoutez un ordre de tri alternatif pour les requêtes qui filtrent par type de paiement et heure de prise en charge tout en continuant à interroger la table source :
EXPLAIN projections = 1 pour vérifier si ClickHouse sélectionne la projection et lit moins de lignes ou d’octets. Mesurez également le surcoût lié aux insertions et au stockage avant d’appliquer largement ce modèle.
Précalculer les opérations répétitives
- À utiliser lorsque : Les mêmes transformations ou agrégations occupent régulièrement l’essentiel du temps de requête.
- Modification : Déplacez les calculs répétitifs vers l’ingestion, une actualisation planifiée ou une organisation des données conçue à cet effet.
- Validation : Vérifiez que la requête lit un résultat plus petit et effectue moins de calculs au moment de l’exécution, tout en maintenant une charge d’ingestion ou d’actualisation acceptable.
Chaque section présente une implémentation de base, le principal compromis opérationnel et une méthode pour valider le résultat.
Vue matérialisée incrémentielle
sum(trip_count), regroupé par pickup_date, afin que les lignes en attente d’une fusion en arrière-plan soient combinées lors de la requête. La vue ne traite que les nouvelles insertions ; rechargez donc séparément les données sources existantes. Validez la modification en comparant la durée et le nombre de lignes lues avec l’agrégation d’origine, puis vérifiez que la charge d’insertion supplémentaire est acceptable.
Vue matérialisée actualisable
system.view_refreshes afin de vérifier que la durée, le statut et la fréquence d’actualisation sont adaptés à la charge de travail.
Table dédiée
Nullable de ces deux colonnes cibles. Vérifiez que cette approche répond aux exigences de données de la charge de travail. Le tableau de bord doit interroger explicitement cette table, et le pipeline d’ingestion doit la maintenir à jour. Validez cette modification en comparant le nombre de lignes et d’octets lus, l’utilisation de la mémoire et la durée avec la requête sur la table source. Tenez compte du stockage supplémentaire et de la maintenance du pipeline dans votre décision.