> ## Documentation Index
> Fetch the complete documentation index at: https://private-7c7dfe99-parallel-read-in-order-multi-part.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

> 一种用于存储时间序列的表引擎，即一组与时间戳和标签（或标记）相关联的值。

# TimeSeries 表引擎

export const PrivatePreviewBadge = () => {
  return <div className="privatePreviewBadge">
            <div className="privatePreviewIcon">
            <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
                <path d="M5.33301 6.66667V4.66667V4.66667C5.33301 3.194 6.52701 2 7.99967 2V2C9.47234 2 10.6663 3.194 10.6663 4.66667V4.66667V6.66667" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" />
                <path d="M8.00033 9.33337V11.3334" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" />
                <path fillRule="evenodd" clipRule="evenodd" d="M11.333 14H4.66634C3.92967 14 3.33301 13.4033 3.33301 12.6666V7.99996C3.33301 7.26329 3.92967 6.66663 4.66634 6.66663H11.333C12.0697 6.66663 12.6663 7.26329 12.6663 7.99996V12.6666C12.6663 13.4033 12.0697 14 11.333 14Z" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" />
            </svg>
        </div>
            {'私有预览'}
        </div>;
};

<PrivatePreviewBadge />

一种用于存储时间序列的表引擎，即一组与时间戳和标签 (或标记) 关联的值：

```sql theme={null}
metric_name1[tag1=value1, tag2=value2, ...] = {timestamp1: value1, timestamp2: value2, ...}
metric_name2[...] = ...
```

<Info>
  这是一个私有预览功能，未来的发行版中可能会发生不向后兼容的变更。
  使用 `enable_time_series_table` 设置
  启用 TimeSeries 表引擎。
  输入命令 `set enable_time_series_table = 1`。
</Info>

<Note>
  `TimeSeries` 表引擎在 ClickHouse Cloud 中作为私有预览功能提供。
  已参与私有预览的服务已配置
  `enable_time_series_table` 设置。其他 ClickHouse Cloud 服务
  没有此配置，且您无法自行在此类服务上启用该引擎。
</Note>

## 语法

```sql theme={null}
CREATE TABLE name [(columns)] ENGINE=TimeSeries
[SETTINGS var1=value1, ...]
[SAMPLES db.samples_table_name | [SAMPLES INNER COLUMNS (...)] [SAMPLES INNER ENGINE engine(arguments)]]
[RECENT SAMPLES db.recent_samples_table_name | [RECENT SAMPLES INNER COLUMNS (...)] [RECENT SAMPLES INNER ENGINE engine(arguments)]]
[TAGS db.tags_table_name | [TAGS INNER COLUMNS (...)] [TAGS INNER ENGINE engine(arguments)]]
[METRIC FAMILIES db.metric_families_table_name | [METRIC FAMILIES INNER COLUMNS (...)] [METRIC FAMILIES INNER ENGINE engine(arguments)]]
```

<Note>
  关键字 `SAMPLES` 有一个别名 `DATA`，关键字 `METRIC FAMILIES` 有一个别名 `METRICS`，保留它们都是为了保持向后兼容性。
  [版本](#schema-versioning)低于 4 的表定义使用 `METRICS` 写入，以便较旧的 server 能够读取它。
</Note>

## 用法

一开始先使用默认设置会更简单 (可以在不指定列列表的情况下创建 `TimeSeries` 表) ：

```sql theme={null}
CREATE TABLE my_table ENGINE=TimeSeries
```

随后，该表可与以下协议配合使用 (必须在服务器配置中分配端口) ：

* [Prometheus remote-write](/zh/concepts/features/interfaces/prometheus#remote-write)
* [Prometheus remote-read](/zh/concepts/features/interfaces/prometheus#remote-read)

### 外部列

TimeSeries 表的列会自动生成。这些列属于外部列，不存储任何数据，只为 SELECT/INSERT 提供接口。实际数据存储在[目标端表](#target-tables)中。以下是外部列列表：

| Name | Type | Description |
| - | - | - |
| `metric_name` | `String` | 指标名称 |
| `tags` | `Map(String, String)` | 时间序列的标签映射 (标记) |
| `samples` | `Array(Tuple(DateTime64(3), Float64))` (默认) | 时间序列的 (timestamp, value) 对数组。该 Tuple 的 timestamp 和 scalar 元素类型可从 samples `INNER COLUMNS` 声明中推导得出 (参见[指定外部列](#specifying-outer-columns))。在 [version](#schema-versioning) 2 及更早版本的表中，该列名为 `time_series` |
| `metric_family` | `String` | 指标族名称 (用于指标元数据) |
| `type` | `String` | 指标类型 (例如 "counter"、"gauge") |
| `unit` | `String` | 指标单位 |
| `help` | `String` | 指标说明 |

示例：

```sql theme={null}
INSERT INTO my_table (metric_name, tags, samples) 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)])
```

在插入时，`metric_name` 可以为空，这表示指标名称是在 `tags` 的 `__name__` 中指定的，例如：

```sql theme={null}
INSERT INTO my_table (tags, samples) VALUES
    ({'__name__': 'cpu_usage', 'job': 'test'},
     [(toDateTime64('2024-01-01 00:00:00', 3), 0.5)])
```

要插入指标元数据，请写入 `metric_family`、`type`、`unit` 和 `help` 列：

```sql theme={null}
INSERT INTO my_table (metric_name, tags, samples, metric_family, type, unit, help) VALUES
    ('http_requests_total', {'method': 'GET'}, [(now64(), 100.0)],
     'http_requests_total', 'counter', 'requests', 'Total HTTP requests')
```

### 指定外部列

可以在 `CREATE TABLE` 语句中显式列出外部 `samples` 列，以覆盖其默认的 `Array(Tuple(DateTime64(3), Float64))` 类型 (其旧名称 `time_series` 同样可用) 。ClickHouse 会从该元组中提取时间戳类型和 scalar 类型，并将它们传递到内部samples表：

```sql theme={null}
CREATE TABLE my_table (samples Array(Tuple(UInt32, Float32))) ENGINE=TimeSeries
```

这相当于直接在 samples 的 `INNER COLUMNS` 子句中声明时间戳列和值列的类型：

```sql theme={null}
CREATE TABLE my_table ENGINE=TimeSeries
SAMPLES INNER COLUMNS (timestamp UInt32 CODEC(Delta, T64, ZSTD(3)), value Float32 CODEC(ALP, ZSTD(3)))
```

如果在同一条 `CREATE TABLE` 语句中同时使用这两种形式，则声明的类型必须一致。

## 目标端表

`TimeSeries` 表本身不存储数据，所有数据都保存在其目标端表中。
这与 [materialized view](/zh/reference/statements/create/view#materialized-view) 的工作方式类似，
区别在于 materialized view 只有一个目标端表，
而 `TimeSeries` 表有三个必需的目标端表，分别名为 [samples](#samples-table)、[标签](#tags-table) 和 [指标族](#metric-families-table)，
以及一个默认启用的可选 [最近样本](#recent-samples-table) 目标端表
(请参阅 [recent\_samples\_ttl\_seconds](#settings) 设置) 。

这些目标端表既可以在 `CREATE TABLE` 查询中显式指定，
也可以由 `TimeSeries` 表引擎自动生成内部目标端表。

插入 `TimeSeries` 表的行会被转换、拆分为块，并写入这些目标端表。

目标端表如下：

### 样本表

*样本* 表包含与某个标识符关联的时间序列。

*样本* 表必须包含以下列：

| 名称 | 必填？ | 默认类型 | 可能的类型 | 描述 |
| - | - | - | - | - |
| `id` | \[x] | `Tuple(UInt64, LowCardinality(UUID))` | any | 标识一组指标名称和标签的组合 |
| `timestamp` | \[x] | `DateTime64(3)` | `DateTime64(X)` | 一个时间点 |
| `value` | \[x] | `Float64` | `Float32` 或 `Float64` | 与 `timestamp` 关联的值 |

引擎自行创建的列会使用时间序列压缩编解码器：
`timestamp CODEC(Delta, T64, ZSTD(3))` 和 `value CODEC(ALP, ZSTD(3))`。近乎单调的时间戳使用通用编解码器时几乎无法
压缩，因而可能会占据样本表磁盘存储空间的大部分。
引擎会为其内部样本表和最近样本表启用 `ALP`，无需设置 `enable_alp_codec`。
另请参阅[调整列的类型](#adjusting-column-types)。

### 最近样本表

\_最近样本\_表是可选的，默认启用 (请参阅 [recent\_samples\_ttl\_seconds](#settings) 设置；将其设为零可禁用该表) 。该表包含 TTL 未超过该设置所定义时长的样本副本，并且必须与[样本](#samples-table)表具有相同的列。
生成的 `timestamp` 列使用 `CODEC(Delta, T64, ZSTD(3))`，
生成的 `value` 列使用 `CODEC(ALP, ZSTD(3))`。

每个插入的样本都会同时写入样本表和最近样本表。
时间范围落在 TTL 窗口内的查询会从最近样本表而非主样本表读取数据，
因为前者小得多 (可通过查询级别设置 `time_series_prefer_recent_samples_table` 禁用此行为) 。

内部最近样本表的 TTL 始终由 [recent\_samples\_ttl\_seconds](#settings) 设置决定。

### 标签表

*tags* 表包含针对每种指标名称与标签组合计算出的标识符。

*tags* 表必须包含以下列：

| 名称 | 必填？ | 默认类型 | 可能的类型 | 描述 |
| - | - | - | - | - |
| `id` | \[x] | `Tuple(UInt64, LowCardinality(UUID))` | any (必须与 [samples](#samples-table) 表中 `id` 的类型匹配) | `id` 用于标识一种指标名称与标签的组合。DEFAULT 表达式指定了如何计算该标识符 |
| `metric_name` | \[x] | `LowCardinality(String)` | `String` 或 `LowCardinality(String)` | 指标名称 |
| `<tag_value_column>` | \[ ] | `String` | `String` 或 `LowCardinality(String)` 或 `LowCardinality(Nullable(String))` | 特定标签的值；该标签的名称以及对应列的名称在 [tags\_to\_columns](#settings) 设置中指定 |
| `tags` | \[x] | `Map(LowCardinality(String), String)` | `Map(String, String)` 或 `Map(LowCardinality(String), String)` 或 `Map(LowCardinality(String), LowCardinality(String))` | 所有标签的映射，包括表示指标名称的标签 `__name__`，以及 [tags\_to\_columns](#settings) 设置中枚举的标签名称。由旧版 ClickHouse 创建的表在此列中仅存储没有专用列且不含指标名称的标签；读取时会处理这两种情况 |
| `min_time` | \[ ] | `Nullable(DateTime64(3))` | `DateTime64(X)` 或 `Nullable(DateTime64(X))` | 具有该 `id` 的时间序列的最小时间戳。如果 [store\_min\_time\_and\_max\_time](#settings) 为 `true`，则会创建此列 |
| `max_time` | \[ ] | `Nullable(DateTime64(3))` | `DateTime64(X)` 或 `Nullable(DateTime64(X))` | 具有该 `id` 的时间序列的最大时间戳。如果 [store\_min\_time\_and\_max\_time](#settings) 为 `true`，则会创建此列 |

[版本](#schema-versioning) 5 及更高版本中，使用 `MergeTree` 家族引擎新建的内部 tags 表会在 `tags` 上建立倒排文本索引：
`INDEX tags_idx tags TYPE text(tokenizer = 'keyValuePairs')`。它通过同时查找键和值来加速 PromQL 中诸如
`{job="api"}` 这样的精确标记匹配。与空字符串的比较也会匹配缺失的标记，并且不会使用该索引。

在 `TAGS INNER COLUMNS` 中显式声明的索引会替换默认索引。已有的表以及外部 tags 表会保留其原有索引；可在其 tags target table 上添加并 materialize 该索引以启用它。

### 指标族表

*指标族* 表包含有关正在采集的指标族、这些指标族的类型及其描述的信息。
指标族是一组名称相同 (`__name__` 标签) 且类型相同的指标，例如，直方图就是一个由多个指标组成的指标族。

*指标族* 表必须包含以下列：

| 名称 | 必需？ | 默认类型 | 可能的类型 | 描述 |
| - | - | - | - | - |
| `metric_family_name` | \[x] | `String` | `String` 或 `LowCardinality(String)` | 指标族的名称 |
| `type` | \[x] | `LowCardinality(String)` | `String` 或 `LowCardinality(String)` | 指标族的类型，可为 "counter"、"gauge"、"summary"、"stateset"、"histogram"、"gaugehistogram" 之一 |
| `unit` | \[x] | `LowCardinality(String)` | `String` 或 `LowCardinality(String)` | 指标使用的单位 |
| `help` | \[x] | `String` | `String` 或 `LowCardinality(String)` | 指标的描述 |

## 创建

可以通过多种方式创建使用 `TimeSeries` 表引擎的表。
最简单的语句是

```sql theme={null}
CREATE TABLE my_table ENGINE=TimeSeries
```

实际上会创建如下表 (可执行 `SHOW CREATE TABLE my_table` 查看) ：

```sql theme={null}
CREATE TABLE my_table
(
    `metric_name` String,
    `tags` Map(String, String),
    `samples` Array(Tuple(DateTime64(3), Float64)),
    `metric_family` String,
    `type` String,
    `unit` String,
    `help` String
)
ENGINE = TimeSeries
SETTINGS version = 5, recent_samples_ttl_seconds = 345600
SAMPLES INNER COLUMNS
(
    `id` Tuple(UInt64, LowCardinality(UUID)),
    `timestamp` DateTime64(3) CODEC(Delta, T64, ZSTD(3)),
    `value` Float64 CODEC(ALP, ZSTD(3))
)
SAMPLES INNER ENGINE = MergeTree ORDER BY (id, timestamp) SETTINGS index_granularity = 32768
RECENT SAMPLES INNER COLUMNS
(
    `id` Tuple(UInt64, UUID),
    `timestamp` DateTime64(3) CODEC(Delta, T64, ZSTD(3)),
    `value` Float64 CODEC(ALP, ZSTD(3))
)
RECENT SAMPLES INNER ENGINE = MergeTree PARTITION BY toStartOfInterval(toDateTime(timestamp), toIntervalHour(5)) ORDER BY (id, timestamp) TTL toDateTime(timestamp) + toIntervalSecond(345600) SETTINGS index_granularity = 8192, ttl_only_drop_parts = 1
TAGS INNER COLUMNS
(
    `id` Tuple(UInt64, LowCardinality(UUID)) DEFAULT tuple(sipHash64(metric_name), toLowCardinality(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))),
    INDEX tags_idx tags TYPE text(tokenizer = 'keyValuePairs') GRANULARITY 100000000
)
TAGS INNER ENGINE = AggregatingMergeTree PRIMARY KEY metric_name ORDER BY (metric_name, id) SETTINGS allow_dimensions_outside_sorting_key = 1, index_granularity = 8192
METRIC FAMILIES INNER COLUMNS
(
    `metric_family_name` String,
    `type` LowCardinality(String),
    `unit` LowCardinality(String),
    `help` String
)
METRIC FAMILIES INNER ENGINE = ReplacingMergeTree ORDER BY metric_family_name
```

因此，列会自动生成。此外，还有四个内部目标表，各自的列定义存储在 `INNER COLUMNS` 子句中。
`recent_samples_ttl_seconds` 设置以其默认值写入 `SETTINGS` 子句：该设置定义最近样本表的 TTL，因此其有效值在创建时便已固定。
此外，最新的 schema 版本已固定写入 `version` 设置中 (参见 [Schema 版本控制](#schema-versioning)) 。

内部目标表的名称类似于 `.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`，
并且每个目标表都有各自的一组列：

```sql theme={null}
CREATE TABLE default.`.inner_id.samples.xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`
(
    `id` Tuple(UInt64, LowCardinality(UUID)),
    `timestamp` DateTime64(3) CODEC(Delta(8), T64, ZSTD(3)),
    `value` Float64 CODEC(ALP, ZSTD(3))
)
ENGINE = MergeTree
ORDER BY (id, timestamp)
SETTINGS index_granularity = 32768
```

```sql theme={null}
CREATE TABLE default.`.inner_id.recentsamples.xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`
(
    `id` Tuple(UInt64, UUID),
    `timestamp` DateTime64(3) CODEC(Delta(8), T64, ZSTD(3)),
    `value` Float64 CODEC(ALP, ZSTD(3))
)
ENGINE = MergeTree
PARTITION BY toStartOfInterval(toDateTime(timestamp), toIntervalHour(5))
ORDER BY (id, timestamp)
TTL toDateTime(timestamp) + toIntervalSecond(345600)
SETTINGS index_granularity = 8192, ttl_only_drop_parts = 1
```

```sql theme={null}
CREATE TABLE default.`.inner_id.tags.xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`
(
    `id` Tuple(UInt64, LowCardinality(UUID)) DEFAULT tuple(sipHash64(metric_name), toLowCardinality(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))),
    INDEX tags_idx tags TYPE text(tokenizer = 'keyValuePairs') GRANULARITY 100000000
)
ENGINE = AggregatingMergeTree
PRIMARY KEY metric_name
ORDER BY (metric_name, id)
SETTINGS allow_dimensions_outside_sorting_key = 1, index_granularity = 8192
```

```sql theme={null}
CREATE TABLE default.`.inner_id.metricfamilies.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 = 8192
```

## 基于现有表创建表

语句 `CREATE TABLE new_table AS existing_table` 会创建一个配置与 `existing_table` 相同的 `TimeSeries` 表，
其中 `existing_table` 必须是 `TimeSeries` 表。`existing_table` 的外部目标不会被复制：该语句必须自行声明
这些目标。

该语句会从 `existing_table` 复制：

* `SETTINGS` 子句，但不包括 `version`：新表始终使用最新版本。语句中
  指定的设置会按名称与复制的设置合并，因此语句中指定的设置优先于复制的设置，而 `name = DEFAULT`
  会将复制的设置重置为默认值；
* 每个内部表的 `INNER COLUMNS` 和 `INNER ENGINE` 子句。会保留自定义列 (例如额外列、带有 codec 或 DEFAULT 表达式的列)
  和自定义引擎部分 (例如带参数的引擎、自定义排序键
  或引擎设置) ；其他列和引擎部分会根据新表的设置进行调整，因此
  例如语句中指定的 `tags_to_columns`、`aggregate_min_time_and_max_time` 或 `tags_index_granularity` 会生效。

除非语句自行声明，否则 `id`、时间戳和值列的类型，以及内部引擎的复制类型 (`MergeTree`、
`ReplicatedMergeTree` 或 `SharedMergeTree`) 也会从 `existing_table` 继承。
外部列列表会重新生成，不会被复制。

由较早版本的 ClickHouse 创建的表可以用作 `existing_table`：新表会采用当前的
结构，例如当前的 `id` 类型和默认标识符表达式。

## 调整列类型

你可以使用 `INNER COLUMNS` 子句来调整内部目标表中各列的类型。例如，要将时间戳以微秒存储，并将值存储为 `Float32`，请使用：

```sql theme={null}
CREATE TABLE my_table ENGINE=TimeSeries
SAMPLES INNER COLUMNS (timestamp DateTime64(6) CODEC(Delta, T64, ZSTD(3)), value Float32 CODEC(ALP, ZSTD(3)))
```

未指定内部列的编解码器时，将使用默认编解码器：

```sql theme={null}
CREATE TABLE my_table ENGINE=TimeSeries
SAMPLES INNER COLUMNS (timestamp DateTime64(6), value Float32)
```

## `id` 列

`id` 列包含标识符；每个标识符都是根据某个指标名称与标签的组合计算得出的。
用于生成标识符的类型和 `DEFAULT` 表达式可通过 `TAGS INNER COLUMNS` 子句自定义：

```sql theme={null}
CREATE TABLE my_table ENGINE=TimeSeries
TAGS INNER COLUMNS (id UInt64 DEFAULT sipHash64(tags))
```

`id` 列可以是任何可比较的非 `Nullable` 类型。samples 和 标签 内部表中声明的 `id` 类型必须保持一致。

如果未为 `id` 列提供 `DEFAULT` 表达式且未设置 `id_generator` 设置，ClickHouse 会根据 `id` 类型自动选择 `DEFAULT` 表达式，但仅当 `id` 类型为 `UUID`、`UInt64`、`UInt128`、`FixedString(16)`、这些类型包裹在 `LowCardinality` 中的形式，或由其中两种类型组成的元组时才会这样做。对于此类元组，自动选择的表达式会在第一个组件中计算指标名称的哈希值，并在第二个组件中计算所有标签的哈希值。

使用 `LowCardinality` 标识符类型 (例如 `Tuple(UInt64, LowCardinality(UUID))`) 可使标识符保持字典编码：samples 表会按块存储小型字典并配合 dictionary indexes，而不是在每一行中重复完整的标识符，从而减少查询读取的数据量。

`id_generator` 设置也支持相同的自定义，而无需使用 `INNER COLUMNS` 子句：

```sql theme={null}
CREATE TABLE my_table ENGINE=TimeSeries
SETTINGS id_generator = 'sipHash64(tags)'
```

如果设置了此项，即使该列的 `DEFAULT` 包含其他表达式，也会用它来生成 `id`。

`id` 列的类型也可以通过 `id_type` 设置指定，而无需使用 `INNER COLUMNS` 子句：

```sql theme={null}
CREATE TABLE my_table ENGINE=TimeSeries
SETTINGS id_type = 'UInt64', id_generator = 'sipHash64(tags)'
```

设置了 `id_generator` 时，`id_type` 设置会在 `CREATE` 时自动记录，
因此该定义会保留此表达式所针对的类型。

## `tags` 列

`tags` 列包含时间序列的所有标签，其中包括带有指标名称的 `__name__` 标签。

`tags_to_columns` 设置允许指定将某个特定标签也存储在单独的列中，
作为 `tags` 列中 Map 的补充：

```sql theme={null}
CREATE TABLE my_table
ENGINE = TimeSeries
SETTINGS tags_to_columns = {'instance': 'instance', 'job': 'job'}
```

该语句将把 `instance` 和 `job` 列添加到内部[标签](#tags-table)目标表中。
标签 `instance` 和 `job` 的值将同时存储在这些列和 `tags` 列中。

<Note>
  在由旧版 ClickHouse 创建的表中，`tags` 列仅包含未存储在专用
  列中且不含指标名称的标签，而 `all_tags` 列是一个临时列，会在插入时填充
  除指标名称外的所有标签。
</Note>

## 内部目标端表的表引擎

默认情况下，内部目标端表使用以下表引擎：

* [samples](#samples-table) 表使用 [MergeTree](/zh/reference/engines/table-engines/mergetree-family/mergetree)；
* [最近样本](#recent-samples-table) 表使用 [MergeTree](/zh/reference/engines/table-engines/mergetree-family/mergetree)，按 5 小时分桶进行分区 (请参见 [recent\_samples\_partition\_by](#settings) 设置)，其 `TTL` 源自
  [recent\_samples\_ttl\_seconds](#settings) 设置，并启用 `ttl_only_drop_parts`，因此会整体删除过期的 parts；
* [标签](#tags-table) 表使用 [AggregatingMergeTree](/zh/reference/engines/table-engines/mergetree-family/aggregatingmergetree)，因为相同的数据通常会多次插入该表，因此需要一种去重方式，
  同时还需要对列 `min_time` 和 `max_time` 进行聚合；
* [指标族](#metric-families-table) 表使用 [ReplacingMergeTree](/zh/reference/engines/table-engines/mergetree-family/replacingmergetree)，因为相同的数据通常会多次插入该表，因此需要一种去重方式。

生成的内部表的引擎家族遵循 `default_table_engine` 查询级别设置：
当 `default_table_engine = ReplicatedMergeTree` 或 `SharedMergeTree` 时，内部表使用相应的
`Replicated` 或 `Shared` 引擎。当 `default_table_engine = None` (或任何其他值) 时，内部表的引擎
必须显式指定。

所有内部表必须具有相同的复制类型：如果其中一个表采用复制 (或共享) 机制，其他内部
表也必须采用复制 (或共享) 机制，否则其内容会在副本之间出现差异。例如，
声明 `SAMPLES INNER ENGINE = ReplicatedMergeTree(...)` 要求其他内部引擎也使用复制引擎 -
要么显式声明，要么通过 `default_table_engine = ReplicatedMergeTree` 生成。

如果指定了其他表引擎，内部目标端表也可以使用它们：

```sql theme={null}
CREATE TABLE my_table ENGINE=TimeSeries
SAMPLES ENGINE=ReplicatedMergeTree
RECENT SAMPLES ENGINE=ReplicatedMergeTree
TAGS ENGINE=ReplicatedAggregatingMergeTree
METRIC FAMILIES ENGINE=ReplicatedReplacingMergeTree
```

[标签](#tags-table) 表将标签列 (以及 `tags` Map) 放在其排序键之外，
而 `AggregatingMergeTree` 默认会拒绝这种做法 (请参见 [`allow_dimensions_outside_sorting_key`](/zh/reference/engines/table-engines/mergetree-family/aggregatingmergetree)) 。
这在这里是安全的，因为这些列在函数上依赖于 `id`，而 `id` 是排序键的一部分，因此后台合并折叠到一起的所有
行都具有相同的值。当内部 标签 表被生成，或者其引擎像上面那样以内联方式指定时，
`TimeSeries` 会自动为其设置 `allow_dimensions_outside_sorting_key = 1`；
对于手动创建的[外部](#external-target-tables)聚合 标签 表，则必须自行设置。

## 外部目标端表

可以让 `TimeSeries` 表使用手动创建的目标表：

```sql theme={null}
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 metric_families_for_my_table ...

CREATE TABLE my_table ENGINE=TimeSeries SAMPLES samples_for_my_table TAGS tags_for_my_table METRIC FAMILIES metric_families_for_my_table;
```

外部表也可用作[最近样本](#recent-samples-table)目标 (`RECENT SAMPLES my_recent_samples_table` 子句) 。
此类表必须与外部样本表具有相同的列，并且必须至少保留
[recent\_samples\_ttl\_seconds](#settings) 秒的数据，这由用户负责。

外部表的列类型 (`id`、`timestamp`、`value`，以及 [`tags_to_columns`](#settings) 中列出的各个 `<tag_value_column>`) 必须与 `TimeSeries` 表原本会在内部生成的类型一致 (类型约束请参见 [Samples 表](#samples-table)、[标签表](#tags-table) 和 [指标族表](#metric-families-table)) 。类型不匹配会在 `CREATE` 时报告。

外部标签表的 `id` 列类型以及生成标识符的表达式会在 `CREATE` 时记录到 [`id_type`](#settings) 和 [`id_generator`](#settings) 设置中 (从 [version](#schema-versioning) 2 开始) ，因此 `TimeSeries` 表的定义会保留它们：例如，`CREATE TABLE ... AS my_table` 会从 `my_table` 的定义中读取 `id` 类型，而无需读取其外部目标端表。如果未指定 `id_generator` 设置，则会将其设置为外部表 `id` 列上声明的 `DEFAULT` (如果有) ，否则设置为根据 `id` 类型派生出的规范生成器。即使外部表的 `DEFAULT` 之后发生变化，也仍会使用已记录的表达式来生成 `id`——详见 [`id` 列](#id-column)。

## 修改设置

执行 `CREATE` 后，可更改以下两个设置：

* `id_generator`
* `filter_by_min_time_and_max_time`

```sql theme={null}
ALTER TABLE my_table MODIFY SETTING id_generator = 'sipHash64(tags)';
ALTER TABLE my_table MODIFY SETTING filter_by_min_time_and_max_time = 0;
ALTER TABLE my_table RESET SETTING filter_by_min_time_and_max_time;
```

请注意，如果在标签表中已有数据后更改 `id_generator`，同一指标+标签组合可能会生成不同的 ID——旧行会保留原来的 ID，新行则会使用新的生成器。

其他设置不能通过 `ALTER ... MODIFY SETTING` 更改：其中大多数在 `CREATE` 时就已经固化在内部表的 schema 中，
而 `version` 设置会在 `CREATE` 时自动固定，并用于标识 schema 本身 (参见 [Schema 版本控制](#schema-versioning)) 。

## 设置

以下列出了在定义 `TimeSeries` 表时可指定的设置：

| 名称 | 类型 | 默认值 | 说明 |
| - | - | - | - |
| `id_type` | 数据类型 | 取决于 `id` 列 | 目标端表中 `id` 列的类型。通常该类型在内部表的 `INNER COLUMNS` 子句中声明，或在[外部](#external-target-tables)标签表中声明；如果类型未以其他方式保留在定义中，则该设置会在 `CREATE` 时自动记录：即当标签目标为外部表时，或设置了 `id_generator` 设置时。该设置也可以显式指定，以替代 `TAGS INNER COLUMNS (id <type>)`。要求 `version` 至少为 2 |
| `id_generator` | 表达式 | 取决于 `id` 类型 | 根据时间序列的标签计算其标识符 (指纹) 的表达式。如果未设置，则使用 `id` 列的默认表达式。如果 `id` 列的默认表达式也未设置，则会自动选择该表达式。对于外部标签表，如果 `version` 至少为 2，则该设置会在 `CREATE` 时自动记录 (请参阅 [外部目标端表](#external-target-tables)) |
| `tags_to_columns` | Map | {} | 指定哪些标签应映射到 [标签](#tags-table) 表中的独立列。语法：`{'tag1': 'column1', 'tag2' : column2, ...}` |
| `use_all_tags_column_to_generate_id` | Bool | false | 已废弃设置，不执行任何操作 |
| `store_min_time_and_max_time` | Bool | true | 如果设置为 true，则该表会为每个时间序列存储 `min_time` 和 `max_time` |
| `aggregate_min_time_and_max_time` | Bool | true | 创建内部目标 `tags` 表时，此标志会启用将 `min_time` 列的类型设为 `SimpleAggregateFunction(min, Nullable(DateTime64(3)))`，而不是仅使用 `Nullable(DateTime64(3))`；`max_time` 列同样如此 |
| `filter_by_min_time_and_max_time` | Bool | true | 如果设置为 true，则该表会使用 `min_time` 和 `max_time` 列来过滤时间序列 |
| `samples_index_granularity` | UInt64 | 32768 | 设置内部 [samples](#samples-table) 表的 `index_granularity`。显式设置时，会覆盖引擎声明中的 `index_granularity`。对于外部 samples 表和非 MergeTree 引擎，此设置会被忽略 |
| `recent_samples_ttl_seconds` | UInt64 | 345600 | 附加 `最近样本` 目标表的保留期，每个插入的样本也会写入该表。内部最近样本表始终会根据此设置获得 `TTL toDateTime(timestamp) + toIntervalSecond(recent_samples_ttl_seconds)` (覆盖引擎声明中的任何生存时间 (TTL)) ；外部最近样本表必须至少保留这么多秒的数据。时间范围落在生存时间 (TTL) 窗口内的查询会优先使用最近样本表，而非主 samples 表 (请参阅查询级别设置 `time_series_prefer_recent_samples_table`) 。默认值为 4 天；有效值会在 CREATE 时固定写入表定义。设置为 0 可禁用最近样本表 |
| `recent_samples_partition_by` | 表达式 | `toStartOfInterval(toDateTime(timestamp), toIntervalHour(5))` | 内部 `最近样本` 表的分区键，例如 `toStartOfHour(timestamp)`。显式设置时，会覆盖引擎声明中的分区键；如果两者均未设置，则每 5 小时使用一个分区。对于外部最近样本表，此设置会被忽略。要求 `recent_samples_ttl_seconds` 非零 |
| `recent_samples_index_granularity` | UInt64 | 8192 | 设置内部 `最近样本` 表的 `index_granularity`。显式设置时，会覆盖引擎声明中的 `index_granularity`。对于外部最近样本表和非 MergeTree 引擎，此设置会被忽略。要求 `recent_samples_ttl_seconds` 非零 |
| `tags_index_granularity` | UInt64 | 8192 | 设置内部 [标签](#tags-table) 表的 `index_granularity`。显式设置时，会覆盖引擎声明中的 `index_granularity`。对于外部标签表和非 MergeTree 引擎，此设置会被忽略 |
| `version` | UInt64 | 5 | 表的版本：它标识目标端表的集合及其结构。版本会在创建表时自动固定，之后无法更改，通常应在 `CREATE TABLE` 查询中省略 (请参阅 [Schema 版本控制](#schema-versioning)) |

## Schema 版本控制

`TimeSeries` 表引擎与 PromQL 执行层仍处于活跃开发阶段：
目标端表的集合及其结构可能在不同 ClickHouse 版本之间发生变化。
为了让此类变化可被检测，每个 `TimeSeries` 表都会将自身版本记录在 [version](#settings) 设置中。
建表时，该版本会自动固化到 `CREATE` 查询中——其取值为服务器已知的最新版本 (当前为 5) ——
并持久化在表的元数据中，且无法通过 `ALTER` 修改。在该设置引入之前创建的表被视为版本 0。
通常在 `CREATE TABLE` 查询中省略该设置即可——此时表会自动采用最新版本。
如果服务器支持所指定的版本，也可以显式指定 `version`；此时该表会按照该版本的方式来定义 (请参阅[版本历史](#version-history)) 。
`CREATE TABLE ... AS other_table` 不会复制另一个表的版本，请参阅[基于现有表创建表](#create-as)。

服务器支持一个版本范围，且不同场景所要求的最低版本可能不同：使用 `SELECT` 读取、使用 `INSERT`
或 Prometheus 远程写入协议写入、以及执行 PromQL ([prometheusQuery](/zh/reference/functions/table-functions/prometheusQuery)、
[prometheusQueryRange](/zh/reference/functions/table-functions/prometheusQueryRange)
和 [timeSeriesSelector](/zh/reference/functions/table-functions/timeSeriesSelector) 表函数、
`promql` dialect 以及 Prometheus HTTP 查询 API) ，三者的最低版本要求各不相同：

* 如果 `TimeSeries` 表的版本对 PromQL 而言过旧，则针对该表的 PromQL 查询会被拒绝。异常信息会建议重建该表：
  创建一个新的 `TimeSeries` 表，通过 `INSERT ... SELECT` 查询复制数据，再用新表替换旧表。
* 如果版本过旧而无法写入，则 `INSERT` 查询和 Prometheus 远程写入协议会被拒绝，但 `SELECT` 查询仍可正常工作。
* 如果版本对服务器而言完全过旧，则针对该表的所有查询 (`SHOW CREATE TABLE`、`DETACH` 和 `DROP` 除外) 都会被拒绝。

### 版本历史

| 版本 | 变更 |
| - | - |
| 0 | 在引入 `version` 设置之前创建的表，包括 “prealpha” 表 (这类表将目标端表的列声明为[外部列](#outer-columns)) 以及没有[最近样本](#recent-samples-table)表的表 |
| 1 | 引入了 `version` 设置 |
| 2 | 引入了 [`id_type`](#settings) 设置：使用外部标签表的表会在 `id_type` 中记录 `id` 列的类型，并在 [`id_generator`](#settings) 中记录生成标识符的表达式，因此其定义不再依赖于外部表。设置了 `id_generator` 时同样会记录 `id_type` (参见 [`id` 列](#id-column)) |
| 3 | 外部列 `time_series` 更名为 `samples` (参见[外部列](#outer-columns)) 。早期版本的表仍沿用该列的旧名称，[prometheusQuery](/zh/reference/functions/table-functions/prometheusQuery) 与 [prometheusQueryRange](/zh/reference/functions/table-functions/prometheusQueryRange) 表函数也会按表所使用的名称返回该列。存储的数据没有变化 |
| 4 | `metrics` 目标端表更名为 `metric families`：内部表命名为 `.inner_id.metricfamilies.<uuid>`，而非 `.inner_id.metrics.<uuid>`，并且定义使用关键字 `METRIC FAMILIES` 而非 `METRICS` 编写。存储的数据没有变化 |
| 5 | 使用 `MergeTree` 家族引擎的新建内部标签表，默认会在 `tags` map 上创建 `keyValuePairs` 文本索引 (参见 [Tags table](#tags-table)) |

# 函数

以下列出了支持将 `TimeSeries` 表作为参数的函数：

* [timeSeriesSamples](/zh/reference/functions/table-functions/timeSeriesSamples)
* [timeSeriesTags](/zh/reference/functions/table-functions/timeSeriesTags)
* [timeSeriesMetricFamilies](/zh/reference/functions/table-functions/timeSeriesMetricFamilies)
