Skip to main content
Un moteur de table qui stocke des séries temporelles, c’est-à-dire un ensemble de valeurs associées à des horodatages et à des tags (ou labels) :
Il s’agit d’une fonctionnalité en private preview qui pourra, dans les versions ultérieures, évoluer de manière incompatible avec les versions précédentes. Activez l’utilisation du moteur de table TimeSeries à l’aide du paramètre enable_time_series_table. Saisissez la commande set enable_time_series_table = 1.
Le moteur de table TimeSeries est disponible dans ClickHouse Cloud en tant que fonctionnalité en private preview. Les services qui participent à la private preview disposent déjà du paramètre enable_time_series_table configuré. Les autres services ClickHouse Cloud ne possèdent pas cette configuration, et vous ne pouvez pas activer le moteur vous-même sur un tel service.

Syntaxe

Le mot-clé SAMPLES a pour alias DATA, et le mot-clé METRIC FAMILIES a pour alias METRICS, tous deux étant maintenus pour assurer la rétrocompatibilité. La définition d’une table d’une version antérieure à 4 est écrite avec METRICS, afin qu’un serveur plus ancien puisse la lire.

Utilisation

Il est plus facile de commencer en laissant tous les paramètres par défaut (il est possible de créer une table TimeSeries sans préciser de liste de colonnes) :
Cette table peut ensuite être utilisée avec les protocoles suivants (un port doit être défini dans la configuration du serveur) :

Colonnes externes

Les colonnes d’une table TimeSeries sont générées automatiquement. Ce sont des colonnes externes : elles ne stockent aucune donnée et servent uniquement d’interface pour SELECT/INSERT. Les données réelles sont stockées dans les tables cibles. Voici la liste des colonnes externes : Exemple :
metric_name peut être vide lors de l’insertion, ce qui signifie que le nom de la métrique est indiqué dans tags sous __name__, par exemple :
Pour insérer les métadonnées des métriques, insérez-les dans les colonnes metric_family, type, unit et help :

Spécification des colonnes externes

La colonne externe samples peut être déclarée explicitement dans une instruction CREATE TABLE afin de remplacer son type par défaut Array(Tuple(DateTime64(3), Float64)) (son ancien nom time_series est également accepté). ClickHouse extrait du tuple le type d’horodatage et le type scalaire, puis les propage à la table samples interne :
Cela revient à déclarer directement les types des colonnes timestamp et value dans la clause INNER COLUMNS de samples :
Si les deux formes sont utilisées dans la même instruction CREATE TABLE, les types déclarés doivent être identiques.

Tables cibles

Une table TimeSeries ne possède pas ses propres données : tout est stocké dans ses tables cibles. Son fonctionnement est similaire à celui d’une vue matérialisée, à la différence qu’une vue matérialisée n’a qu’une seule table cible, tandis qu’une table TimeSeries a trois tables cibles obligatoires nommées samples, tags et familles de métriques, ainsi qu’une table cible facultative d’échantillons récents, activée par défaut (consultez le paramètre recent_samples_ttl_seconds). Les tables cibles peuvent être spécifiées explicitement dans la requête CREATE TABLE, ou le moteur de table TimeSeries peut générer automatiquement des tables cibles internes. Les lignes insérées dans une table TimeSeries sont transformées, découpées en blocs, puis insérées dans ces tables cibles. Les tables cibles sont les suivantes :

Table samples

La table samples contient des séries temporelles associées à un certain identifiant. La table samples doit comporter les colonnes suivantes : Les colonnes créées par le moteur lui-même reçoivent des codecs de compression pour séries temporelles : timestamp CODEC(Delta, T64, ZSTD(3)) et value CODEC(ALP, ZSTD(3)). Les horodatages quasi monotones se compressent très peu avec des codecs génériques et peuvent sinon représenter l’essentiel de la taille sur disque de la table samples. Le moteur active ALP pour ses tables internes samples et recent samples sans qu’il soit nécessaire de définir enable_alp_codec. Voir aussi Ajustement des types de colonnes.

Table des échantillons récents

La table des échantillons récents est facultative et activée par défaut (voir le paramètre recent_samples_ttl_seconds ; définir sa valeur sur zéro désactive la table). Elle contient une copie des échantillons dont l’ancienneté est inférieure à la TTL définie par ce paramètre et doit comporter les mêmes colonnes que la table samples. La colonne générée timestamp utilise CODEC(Delta, T64, ZSTD(3)), et la colonne générée value utilise CODEC(ALP, ZSTD(3)). Chaque échantillon inséré est écrit à la fois dans la table samples et dans la table des échantillons récents. Les requêtes dont l’intervalle de temps est compris dans la fenêtre TTL lisent la table des échantillons récents plutôt que la table samples principale, car elle est bien plus petite (ce comportement peut être désactivé à l’aide du paramètre au niveau de la requête time_series_prefer_recent_samples_table). La TTL de la table interne des échantillons récents est toujours dérivée du paramètre recent_samples_ttl_seconds.

Table des tags

La table tags contient des identifiants calculés pour chaque combinaison d’un nom de métrique et de tags. La table tags doit contenir les colonnes suivantes : Les nouvelles tables internes de tags de version 5 et ultérieures, dotées d’un moteur de la famille MergeTree, possèdent un index de texte inversé sur tags : INDEX tags_idx tags TYPE text(tokenizer = 'keyValuePairs'). Il accélère les correspondances exactes de labels, telles que {job="api"} dans PromQL, en recherchant simultanément la clé et la valeur. Les comparaisons avec une chaîne vide correspondent également aux labels manquants et n’utilisent pas cet index. Les index explicites déclarés dans TAGS INNER COLUMNS remplacent l’index par défaut. Les tables existantes et les tables de tags externes conservent leurs index ; ajoutez et matérialisez l’index sur leur table de tags cible pour l’activer.

Table familles de métriques

La table familles de métriques contient des informations sur les familles de métriques collectées, leurs types et leurs descriptions. Une famille de métriques est un groupe de métriques portant le même nom (le tag __name__) et ayant le même type. Par exemple, un histogramme est une famille de métriques composée de plusieurs métriques. La table familles de métriques doit comporter les colonnes suivantes :

Création

Il existe plusieurs façons de créer une table avec le moteur de table TimeSeries. L’instruction la plus simple
créera en fait la table suivante (vous pouvez le vérifier en exécutant SHOW CREATE TABLE my_table) :
Les colonnes ont donc été générées automatiquement, et il existe également quatre tables cibles internes, chacune avec ses propres définitions de colonnes stockées dans les clauses INNER COLUMNS. Le paramètre recent_samples_ttl_seconds a été écrit dans la clause SETTINGS avec sa valeur par défaut : il définit le TTL de la table des échantillons récents, et sa valeur effective est donc fixée lors de la création. De plus, la dernière version du schéma a été fixée dans le paramètre version (voir Versionnement du schéma). Les tables cibles internes portent des noms tels que .inner_id.samples.xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx, .inner_id.recentsamples.xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx, .inner_id.tags.xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx, .inner_id.metricfamilies.xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx et chaque table cible possède son propre ensemble de colonnes :

Créer une table AS une table existante

L’instruction CREATE TABLE new_table AS existing_table crée une table TimeSeries configurée comme existing_table, qui doit elle-même être une table TimeSeries. Les cibles externes d’existing_table ne sont pas copiées : l’instruction doit déclarer ces cibles elle-même. L’instruction reprend d’existing_table :
  • la clause SETTINGS, à l’exception de version : la nouvelle table reçoit toujours la dernière version. Les paramètres indiqués dans l’instruction elle-même sont fusionnés par nom avec ceux qui sont copiés, si bien qu’un paramètre indiqué explicitement prime sur le paramètre copié, et que name = DEFAULT réinitialise un paramètre copié à sa valeur par défaut ;
  • les clauses INNER COLUMNS et INNER ENGINE de chaque table interne. Les colonnes personnalisées (par exemple des colonnes supplémentaires, des colonnes dotées d’un codec ou d’une expression DEFAULT) ainsi que les éléments de moteur personnalisés (par exemple un moteur avec des arguments, une clé de tri personnalisée ou un paramètre de moteur) sont conservés ; les autres colonnes et éléments de moteur sont alignés sur les paramètres de la nouvelle table, de sorte que tags_to_columns, aggregate_min_time_and_max_time ou tags_index_granularity indiqués dans l’instruction prennent effet.
Les types des colonnes id, timestamp et valeur, ainsi que le type de replication des moteurs internes (MergeTree, ReplicatedMergeTree ou SharedMergeTree), sont également repris d’existing_table, sauf si l’instruction les déclare elle-même. La liste des colonnes externes est régénérée et non copiée. Une table créée par une version plus ancienne de ClickHouse peut servir d’existing_table : la nouvelle table adopte la structure actuelle, par exemple le type id actuel et l’expression d’identifiant par défaut.

Ajustement des types de colonnes

Vous pouvez modifier le type des colonnes dans les tables cibles internes à l’aide de la clause INNER COLUMNS. Par exemple, pour stocker les horodatages en microsecondes et les valeurs en Float32, utilisez :
Spécifier des colonnes internes sans codec revient à utiliser le codec par défaut pour celles-ci :

La colonne id

La colonne id contient des identifiants ; chacun d’eux est calculé à partir d’une combinaison d’un nom de métrique et de tags. Le type et l’expression DEFAULT utilisés pour générérer les identifiants peuvent être personnalisés via la clause TAGS INNER COLUMNS :
La colonne id peut être de tout type comparable non-Nullable. Les types de id déclarés dans les tables internes samples et tags doivent correspondre. Si aucune expression DEFAULT n’est définie pour la colonne id et que le paramètre id_generator n’est pas défini, ClickHouse choisira automatiquement l’expression DEFAULT en fonction du type de id, mais uniquement si celui-ci est UUID, UInt64, UInt128, FixedString(16), ces mêmes types encapsulés dans LowCardinality ou un tuple de deux de ces types. Pour un tel tuple, l’expression choisie automatiquement calcule un hash du nom de métrique dans le premier composant et un hash de tous les tags dans le second composant. Un type d’identifiant LowCardinality, par exemple Tuple(UInt64, LowCardinality(UUID)), conserve les identifiants encodés par dictionnaire : la table samples stocke de petits dictionnaires par bloc avec des index de dictionnaire au lieu de répéter l’identifiant complet dans chaque ligne, ce qui réduit la quantité de données lues par les requêtes. Le paramètre id_generator offre la même possibilité de personnalisation sans utiliser la clause INNER COLUMNS :
Si ce paramètre est défini, il est utilisé pour générer id, même si le DEFAULT de la colonne contient une expression différente. Le type de la colonne id peut également être spécifié dans le paramètre id_type au lieu de la clause INNER COLUMNS :
Lorsque le paramètre id_generator est défini, le paramètre id_type est enregistré automatiquement au moment du CREATE, de sorte que la définition conserve le type pour lequel l’expression a été écrite.

La colonne tags

La colonne tags contient tous les tags d’une série temporelle, y compris le tag __name__ avec le nom d’une métrique. Le paramètre tags_to_columns permet de spécifier qu’un tag donné doit également être stocké dans une colonne distincte en plus de la map au sein de la colonne tags :
Cette instruction ajoute les colonnes instance et job à la table cible interne des tags. Les valeurs des tags instance et job seront stockées à la fois dans ces colonnes et dans la colonne tags.
Dans les tables créées par d’anciennes versions de ClickHouse, la colonne tags contient uniquement les tags sans colonnes dédiées et sans le nom de la métrique, et la colonne all_tags est une colonne éphémère qui était remplie lors de l’insertion avec tous les tags à l’exception du nom de la métrique.

Moteurs des tables cibles internes

Par défaut, les tables cibles internes utilisent les moteurs de table suivants :
  • la table samples utilise MergeTree ;
  • la table recent samples utilise MergeTree, partitionné en compartiments de 5 heures (voir le paramètre recent_samples_partition_by), avec un TTL dérivé du paramètre recent_samples_ttl_seconds et avec ttl_only_drop_parts activé, de sorte que les parties expirées sont supprimées dans leur intégralité ;
  • la table tags utilise AggregatingMergeTree, car les mêmes données sont souvent insérées plusieurs fois dans cette table ; il faut donc un moyen de supprimer les doublons, et ce moteur est également nécessaire pour effectuer une agrégation sur les colonnes min_time et max_time ;
  • la table familles de métriques utilise ReplacingMergeTree, car les mêmes données sont souvent insérées plusieurs fois dans cette table ; il faut donc un moyen de supprimer les doublons.
La famille de moteurs des tables internes générées suit le paramètre default_table_engine au niveau de la requête : avec default_table_engine = ReplicatedMergeTree ou SharedMergeTree, les tables internes utilisent les moteurs Replicated ou Shared correspondants. Avec default_table_engine = None (ou toute autre valeur), les moteurs des tables internes doivent être spécifiés explicitement. Toutes les tables internes doivent avoir le même type de réplication : si l’une d’elles est répliquée (ou partagée), les autres tables internes doivent également être répliquées (ou partagées), sans quoi leur contenu divergerait entre les répliques. Par exemple, déclarer SAMPLES INNER ENGINE = ReplicatedMergeTree(...) exige que les autres moteurs internes soient également répliqués - soit déclarés explicitement, soit générés avec default_table_engine = ReplicatedMergeTree. D’autres moteurs de table peuvent également être utilisés pour les tables cibles internes si cela est explicitement spécifié :
La table tags conserve les colonnes de tag (et le Map tags) en dehors de sa clé de tri, ce que AggregatingMergeTree refuse par défaut (voir allow_dimensions_outside_sorting_key). C’est sans danger ici, car ces colonnes dépendent fonctionnellement de id, qui fait partie de la clé de tri, de sorte que toutes les lignes qu’une fusion en arrière-plan regroupe partagent les mêmes valeurs. Lorsque la table interne de tags est générée ou que son moteur est spécifié en intégré comme ci-dessus, TimeSeries y définit automatiquement allow_dimensions_outside_sorting_key = 1 ; pour une table de tags d’agrégation externe créée manuellement, vous devez le définir vous-même.

Tables cibles externes

Il est possible de faire en sorte qu’une table TimeSeries utilise une table créée manuellement :
Une table externe peut également servir de cible pour les échantillons récents (la clause RECENT SAMPLES my_recent_samples_table). Une telle table doit comporter les mêmes colonnes qu’une table samples externe et doit conserver au moins recent_samples_ttl_seconds secondes de données, ce qui relève de la responsabilité de l’utilisateur. Les types de colonnes des tables externes (id, timestamp, value et les <tag_value_column> répertoriées dans tags_to_columns) doivent correspondre à ceux que la table TimeSeries générerait sinon en interne (voir table samples, table des tags et table des familles de métriques pour les contraintes de type). Les incompatibilités de type sont signalées lors de CREATE. Le type de la colonne id d’une table tags externe et l’expression générant les identifiants sont enregistrés dans les paramètres id_type et id_generator lors de CREATE (à partir de la version 2), de sorte que la définition de la table TimeSeries les conserve : par exemple, CREATE TABLE ... AS my_table lit le type de id depuis la définition de my_table sans lire ses tables cibles externes. Si le paramètre id_generator n’est pas spécifié, il prend la valeur DEFAULT déclarée sur la colonne id de la table externe (le cas échéant), sinon celle du générateur canonique dérivé du type de id. L’expression enregistrée est utilisée pour générer id même si la valeur DEFAULT de la table externe change par la suite — voir la colonne id pour plus de détails.

Modifier les paramètres

Deux paramètres peuvent être modifiés après CREATE :
  • id_generator
  • filter_by_min_time_and_max_time
Notez que la modification de id_generator alors que des données sont déjà présentes dans la table Tags peut produire des ID différents pour la même combinaison métrique+tag — les anciennes lignes conservent leurs anciens ID, les nouvelles lignes utilisent le nouveau générateur. Les autres paramètres ne peuvent pas être modifiés avec ALTER ... MODIFY SETTING : la plupart sont figés dans le schéma des tables internes au moment du CREATE, et le paramètre version est fixé automatiquement au moment du CREATE et identifie le schéma lui-même (voir versionnement du schéma).

Paramètres

Voici la liste des paramètres qui peuvent être spécifiés lors de la définition d’une table TimeSeries :

versionnement du schéma

Le moteur de table TimeSeries et la couche d’exécution PromQL sont en cours de développement actif : l’ensemble des tables cibles et leur structure peuvent évoluer d’une version de ClickHouse à l’autre. Afin de détecter ces changements, chaque table TimeSeries stocke sa version dans le paramètre version. La version est automatiquement inscrite dans la requête CREATE lors de la création d’une table ; sa valeur correspond à la dernière version connue du serveur (actuellement 5), elle est conservée dans les métadonnées de la table et ne peut pas être modifiée par ALTER. Les tables créées avant l’introduction du paramètre sont considérées comme étant en version 0. En règle générale, il suffit d’omettre le paramètre dans la requête CREATE TABLE : la table reçoit alors la dernière version. Une version explicite est acceptée si le serveur la prend en charge ; la table est alors définie selon les règles de cette version (voir Historique des versions). CREATE TABLE ... AS other_table ne copie pas la version de l’autre table, voir Créer une table AS une table existante. Un serveur prend en charge une plage de versions, et la version minimale peut différer selon qu’il s’agit de lire avec SELECT, d’écrire avec INSERT, d’utiliser le protocole remote-write de Prometheus ou d’évaluer des requêtes PromQL (les fonctions de table prometheusQuery, prometheusQueryRange et timeSeriesSelector, le dialecte promql et l’API HTTP de requêtes de Prometheus) :
  • Si la version d’une table TimeSeries est trop ancienne pour PromQL, les requêtes PromQL qui l’utilisent sont rejetées. L’exception suggère de recréer la table : créez une nouvelle table TimeSeries, copiez les données à l’aide d’une requête INSERT ... SELECT, puis remplacez l’ancienne table par la nouvelle.
  • Si la version est trop ancienne pour permettre l’écriture, les requêtes INSERT et le protocole remote-write de Prometheus sont rejetés, tandis que les requêtes SELECT continuent de fonctionner.
  • Si la version est trop ancienne pour le serveur, toute requête sur la table (à l’exception de SHOW CREATE TABLE, DETACH et DROP) est rejetée.

Historique des versions

Fonctions

Voici une liste de fonctions qui acceptent une table TimeSeries comme argument :
Dernière modification le 26 septembre 2026