Un motor de tabla que almacena series temporales, es decir, un conjunto de valores asociados a marcas de tiempo y etiquetas (o labels):
metric_name1[tag1=value1, tag2=value2, ...] = {timestamp1: value1, timestamp2: value2, ...}
metric_name2[...] = ...Sintaxis
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
Es más fácil empezar con la configuración predeterminada (se puede crear una tabla TimeSeries sin especificar una lista de columnas):
CREATE TABLE my_table ENGINE=TimeSeriesA continuación, esta tabla puede utilizarse con los siguientes protocolos (debe asignarse un puerto en la configuración del servidor):
Columnas externas
Las columnas de una tabla TimeSeries se generan automáticamente. Son columnas externas: no almacenan datos, solo proporcionan la interfaz para SELECT/INSERT. Los datos reales se almacenan en las tablas de destino. Aquí está la lista de las columnas externas:
| Nombre | Tipo | Descripción |
|---|---|---|
metric_name |
String |
El nombre de la métrica |
tags |
Map(String, String) |
Mapa de etiquetas (labels) de la serie temporal |
time_series |
Array(Tuple(DateTime64(3), Float64)) de forma predeterminada |
Array de pares (timestamp, valor) de una serie temporal. Los tipos del timestamp y del elemento escalar de la tupla pueden derivarse de la declaración INNER COLUMNS de las sample (consulta Especificación de columnas externas) |
metric_family |
String |
El nombre de la familia de métricas (para los metadatos de métricas) |
type |
String |
El tipo de la métrica (p. ej., "counter", "gauge") |
unit |
String |
La unidad de la métrica |
help |
String |
La descripción de la métrica |
Ejemplo:
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)])Se permite que metric_name esté vacío durante la inserción, lo que significa que el nombre de la métrica se especifica en tags con __name__, por ejemplo:
INSERT INTO my_table (tags, time_series) VALUES
({'__name__': 'cpu_usage', 'job': 'test'},
[(toDateTime64('2024-01-01 00:00:00', 3), 0.5)])Para insertar metadatos de métricas, insértelos en las columnas metric_family, type, unit y 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')Especificación de columnas externas
La columna externa time_series se puede incluir explícitamente en una sentencia CREATE TABLE para sobrescribir su tipo predeterminado Array(Tuple(DateTime64(3), Float64)). ClickHouse extrae de la tupla los tipos de marca de tiempo y escalares, y los propaga a la tabla interna de muestras:
CREATE TABLE my_table (time_series Array(Tuple(UInt32, Float32))) ENGINE=TimeSeriesEsto equivale a declarar directamente los tipos de las columnas timestamp y value en la cláusula INNER COLUMNS de samples:
CREATE TABLE my_table ENGINE=TimeSeries
SAMPLES INNER COLUMNS (timestamp UInt32 CODEC(DoubleDelta, ZSTD(1)), value Float32 CODEC(ZSTD(3)))Si ambas formas se usan en la misma sentencia CREATE TABLE, los tipos declarados deben coincidir.
Tablas de destino
Una tabla TimeSeries no tiene datos propios; todo se almacena en sus tablas de destino.
Esto es similar al funcionamiento de una vista materializada,
con la diferencia de que una vista materializada tiene una sola tabla de destino,
mientras que una tabla TimeSeries tiene tres tablas de destino llamadas samples, etiqueta y metrics.
Las tablas de destino pueden especificarse explícitamente en la consulta CREATE TABLE
o el motor de tabla TimeSeries puede generar automáticamente tablas de destino internas.
Las filas insertadas en una tabla TimeSeries se transforman, se dividen en bloques y se insertan en estas tres tablas de destino.
Las tablas de destino son las siguientes:
Tabla samples
La tabla samples contiene series temporales asociadas a algún identificador.
La tabla samples debe tener las siguientes columnas:
| Nombre | ¿Obligatorio? | Tipo predeterminado | Tipos posibles | Descripción |
|---|---|---|---|---|
id |
[x] | Tuple(UInt64, UUID) |
cualquiera | Identifica una combinación de nombres de métricas y etiquetas |
timestamp |
[x] | DateTime64(3) |
DateTime64(X) |
Un instante temporal |
value |
[x] | Float64 |
Float32 o Float64 |
Un valor asociado al timestamp |
Las columnas que crea el propio motor utilizan códecs de compresión para series temporales:
timestamp CODEC(DoubleDelta, ZSTD(1)) y value CODEC(ZSTD(3)). Las marcas de tiempo casi monotónicas apenas
se comprimen con códecs genéricos y, de lo contrario, pueden representar la mayor parte del tamaño en disco de la tabla samples.
Consulte también Ajustar los tipos de las columnas.
La tabla etiquetas contiene identificadores calculados para cada combinación de un nombre de métrica y etiquetas.
La tabla etiquetas debe tener las siguientes columnas:
| Nombre | ¿Obligatorio? | Tipo predeterminado | Tipos posibles | Descripción |
|---|---|---|---|---|
id |
[x] | Tuple(UInt64, UUID) |
cualquiera (debe coincidir con el tipo de id de la tabla samples) |
Un id identifica una combinación de un nombre de métrica y etiquetas. La expresión DEFAULT especifica cómo calcular ese identificador |
metric_name |
[x] | LowCardinality(String) |
String o LowCardinality(String) |
El nombre de una métrica |
<tag_value_column> |
[ ] | String |
String o LowCardinality(String) o LowCardinality(Nullable(String)) |
El valor de una etiqueta específica; el nombre de la etiqueta y el nombre de la columna correspondiente se especifican en la configuración tags_to_columns |
tags |
[x] | Map(LowCardinality(String), String) |
Map(String, String) o Map(LowCardinality(String), String) o Map(LowCardinality(String), LowCardinality(String)) |
Mapa de todas las etiquetas, incluida la etiqueta __name__, que contiene el nombre de una métrica, y las etiquetas cuyos nombres se enumeran en la configuración tags_to_columns. Las tablas creadas por versiones anteriores de ClickHouse almacenaban en esta columna solo las etiquetas sin columnas dedicadas y sin el nombre de la métrica; la lectura gestiona ambos casos |
min_time |
[ ] | Nullable(DateTime64(3)) |
DateTime64(X) o Nullable(DateTime64(X)) |
Marca de tiempo mínima de la serie temporal con ese id. La columna se crea si store_min_time_and_max_time es true |
max_time |
[ ] | Nullable(DateTime64(3)) |
DateTime64(X) o Nullable(DateTime64(X)) |
Marca de tiempo máxima de la serie temporal con ese id. La columna se crea si store_min_time_and_max_time es true |
Tabla de métricas
La tabla metrics contiene información sobre las métricas recopiladas, sus tipos y sus descripciones.
La tabla metrics debe tener las siguientes columnas:
| Nombre | ¿Obligatorio? | Tipo predeterminado | Tipos posibles | Descripción |
|---|---|---|---|---|
metric_family_name |
[x] | String |
String o LowCardinality(String) |
El nombre de una familia de métricas |
type |
[x] | LowCardinality(String) |
String o LowCardinality(String) |
El tipo de una familia de métricas; uno de "counter", "gauge", "summary", "stateset", "histogram", "gaugehistogram" |
unit |
[x] | LowCardinality(String) |
String o LowCardinality(String) |
La unidad utilizada en una métrica |
help |
[x] | String |
String o LowCardinality(String) |
La descripción de una métrica |
Creación
Hay varias formas de crear una tabla con el motor de tabla TimeSeries.
La sentencia más sencilla
CREATE TABLE my_table ENGINE=TimeSeriesen realidad creará la siguiente tabla (puedes comprobarlo ejecutando 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_nameAsí que las columnas se generaron automáticamente y, además, hay tres tablas de destino internas con sus propias definiciones de columnas
almacenadas en las cláusulas INNER COLUMNS.
Las tablas de destino internas tienen nombres 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
y cada tabla de destino tiene su propio conjunto de columnas:
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 = 8192Crear una tabla AS desde una tabla existente
La sentencia CREATE TABLE new_table AS existing_table copia de existing_table:
SETTINGSINNER COLUMNSpara cada tipoINNER ENGINEpara cada tipo
La sentencia no está permitida si existing_table tiene destinos externos.
La lista de columnas externa se vuelve a generar y no se copia.
Ajuste de los tipos de las columnas
Puede ajustar los tipos de las columnas en las tablas de destino internas mediante la cláusula INNER COLUMNS. Por ejemplo, para almacenar marcas de tiempo en microsegundos y 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 columnas internas sin códecs implica usar el códec predeterminado para ellas:
CREATE TABLE my_table ENGINE=TimeSeries
SAMPLES INNER COLUMNS (timestamp DateTime64(6), value Float32)La columna id
La columna id contiene identificadores; cada uno se calcula a partir de una combinación de un nombre de métrica y etiquetas.
El tipo y la expresión DEFAULT utilizados para generar los identificadores se pueden personalizar mediante la cláusula TAGS INNER COLUMNS:
CREATE TABLE my_table ENGINE=TimeSeries
TAGS INNER COLUMNS (id UInt64 DEFAULT sipHash64(tags))La columna id puede ser de cualquier tipo comparable que no sea Nullable. Los tipos de id declarados en las tablas internas de samples y etiquetas deben coincidir.
Si no se proporciona ninguna expresión DEFAULT para la columna id y la configuración id_generator no está definida, ClickHouse elegirá automáticamente la expresión DEFAULT en función del tipo de id, pero solo si el tipo de id es uno de UUID, UInt64, UInt128, FixedString(16) o una tupla de dos de esos tipos. Para dicha tupla, la expresión elegida automáticamente calcula un hash del nombre de la métrica en el primer componente y un hash de todas las etiquetas en el segundo componente.
La configuración id_generator permite la misma personalización sin usar la cláusula INNER COLUMNS:
CREATE TABLE my_table ENGINE=TimeSeries
SETTINGS id_generator = 'sipHash64(tags)'Si el ajuste está definido, se usa para generar id incluso si el DEFAULT de la columna contiene otra expresión.
La columna tags contiene todas las etiquetas de una serie temporal, incluida la etiqueta __name__ con el nombre de una métrica.
La configuración tags_to_columns permite especificar que una etiqueta concreta también debe almacenarse en una columna independiente
además del mapa dentro de la columna tags:
CREATE TABLE my_table
ENGINE = TimeSeries
SETTINGS tags_to_columns = {'instance': 'instance', 'job': 'job'}Esta sentencia añadirá las columnas instance y job a la tabla de destino interna etiquetas.
Los valores de las etiquetas instance y job se almacenarán tanto en esas columnas como en la columna tags.
Motores de las tablas internas de destino
De forma predeterminada, las tablas internas de destino usan los siguientes motores de tabla:
- la tabla samples usa MergeTree;
- la tabla etiquetas usa AggregatingMergeTree porque los mismos datos suelen insertarse varias veces en esta tabla, por lo que hace falta una forma
de eliminar duplicados, y también porque es necesario realizar agregación para las columnas
min_timeymax_time; - la tabla de métrica usa ReplacingMergeTree porque los mismos datos suelen insertarse varias veces en esta tabla, por lo que hace falta una forma de eliminar duplicados.
También se pueden usar otros motores de tabla para las tablas internas de destino si así se especifica:
CREATE TABLE my_table ENGINE=TimeSeries
SAMPLES ENGINE=ReplicatedMergeTree
TAGS ENGINE=ReplicatedAggregatingMergeTree
METRICS ENGINE=ReplicatedReplacingMergeTreeLa tabla etiquetas mantiene las columnas de etiquetas (y el Map tags) fuera de su clave de ordenación,
algo que AggregatingMergeTree rechaza de forma predeterminada (consulte allow_dimensions_outside_sorting_key).
Esto es seguro aquí porque esas columnas dependen funcionalmente de id, que forma parte de la clave de ordenación, por lo que todas las
filas que un merge en segundo plano colapsa comparten los mismos valores. Cuando la tabla interna de etiquetas se genera o su
motor se especifica en línea como arriba, TimeSeries establece automáticamente allow_dimensions_outside_sorting_key = 1 en ella;
para una tabla de etiquetas de agregación externa creada manualmente, debe configurarlo usted mismo.
Tablas de destino externas
Es posible hacer que una tabla TimeSeries utilice una tabla creada 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;Los tipos de columna de las tablas externas (id, timestamp, value y las <tag_value_column> enumeradas en tags_to_columns) deben coincidir con los que la tabla TimeSeries generaría internamente en otras circunstancias (consulte tabla Samples, tabla Etiquetas y tabla Metrics para conocer las restricciones de tipo). Las discrepancias de tipo se notifican en el momento de CREATE.
La expresión del generador de id para un destino externo de etiquetas se resuelve en tiempo de INSERT en el siguiente orden: la configuración id_generator (si está establecida), luego el DEFAULT declarado en la columna id de la tabla externa (si existe) y, por último, el generador canónico derivado del tipo de id. Por lo tanto, la configuración sobrescribe cualquier DEFAULT declarado en la tabla externa; consulte La columna id para obtener más detalles.
Modificar los ajustes
Se pueden modificar dos ajustes después de 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;Ten en cuenta que cambiar id_generator cuando ya hay datos en la tabla de etiquetas puede generar IDs distintos para la misma combinación de métrica+etiqueta: las filas antiguas conservan sus IDs anteriores y las nuevas usan el nuevo generador.
Los demás ajustes no se pueden cambiar con ALTER ... MODIFY SETTING porque quedan incorporados en el esquema de las tablas internas en el momento de CREATE.
Configuración
Aquí tienes una lista de configuraciones que se pueden especificar al definir una tabla TimeSeries:
| Nombre | Tipo | Predeterminado | Descripción |
|---|---|---|---|
id_generator |
Expression | depende del tipo de id |
Expresión que calcula el identificador (huella) de una serie temporal a partir de sus etiquetas. Si no se establece, se usa la expresión predeterminada para la columna id. Si la expresión predeterminada para la columna id tampoco está establecida, la expresión se elige automáticamente |
tags_to_columns |
Map | Map que especifica qué etiquetas deben colocarse en columnas independientes en la tabla etiquetas. Sintaxis: {'tag1': 'column1', 'tag2' : column2, ...} |
|
use_all_tags_column_to_generate_id |
Bool | false | Configuración obsoleta, no tiene ningún efecto |
store_min_time_and_max_time |
Bool | true | Si se establece en true, la tabla almacenará min_time y max_time para cada serie temporal |
aggregate_min_time_and_max_time |
Bool | true | Al crear una tabla tags interna de destino, esta opción permite usar SimpleAggregateFunction(min, Nullable(DateTime64(3))) en lugar de solo Nullable(DateTime64(3)) como tipo de la columna min_time, y lo mismo para la columna max_time |
filter_by_min_time_and_max_time |
Bool | true | Si se establece en true, la tabla usará las columnas min_time y max_time para filtrar las series temporales |
samples_index_granularity |
UInt64 | 32768 | Establece index_granularity de la tabla samples interna. Cuando se establece explícitamente, sobrescribe index_granularity de la declaración del motor. Se ignora para una tabla externa de samples y un motor que no sea MergeTree |
tags_index_granularity |
UInt64 | 8192 | Establece index_granularity de la tabla etiquetas interna. Cuando se establece explícitamente, sobrescribe index_granularity de la declaración del motor. Se ignora para una tabla externa de etiquetas y un motor que no sea MergeTree |
recent_samples_ttl_seconds |
UInt64 | 345600 | Retención de la tabla de destino adicional recent samples, en la que también se escribe cada sample insertado. Una tabla interna de samples recientes siempre obtiene TTL toDateTime(timestamp) + toIntervalSecond(recent_samples_ttl_seconds) a partir de esta configuración (sobrescribiendo cualquier TTL de la declaración del motor); una tabla externa de samples recientes debe retener al menos esta cantidad de segundos de datos. Las consultas cuyo rango de tiempo cabe en la ventana de TTL prefieren la tabla de samples recientes a la tabla principal de samples (consulta la configuración de nivel de consulta time_series_prefer_recent_samples_table). El valor predeterminado es de 4 días; el valor efectivo queda fijado en la definición de la tabla en el momento de CREATE. Establécelo en 0 para deshabilitar la tabla de samples recientes |
recent_samples_partition_by |
Expression | toStartOfInterval(toDateTime(timestamp), toIntervalHour(5)) |
Clave de partición de la tabla interna recent samples, por ejemplo, toStartOfHour(timestamp). Requiere que recent_samples_ttl_seconds sea distinto de cero |
recent_samples_index_granularity |
UInt64 | 8192 | Establece index_granularity de la tabla interna recent samples. Requiere que recent_samples_ttl_seconds sea distinto de cero |
Funciones
Aquí tienes una lista de funciones que admiten una tabla TimeSeries como argumento: