Skip to main content
Les déploiements de ClickHouse for Observability impliquent invariablement de grands volumes de données qui doivent être gérés. ClickHouse offre un certain nombre de fonctionnalités pour faciliter cette gestion.
ClickStack inclut un schéma par défaut optimiséClickStack fournit des schémas prêts à l’emploi pour les logs, les traces et les métriques qui intègrent les dernières fonctionnalités de ClickHouse (index textuels pour la recherche en texte intégral et sur les clés de Map, colonnes matérialisées et arrays ALIAS pour le filtrage en lecture directe, recherches de lignes par numéro de bloc) et ont été benchmarkés pour offrir de solides performances prêtes à l’emploi pour les charges de travail de logs et de traces. Servez-vous-en comme point de référence pour votre propre conception.

Partitions

Le partitionnement dans ClickHouse permet de séparer logiquement les données sur le disque selon une colonne ou une expression SQL. Grâce à cette séparation logique, chaque partition peut être manipulée indépendamment, par exemple supprimée. Cela permet de déplacer efficacement des partitions, et donc des sous-ensembles, entre les niveaux de stockage en fonction du temps, ou d’expirer des données/supprimer efficacement depuis un cluster. Le partitionnement est spécifié sur une table lors de sa définition initiale via la clause PARTITION BY. Cette clause peut contenir une expression SQL portant sur une ou plusieurs colonnes, dont le résultat détermine vers quelle partition une ligne est envoyée. Les data parts sont logiquement associées (via un préfixe commun de nom de dossier) à chaque partition sur le disque et peuvent être interrogées séparément. Dans l’exemple ci-dessous, le schéma otel_logs par défaut effectue un partitionnement par jour à l’aide de l’expression toDate(Timestamp). À mesure que des lignes sont insérées dans ClickHouse, cette expression est évaluée pour chaque ligne, qui est ensuite dirigée vers la partition correspondante si elle existe (si la ligne est la première d’un jour donné, la partition sera créée).
Un certain nombre d’opérations peuvent être effectuées sur les partitions, notamment des sauvegardes, des opérations sur les colonnes, des mutations modifiant/supprimant des données ligne par ligne) et la suppression d’index (p. ex. d’index secondaires). À titre d’exemple, supposons que notre table otel_logs soit partitionnée par jour. Si elle est alimentée avec le jeu de données de logs structurés, elle contiendra plusieurs jours de données :
Vous pouvez obtenir la liste des partitions actuelles à l’aide d’une simple requête sur une table système :
Nous pouvons avoir une autre table, otel_logs_archive, utilisée pour stocker les données plus anciennes. Les données peuvent être déplacées efficacement vers cette table au niveau de la partition (il s’agit simplement d’une modification des métadonnées).
À l’inverse d’autres techniques, celle-ci nécessiterait l’utilisation d’un INSERT INTO SELECT ainsi qu’une réécriture des données dans la nouvelle table cible.
Déplacement de partitionsLe déplacement de partitions entre tables impose que plusieurs conditions soient remplies, notamment que les tables aient la même structure, la même clé de partition, la même clé primaire et les mêmes index/projections. Des notes détaillées sur la façon de spécifier les partitions dans le DDL ALTER sont disponibles ici.
En outre, les données peuvent être supprimées efficacement par partition. Cette méthode est bien plus économe en ressources que les techniques alternatives (mutations ou lightweight deletes) et doit être privilégiée.
Cette fonctionnalité est utilisée par TTL lorsque le paramètre ttl_only_drop_parts=1 est activé. Voir Gestion des données avec TTL pour en savoir plus.

Applications

Ce qui précède illustre comment les données peuvent être déplacées et manipulées efficacement au niveau des partitions. En pratique, c’est le plus souvent dans des cas d’usage d’observabilité que vous exploiterez les opérations sur les partitions, selon deux scénarios :
  • Architectures à niveaux - déplacement des données entre les niveaux de stockage (voir Niveaux de stockage), ce qui permet de mettre en place des architectures hot-cold.
  • Suppression efficace - lorsque les données atteignent le TTL défini (voir Gestion des données avec TTL)
Nous examinons ces deux scénarios en détail ci-dessous.

Performances des requêtes

Bien que les partitions puissent contribuer aux performances des requêtes, cela dépend fortement des modes d’accès aux données. Si les requêtes ne ciblent qu’un petit nombre de partitions (idéalement une seule), les performances peuvent s’en trouver améliorées. Cela n’est généralement utile que si la clé de partitionnement ne fait pas partie de la clé primaire et que vous l’utilisez comme filtre. En revanche, les requêtes qui doivent couvrir un grand nombre de partitions peuvent être moins performantes qu’en l’absence de partitionnement (car il peut alors y avoir davantage de parts). L’intérêt de cibler une seule partition sera encore plus limité, voire nul, si la clé de partitionnement figure déjà au début de la clé primaire. Le partitionnement peut également être utilisé pour optimiser les requêtes GROUP BY si les valeurs de chaque partition sont uniques. Cependant, de façon générale, vous devez vous assurer que la clé primaire est optimisée et n’envisager le partitionnement comme technique d’optimisation des requêtes que dans des cas exceptionnels, lorsque les modes d’accès ne concernent qu’un sous-ensemble spécifique et prévisible des données, par exemple un partitionnement par jour lorsque la plupart des requêtes portent sur le dernier jour. Voir ici un exemple de ce comportement.

Gestion des données avec TTL (Time-to-live)

Le Time-to-Live (TTL) est une fonctionnalité clé des solutions d’observabilité basées sur ClickHouse pour assurer une rétention et une gestion efficaces des données, en particulier compte tenu des volumes considérables de données générés en continu. La mise en œuvre du TTL dans ClickHouse permet l’expiration et la suppression automatiques des données les plus anciennes, garantissant ainsi une utilisation optimale du stockage et le maintien des performances sans intervention manuelle. Cette fonctionnalité est essentielle pour alléger la base de données, réduire les coûts de stockage et garantir que les requêtes restent rapides et efficaces en se concentrant sur les données les plus pertinentes et les plus récentes. En outre, elle contribue au respect des politiques de rétention des données en gérant systématiquement le cycle de vie des données, renforçant ainsi la durabilité et la capacité de passage à l’échelle globales de la solution d’observabilité. Le TTL peut être spécifié au niveau de la table ou de la colonne dans ClickHouse.

TTL au niveau de la table

Le schéma par défaut des logs et des traces inclut un TTL pour faire expirer les données après une période donnée. Ce paramètre est défini dans le ClickHouse exporter sous une clé ttl, par exemple.
Cette syntaxe prend actuellement en charge la syntaxe Duration de Go. Nous recommandons d’utiliser h et de veiller à ce que cette valeur corresponde à la période de partitionnement. Par exemple, si vous partitionnez par jour, assurez-vous qu’il s’agit d’un multiple de jours, p. ex. 24h, 48h, 72h. Cela garantit automatiquement l’ajout d’une clause TTL à la table, p. ex. avec ttl: 96h.
Par défaut, les données dont le TTL a expiré sont supprimées lorsque ClickHouse fusionne des data parts. Lorsque ClickHouse détecte que des données ont expiré, il effectue une fusion hors cycle.
TTLs planifiésLes TTL ne sont pas appliqués immédiatement, mais selon une planification, comme indiqué ci-dessus. Le paramètre de table MergeTree merge_with_ttl_timeout définit le délai minimal, en secondes, avant de relancer une fusion avec un TTL de suppression. La valeur par défaut est de 14 400 secondes (4 heures). Mais il ne s’agit que du délai minimal : il peut s’écouler plus de temps avant qu’une fusion TTL ne soit déclenchée. Si la valeur est trop basse, de nombreuses fusions hors cycle pourront être effectuées, ce qui peut consommer beaucoup de ressources. Il est possible de forcer l’expiration d’un TTL à l’aide de la commande ALTER TABLE my_table MATERIALIZE TTL.
**Important : nous recommandons d’utiliser le paramètre ttl_only_drop_parts=1 ** (appliqué par le schéma par défaut). Lorsque ce paramètre est activé, ClickHouse supprime une part entière lorsque toutes les lignes qu’elle contient ont expiré. Supprimer des parts entières au lieu de nettoyer partiellement les lignes expirées par le TTL (au moyen de mutations gourmandes en ressources lorsque ttl_only_drop_parts=0) permet d’utiliser des valeurs merge_with_ttl_timeout plus courtes et de réduire l’impact sur les performances du système. Si les données sont partitionnées selon la même unité que celle utilisée pour l’expiration TTL, par exemple le jour, les parts ne contiendront naturellement que des données de l’intervalle défini. Cela garantit que ttl_only_drop_parts=1 puisse être appliqué efficacement.

TTL au niveau de la colonne

L’exemple ci-dessus fait expirer les données au niveau de la table. Vous pouvez également faire expirer les données au niveau de la colonne. À mesure que les données vieillissent, cela permet de supprimer les colonnes dont la valeur pour les investigations ne justifie plus le surcoût en ressources lié à leur conservation. Par exemple, nous recommandons de conserver la colonne Body au cas où de nouvelles métadonnées dynamiques seraient ajoutées sans avoir été extraites au moment de l’insertion, par exemple un nouveau label Kubernetes. Après un certain temps, par exemple 1 mois, il peut devenir évident que ces métadonnées supplémentaires ne sont pas utiles, ce qui réduit l’intérêt de conserver la colonne Body. Ci-dessous, nous montrons comment supprimer la colonne Body après 30 jours.
Pour spécifier un TTL au niveau d’une colonne, les utilisateurs doivent définir leur propre schéma. Cela ne peut pas être configuré dans l’OTel collector.

Recompression des données

Bien que nous recommandions généralement ZSTD(1) pour les jeux de données d’observability, vous pouvez tester différents algorithmes de compression ou des niveaux de compression plus élevés, par exemple ZSTD(3). Outre la possibilité de le spécifier lors de la création du schéma, la compression peut être configurée pour changer après une période définie. Cela peut être pertinent si un codec ou un algorithme de compression améliore la compression, mais dégrade les performances des requêtes. Ce compromis peut être acceptable pour des données plus anciennes, qui sont interrogées moins fréquemment, mais pas pour des données récentes, davantage utilisées lors des investigations. Un exemple est présenté ci-dessous, où nous compressons les données avec ZSTD(3) après 4 jours au lieu de les supprimer.
Évaluer les performancesNous recommandons aux utilisateurs d’évaluer toujours l’impact des différents niveaux et algorithmes de compression sur les performances d’insertion et de requête. Par exemple, les codecs delta peuvent être utiles pour compresser les horodatages. Toutefois, si ces horodatages font partie de la clé primaire, les performances de filtrage peuvent en pâtir.
Vous trouverez ici plus de détails et des exemples sur la configuration du TTL. Des exemples montrant comment ajouter et modifier des TTL pour des tables et des colonnes sont disponibles ici. Pour savoir comment les TTL permettent de mettre en place des hiérarchies de stockage, comme les architectures hot-warm, consultez niveau de stockage.

Niveaux de stockage

Dans ClickHouse, vous pouvez créer des niveaux de stockage sur différents disques, par exemple avec les données chaudes/récentes sur SSD et les données plus anciennes sur S3. Cette architecture permet d’utiliser un stockage moins coûteux pour les données anciennes, qui peuvent avoir des SLA de requête plus souples en raison de leur utilisation moins fréquente lors des investigations.
Ne s’applique pas à ClickHouse CloudClickHouse Cloud utilise une copie unique des données stockée sur S3, avec des caches de nœud sur SSD. Les niveaux de stockage ne sont donc pas nécessaires dans ClickHouse Cloud.
La création de niveaux de stockage nécessite que les utilisateurs créent des disques, qui servent ensuite à définir des politiques de stockage, avec des volumes pouvant être spécifiés lors de la création de la table. Les données peuvent être déplacées automatiquement entre les disques en fonction des taux de remplissage, de la taille des parts et des priorités des volumes. Vous trouverez plus de détails ici. Bien que les données puissent être déplacées manuellement entre les disques à l’aide de la commande ALTER TABLE MOVE PARTITION, le déplacement des données entre les volumes peut également être contrôlé à l’aide des TTL. Un exemple complet est disponible ici.

Gérer les changements de schéma

Les schémas des logs et des traces évolueront inévitablement au cours de la durée de vie d’un système, par exemple lorsque les utilisateurs surveillent de nouveaux systèmes avec des métadonnées ou des libellés de pod différents. En produisant les données à l’aide du schéma OTel et en capturant les données d’événement d’origine dans un format structuré, les schémas ClickHouse resteront robustes face à ces changements. Cependant, à mesure que de nouvelles métadonnées deviennent disponibles et que les modes d’accès des requêtes évoluent, vous souhaiterez mettre à jour les schémas pour tenir compte de ces évolutions. Afin d’éviter toute interruption de service lors des changements de schéma, les utilisateurs disposent de plusieurs options, que nous présentons ci-dessous.

Utiliser des valeurs par défaut

Des colonnes peuvent être ajoutées au schéma à l’aide de valeurs DEFAULT“. La valeur par défaut spécifiée sera utilisée si elle n’est pas fournie lors de l’INSERT. Les modifications du schéma peuvent être effectuées avant de modifier la logique de transformation d’une vue matérialisée ou la configuration d’un OTel collector, afin que ces nouvelles colonnes soient envoyées. Une fois le schéma modifié, vous pouvez reconfigurer les OTel collectors. En supposant que les utilisateurs suivent le processus recommandé décrit dans “Extraction de la structure avec SQL”, dans lequel les OTel collectors envoient leurs données vers un moteur de table Null, avec une vue matérialisée chargée d’extraire le schéma cible et d’envoyer les résultats vers une table cible pour le stockage, la vue peut être modifiée à l’aide de la syntaxe ALTER TABLE ... MODIFY QUERY syntax. Supposons que nous ayons la table cible ci-dessous avec la vue matérialisée correspondante (semblable à celle utilisée dans “Extraction de la structure avec SQL”) pour extraire le schéma cible à partir des logs structurés OTel :
Supposons que nous souhaitions extraire une nouvelle colonne Size à partir de LogAttributes. Nous pouvons l’ajouter à notre schéma avec un ALTER TABLE, en indiquant la valeur par défaut :
Dans l’exemple ci-dessus, nous définissons la valeur par défaut sur la clé size dans LogAttributes (elle vaudra 0 si elle n’existe pas). Cela signifie que les requêtes qui accèdent à cette colonne pour les lignes où la valeur n’a pas été insérée doivent accéder à la Map et seront donc plus lentes. Nous pourrions tout aussi bien la définir comme une constante, par exemple 0, ce qui réduirait le coût des requêtes ultérieures sur les lignes qui n’ont pas cette valeur. En interrogeant cette table, on constate que la valeur est renseignée comme prévu à partir de la Map :
Pour garantir que cette valeur soit insérée dans toutes les nouvelles données, nous pouvons modifier notre vue matérialisée à l’aide de la syntaxe ALTER TABLE, comme indiqué ci-dessous :
Les lignes suivantes auront la colonne Size renseignée lors de l’insertion.

Créer de nouvelles tables

Comme alternative au processus ci-dessus, vous pouvez simplement créer une nouvelle table cible avec le nouveau schéma. Toute vue matérialisée peut ensuite être modifiée pour utiliser la nouvelle table à l’aide de l’instruction ALTER TABLE MODIFY QUERY ci-dessus. Avec cette approche, vous pouvez versionner vos tables, par exemple otel_logs_v3. Cette approche laisse toutefois aux utilisateurs plusieurs tables à interroger. Pour interroger plusieurs tables, vous pouvez utiliser la fonction merge, qui accepte des motifs génériques pour le nom de la table. Nous le montrons ci-dessous en interrogeant les versions v2 et v3 de la table otel_logs :
Si les utilisateurs souhaitent éviter d’utiliser la fonction merge et exposer aux utilisateurs finaux une table combinant plusieurs tables, le moteur de table Merge peut être utilisé. Nous l’illustrons ci-dessous :
Cela peut être mis à jour chaque fois qu’une nouvelle table est ajoutée à l’aide de la syntaxe de table EXCHANGE. Par exemple, pour ajouter une table v4, nous pouvons créer une nouvelle table et l’échanger de façon atomique avec la version précédente.
Dernière modification le 3 juillet 2026