Um motor de tabela que armazena séries temporais, ou seja, um conjunto de valores associados a timestamps e tags (ou labels):
metric_name1[tag1=value1, tag2=value2, ...] = {timestamp1: value1, timestamp2: value2, ...}
metric_name2[...] = ...Sintaxe
CREATE TABLE name [(columns)] ENGINE=TimeSeries
[SETTINGS var1=value1, ...]
[SAMPLES db.samples_table_name | [SAMPLES INNER COLUMNS (...)] [SAMPLES INNER ENGINE engine(arguments)]]
[TAGS db.tags_table_name | [TAGS INNER COLUMNS (...)] [TAGS INNER ENGINE engine(arguments)]]
[METRICS db.metrics_table_name | [METRICS INNER COLUMNS (...)] [METRICS INNER ENGINE engine(arguments)]]Uso
É mais fácil começar com tudo configurado com os valores padrão (é permitido criar uma tabela TimeSeries sem especificar uma lista de colunas):
CREATE TABLE my_table ENGINE=TimeSeriesEssa tabela pode ser usada com os seguintes protocolos (uma porta deve ser atribuída na configuração do servidor):
Colunas externas
As colunas de uma tabela TimeSeries são geradas automaticamente. São colunas externas: não armazenam dados, apenas fornecem a interface para SELECT/INSERT. Os dados reais são armazenados em tabelas de destino. Aqui está a lista das colunas externas:
| Nome | Tipo | Descrição |
|---|---|---|
metric_name |
String |
O nome da métrica |
tags |
Map(String, String) |
map de tags (labels) da série temporal |
time_series |
Array(Tuple(DateTime64(3), Float64)) por padrão |
Array de pares (timestamp, valor) de uma série temporal. Os tipos de elemento do timestamp e do escalar da tupla podem ser derivados da declaração INNER COLUMNS das amostras (consulte Especificando colunas externas) |
metric_family |
String |
O nome da família de métricas (para os metadados das métricas) |
type |
String |
O tipo da métrica (por exemplo, "counter", "gauge") |
unit |
String |
A unidade da métrica |
help |
String |
A descrição da métrica |
Exemplo:
INSERT INTO my_table (metric_name, tags, time_series) VALUES
('cpu_usage', {'job': 'node_exporter', 'instance': 'host1:9100'},
[(toDateTime64('2024-01-01 00:00:00', 3), 0.5), (toDateTime64('2024-01-01 00:01:00', 3), 0.7)])É permitido que metric_name fique vazio na inserção; isso significa que o nome da métrica é especificado em tags, em __name__, por exemplo:
INSERT INTO my_table (tags, time_series) VALUES
({'__name__': 'cpu_usage', 'job': 'test'},
[(toDateTime64('2024-01-01 00:00:00', 3), 0.5)])Para inserir os metadados das métricas, insira nas colunas metric_family, type, unit e help:
INSERT INTO my_table (metric_name, tags, time_series, metric_family, type, unit, help) VALUES
('http_requests_total', {'method': 'GET'}, [(now64(), 100.0)],
'http_requests_total', 'counter', 'requests', 'Total HTTP requests')Especificando colunas externas
A coluna externa time_series pode ser listada explicitamente em uma instrução CREATE TABLE para substituir seu tipo padrão Array(Tuple(DateTime64(3), Float64)). O ClickHouse extrai, da tupla, os tipos de timestamp e do valor escalar e os propaga para a tabela samples:
CREATE TABLE my_table (time_series Array(Tuple(UInt32, Float32))) ENGINE=TimeSeriesIsso equivale a declarar diretamente, na cláusula INNER COLUMNS de samples, os tipos das colunas de timestamp e valor:
CREATE TABLE my_table ENGINE=TimeSeries
SAMPLES INNER COLUMNS (timestamp UInt32 CODEC(DoubleDelta, ZSTD(1)), value Float32 CODEC(ZSTD(3)))Se ambas as formas forem usadas na mesma instrução CREATE TABLE, os tipos declarados deverão coincidir.
Tabelas de destino
Uma tabela TimeSeries não armazena dados próprios; tudo é armazenado em suas tabelas de destino.
Isso é semelhante ao funcionamento de uma visão materializada,
com a diferença de que uma visão materializada tem uma tabela de destino,
enquanto uma tabela TimeSeries tem três tabelas de destino chamadas samples, tags e metrics.
As tabelas de destino podem ser especificadas explicitamente na consulta CREATE TABLE
ou o motor de tabela TimeSeries pode gerar automaticamente tabelas de destino internas.
As linhas inseridas em uma tabela TimeSeries são transformadas, divididas em blocos e inseridas nessas três tabelas de destino.
As tabelas de destino são as seguintes:
Tabela samples
A tabela samples contém séries temporais associadas a um identificador.
A tabela samples deve ter as seguintes colunas:
| Nome | Obrigatória? | Tipo padrão | Tipos possíveis | Descrição |
|---|---|---|---|---|
id |
[x] | Tuple(UInt64, UUID) |
qualquer | Identifica uma combinação de nomes de métricas e tags |
timestamp |
[x] | DateTime64(3) |
DateTime64(X) |
Um ponto no tempo |
value |
[x] | Float64 |
Float32 ou Float64 |
Um valor associado ao timestamp |
As colunas que o motor cria por conta própria recebem codecs de compressão de séries temporais:
timestamp CODEC(DoubleDelta, ZSTD(1)) e value CODEC(ZSTD(3)). Timestamps quase monotônicos mal
são comprimidos por codecs genéricos e podem, caso contrário, dominar o tamanho em disco da tabela samples.
Consulte também Ajustando os tipos das colunas.
A tabela tags contém identificadores calculados para cada combinação de nome de métrica e tags.
A tabela tags deve ter as colunas:
| Nome | Obrigatório? | Tipo padrão | Tipos possíveis | Descrição |
|---|---|---|---|---|
id |
[x] | Tuple(UInt64, UUID) |
qualquer tipo (deve corresponder ao tipo de id na tabela samples) |
Um id identifica uma combinação de nome de métrica e tags. A expressão DEFAULT especifica como calcular esse identificador |
metric_name |
[x] | LowCardinality(String) |
String ou LowCardinality(String) |
O nome de uma métrica |
<tag_value_column> |
[ ] | String |
String ou LowCardinality(String) ou LowCardinality(Nullable(String)) |
O valor de uma tag específica; o nome da tag e o nome da coluna correspondente são especificados na configuração tags_to_columns |
tags |
[x] | Map(LowCardinality(String), String) |
Map(String, String) ou Map(LowCardinality(String), String) ou Map(LowCardinality(String), LowCardinality(String)) |
map de todas as tags, incluindo a tag __name__, que contém o nome de uma métrica, e as tags com nomes listados na configuração tags_to_columns. As tabelas criadas por versões mais antigas do ClickHouse armazenavam nesta coluna apenas as tags sem colunas dedicadas e sem o nome da métrica; a leitura lida com ambos os casos |
min_time |
[ ] | Nullable(DateTime64(3)) |
DateTime64(X) ou Nullable(DateTime64(X)) |
timestamp mínimo da série temporal com esse id. A coluna é criada se store_min_time_and_max_time for true |
max_time |
[ ] | Nullable(DateTime64(3)) |
DateTime64(X) ou Nullable(DateTime64(X)) |
timestamp máximo da série temporal com esse id. A coluna é criada se store_min_time_and_max_time for true |
Tabela de métricas
A tabela metrics contém algumas informações sobre as métricas coletadas, os tipos dessas métricas e suas descrições.
A tabela metrics deve conter as colunas:
| Nome | Obrigatória? | Tipo padrão | Tipos possíveis | Descrição |
|---|---|---|---|---|
metric_family_name |
[x] | String |
String ou LowCardinality(String) |
O nome de uma família de métricas |
type |
[x] | LowCardinality(String) |
String ou LowCardinality(String) |
O tipo de uma família de métricas, um de "counter", "gauge", "summary", "stateset", "histogram", "gaugehistogram" |
unit |
[x] | LowCardinality(String) |
String ou LowCardinality(String) |
A unidade usada em uma métrica |
help |
[x] | String |
String ou LowCardinality(String) |
A descrição de uma métrica |
Criação
Há várias maneiras de criar uma tabela com o motor de tabela TimeSeries.
A instrução mais simples
CREATE TABLE my_table ENGINE=TimeSeriesna verdade, criará a tabela a seguir (você pode verificar isso executando SHOW CREATE TABLE my_table):
CREATE TABLE my_table
(
`metric_name` String,
`tags` Map(String, String),
`time_series` Array(Tuple(DateTime64(3), Float64)),
`metric_family` String,
`type` String,
`unit` String,
`help` String
)
ENGINE = TimeSeries
SAMPLES INNER COLUMNS
(
`id` Tuple(UInt64, UUID),
`timestamp` DateTime64(3) CODEC(DoubleDelta, ZSTD(1)),
`value` Float64 CODEC(ZSTD(3))
)
SAMPLES INNER ENGINE = MergeTree ORDER BY (id, timestamp) SETTINGS index_granularity = 32768
TAGS INNER COLUMNS
(
`id` Tuple(UInt64, UUID) DEFAULT tuple(sipHash64(metric_name), reinterpretAsUUID(sipHash128(tags))),
`metric_name` LowCardinality(String),
`tags` Map(LowCardinality(String), String),
`min_time` SimpleAggregateFunction(min, Nullable(DateTime64(3))),
`max_time` SimpleAggregateFunction(max, Nullable(DateTime64(3)))
)
TAGS INNER ENGINE = AggregatingMergeTree PRIMARY KEY metric_name ORDER BY (metric_name, id) SETTINGS allow_dimensions_outside_sorting_key = 1, index_granularity = 8192
METRICS INNER COLUMNS
(
`metric_family_name` String,
`type` LowCardinality(String),
`unit` LowCardinality(String),
`help` String
)
METRICS INNER ENGINE = ReplacingMergeTree ORDER BY metric_family_nameAssim, as colunas foram geradas automaticamente, e também há três tabelas de destino internas com suas próprias definições de colunas
armazenadas nas cláusulas INNER COLUMNS.
As tabelas de destino internas têm nomes como .inner_id.samples.xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx,
.inner_id.tags.xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx, .inner_id.metrics.xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
e cada tabela de destino tem seu próprio conjunto de colunas:
CREATE TABLE default.`.inner_id.samples.xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`
(
`id` Tuple(UInt64, UUID),
`timestamp` DateTime64(3) CODEC(DoubleDelta, ZSTD(1)),
`value` Float64 CODEC(ZSTD(3))
)
ENGINE = MergeTree
ORDER BY (id, timestamp)
SETTINGS index_granularity = 32768CREATE TABLE default.`.inner_id.tags.xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`
(
`id` Tuple(UInt64, UUID) DEFAULT tuple(sipHash64(metric_name), reinterpretAsUUID(sipHash128(tags))),
`metric_name` LowCardinality(String),
`tags` Map(LowCardinality(String), String),
`min_time` SimpleAggregateFunction(min, Nullable(DateTime64(3))),
`max_time` SimpleAggregateFunction(max, Nullable(DateTime64(3)))
)
ENGINE = AggregatingMergeTree
PRIMARY KEY metric_name
ORDER BY (metric_name, id)
SETTINGS allow_dimensions_outside_sorting_key = 1, index_granularity = 8192CREATE TABLE default.`.inner_id.metrics.xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`
(
`metric_family_name` String,
`type` LowCardinality(String),
`unit` LowCardinality(String),
`help` String
)
ENGINE = ReplacingMergeTree
ORDER BY metric_family_name
SETTINGS index_granularity = 8192Criando uma tabela AS com base em uma tabela existente
A instrução CREATE TABLE new_table AS existing_table copia da existing_table:
SETTINGSINNER COLUMNSpara cada tipoINNER ENGINEpara cada tipo
A instrução não é permitida se a existing_table tiver alvos externos.
A lista externa de colunas é regenerada, e não copiada.
Ajustando os tipos das colunas
Você pode ajustar os tipos das colunas nas tabelas de destino internas usando a cláusula INNER COLUMNS. Por exemplo, para armazenar timestamps em microssegundos e valores como Float32, use:
CREATE TABLE my_table ENGINE=TimeSeries
SAMPLES INNER COLUMNS (timestamp DateTime64(6) CODEC(DoubleDelta, ZSTD(1)), value Float32 CODEC(ZSTD(3)))Especificar colunas internas sem codecs significa usar o codec padrão para elas:
CREATE TABLE my_table ENGINE=TimeSeries
SAMPLES INNER COLUMNS (timestamp DateTime64(6), value Float32)A coluna id
A coluna id contém identificadores; cada identificador é calculado com base em uma combinação de nome de métrica e tags.
O tipo e a expressão DEFAULT usados para gerar identificadores podem ser personalizados por meio da cláusula TAGS INNER COLUMNS:
CREATE TABLE my_table ENGINE=TimeSeries
TAGS INNER COLUMNS (id UInt64 DEFAULT sipHash64(tags))A coluna id pode ser de qualquer tipo comparável que não seja Nullable. Os tipos de id declarados nas tabelas internas samples e tags devem corresponder.
Se nenhuma expressão DEFAULT for fornecida para a coluna id e a configuração id_generator não estiver definida, ClickHouse escolherá a expressão DEFAULT automaticamente com base no tipo de id, mas apenas se o tipo de id for um dos seguintes: UUID, UInt64, UInt128, FixedString(16) ou uma tupla de dois desses tipos. Para essa tupla, a expressão escolhida automaticamente calcula um hash do nome de métrica no primeiro componente e um hash de todas as tags no segundo componente.
A configuração id_generator oferece a mesma personalização sem usar a cláusula INNER COLUMNS:
CREATE TABLE my_table ENGINE=TimeSeries
SETTINGS id_generator = 'sipHash64(tags)'Se essa configuração estiver definida, ela será usada para gerar o id, mesmo que o DEFAULT da coluna contenha uma expressão diferente.
A coluna tags contém todas as tags de uma série temporal, incluindo a tag __name__ com o nome de uma métrica.
A configuração tags_to_columns permite especificar que uma tag específica também deve ser armazenada em uma coluna separada
além do map dentro da coluna tags:
CREATE TABLE my_table
ENGINE = TimeSeries
SETTINGS tags_to_columns = {'instance': 'instance', 'job': 'job'}Esta instrução adicionará as colunas instance e job à tabela de destino interna de tags.
Os valores das tags instance e job serão armazenados tanto nessas colunas quanto na coluna tags.
Motores de tabela das tabelas de destino internas
Por padrão, as tabelas de destino internas usam os seguintes motores de tabela:
- a tabela samples usa MergeTree;
- a tabela tags usa AggregatingMergeTree porque os mesmos dados costumam ser inseridos várias vezes nessa tabela, então precisamos de uma forma
de remover duplicatas, além de ser necessário fazer agregação para as colunas
min_timeemax_time; - a tabela metrics usa ReplacingMergeTree porque os mesmos dados costumam ser inseridos várias vezes nessa tabela, então precisamos de uma forma de remover duplicatas.
Outros motores de tabela também podem ser usados nas tabelas de destino internas, caso isso seja especificado:
CREATE TABLE my_table ENGINE=TimeSeries
SAMPLES ENGINE=ReplicatedMergeTree
TAGS ENGINE=ReplicatedAggregatingMergeTree
METRICS ENGINE=ReplicatedReplacingMergeTreeA tabela tags mantém as colunas de tag (e o Map tags) fora de sua chave de ordenação,
o que AggregatingMergeTree rejeita por padrão (consulte allow_dimensions_outside_sorting_key).
Isso é seguro aqui porque essas colunas dependem funcionalmente de id, que faz parte da chave de ordenação, portanto todas as
linhas que uma mesclagem em segundo plano combina compartilham os mesmos valores. Quando a tabela interna de tags é gerada ou seu
motor é especificado inline, como acima, TimeSeries define allow_dimensions_outside_sorting_key = 1 nela automaticamente;
para uma tabela externa de tags com agregação criada manualmente, você deve definir isso por conta própria.
Tabelas de destino externas
É possível fazer com que uma tabela TimeSeries use uma tabela criada manualmente:
CREATE TABLE samples_for_my_table
(
`id` UUID,
`timestamp` DateTime64(3),
`value` Float64
)
ENGINE = MergeTree
ORDER BY (id, timestamp);
CREATE TABLE tags_for_my_table ...
CREATE TABLE metrics_for_my_table ...
CREATE TABLE my_table ENGINE=TimeSeries SAMPLES samples_for_my_table TAGS tags_for_my_table METRICS metrics_for_my_table;Os tipos de coluna das tabelas externas (id, timestamp, value e os <tag_value_column> listados em tags_to_columns) devem corresponder aos que a tabela TimeSeries geraria internamente (consulte Tabela samples, Tabela de Tags e Tabela de métrica para as restrições de tipo). Incompatibilidades de tipo são informadas no momento do CREATE.
A expressão do gerador de ID para um destino externo de tags é resolvida no momento do INSERT, na seguinte ordem: a configuração id_generator (se definida), depois o DEFAULT declarado na coluna id da tabela externa (se houver) e, por fim, o gerador canônico derivado do tipo de id. Portanto, a configuração substitui qualquer DEFAULT declarado na tabela externa — consulte A coluna id para mais detalhes.
Alterando configurações
Duas configurações podem ser alteradas após CREATE:
id_generatorfilter_by_min_time_and_max_time
ALTER TABLE my_table MODIFY SETTING id_generator = 'sipHash64(tags)';
ALTER TABLE my_table MODIFY SETTING filter_by_min_time_and_max_time = 0;Observe que alterar id_generator quando já existem dados na tabela de tags pode gerar IDs diferentes para a mesma combinação de métrica+tag — as linhas antigas mantêm seus IDs antigos, e as linhas novas usam o novo gerador.
As outras configurações não podem ser alteradas com ALTER ... MODIFY SETTING porque são incorporadas ao esquema das tabelas internas no momento do CREATE.
Configurações
Aqui está uma lista de configurações que podem ser especificadas ao definir uma tabela TimeSeries:
| Nome | Tipo | Padrão | Descrição |
|---|---|---|---|
id_generator |
Expression | depende do tipo de id |
Expressão que calcula o identificador (fingerprint) de uma série temporal a partir de suas tags. Se não for definida, a expressão padrão da coluna id será usada. Se a expressão padrão da coluna id também não estiver definida, a expressão será escolhida automaticamente |
tags_to_columns |
Map | Map que especifica quais tags devem ser colocadas em colunas separadas na tabela tags. Sintaxe: {'tag1': 'column1', 'tag2' : column2, ...} |
|
use_all_tags_column_to_generate_id |
Bool | false | Configuração obsoleta, não faz nada |
store_min_time_and_max_time |
Bool | true | Se definido como true, a tabela armazenará min_time e max_time para cada série temporal |
aggregate_min_time_and_max_time |
Bool | true | Ao criar uma tabela tags interna de destino, esta opção permite usar SimpleAggregateFunction(min, Nullable(DateTime64(3))) em vez de apenas Nullable(DateTime64(3)) como tipo da coluna min_time, e o mesmo para a coluna max_time |
filter_by_min_time_and_max_time |
Bool | true | Se definido como true, a tabela usará as colunas min_time e max_time para filtrar séries temporais |
samples_index_granularity |
UInt64 | 32768 | Define index_granularity da tabela samples interna. Quando definido explicitamente, substitui index_granularity da declaração do motor. Ignorado para uma tabela samples externa e um motor que não seja MergeTree |
tags_index_granularity |
UInt64 | 8192 | Define index_granularity da tabela tags interna. Quando definido explicitamente, substitui index_granularity da declaração do motor. Ignorado para uma tabela tags externa e um motor que não seja MergeTree |
recent_samples_ttl_seconds |
UInt64 | 345600 | Retenção da tabela de destino adicional recent samples, na qual cada amostra inserida também é gravada. Uma tabela interna de amostras recentes sempre recebe TTL toDateTime(timestamp) + toIntervalSecond(recent_samples_ttl_seconds) derivado desta configuração (substituindo qualquer TTL da declaração do motor); uma tabela externa de amostras recentes deve reter pelo menos essa quantidade de segundos de dados. Consultas cujo intervalo de tempo está dentro da janela de TTL priorizam a tabela de amostras recentes em vez da tabela principal de amostras (consulte a configuração no nível da consulta time_series_prefer_recent_samples_table). O padrão é de 4 dias; o valor efetivo é fixado na definição da tabela no momento do CREATE. Defina como 0 para desabilitar a tabela de amostras recentes |
recent_samples_partition_by |
Expression | toStartOfInterval(toDateTime(timestamp), toIntervalHour(5)) |
Chave de partição da tabela interna recent samples, por exemplo, toStartOfHour(timestamp). Requer que recent_samples_ttl_seconds seja diferente de zero |
recent_samples_index_granularity |
UInt64 | 8192 | Define index_granularity da tabela interna recent samples. Requer que recent_samples_ttl_seconds seja diferente de zero |
Funções
Aqui está uma lista de funções que aceitam uma tabela TimeSeries como argumento: