时间序列类型格式
所有基于时间序列的格式共享的属性
- 帧应按时间列/字段按升序排序1
- 时间字段
- 不应包含空值
- 字段名称仅用于显示,不应包含标签
- 在每个帧中,第一个时间字段之后的任何额外时间字段都被视为剩余数据
- 值字段
- 值字段之所以这样命名,是因为它是每个数据点(时间,值)的值所在的字段。
- 它可以是数值或布尔字段。对于数值
- Go: Float64,*Float64, 或 Int64 等
- 在 JS 中为 'number'
- 系列名称来自值字段的 Name 属性
无效情况
- 至少没有时间字段和值字段(除非是单个帧的“无数据”情况)
- 存在“无数据”情况(不含字段的帧)和数据同时出现
- 可能是警告而非错误
- 重复项(由名称+维度标识)
- 未排序(时间未按从旧到新排序)
时间序列宽格式 (TimeSeriesWide)
版本: 0.1
宽格式在一个帧中包含一组共享相同时间字段的时间序列。之所以称为“宽”,是因为随着更多系列的添加,它会变得更宽。
示例
类型: Time 名称: T 标签: nil | 类型: Number 名称: cpu 标签: {"host": "a"} | 类型: Number 名称: cpu 标签: {"host": "b"} |
2022-04-27 5:00 | 1 | 6 |
2022-04-27 6:00 | 4 | 8 |
2022-04-27 7:00 | 2 | 5 |
2022-04-27 8:00 | 3 | 9 |
应具备以下属性:(另见共享属性)
- 第一个 Time 类型的字段是所有时间序列的时间索引。
- 应只有一个包含数据类型声明的帧。
- 应至少有一个属于值字段类型 的字段
- 如果存在多个数值字段,帧中时间字段与每个值字段的组合会创建每个时间序列(指标)
- 时间字段不应包含重复值(重复时间戳)。
剩余数据
- 任何没有类型声明或声明不同的额外帧
- 帧中的任何字符串字段
注意
- 一个 Go 示例对此进行了近似说明,请参见此处。
时间序列多格式 (TimeSeriesMulti)
版本: 0.1
TimeSeriesMulti 格式每帧包含一个时间序列。如果响应包含多个时间值可能不对齐的系列,则必须使用此格式而非 TimeSeriesWide。之所以称为“多”,是因为数据存在于多个数据帧中。
示例:
帧 0
类型: Time 名称: T 标签: nil | 类型: Number 名称: cpu 标签: {"host": "a"} |
2022-04-27 5:00 | 1 |
2022-04-27 6:00 | 4 |
2022-04-27 7:00 | 2 |
2022-04-27 8:00 | 3 |
帧 1
类型: Time 名称: T 标签: nil | 类型: Number 名称: cpu 标签: {"host": "b"} |
2022-04-27 5:00 | 6 |
2022-04-27 6:00 | 8 |
2022-04-27 7:00 | 5 |
2022-04-27 8:00 | 9 |
应具备以下属性:(另见共享属性)
- 每个帧应至少包含一个时间列和一个数值列。此类型的每个字段的第一次出现用于该系列。
- 不同的帧可以有不同的字段长度(但在同一帧内,它们必须长度相同)
- 每个时间字段不应包含重复值(重复时间戳)
剩余数据
- 每个帧中第一个数值或时间字段之后的任何字段
- 任何没有类型声明或声明不同的额外帧
- 帧中的任何字符串字段
注意
- Go 示例请参见此处。
- 多格式是唯一可以在不操作数据的情况下从其他格式转换而来的格式。因此,它是一种可以包含所有其他类型系列信息的类型。
时间序列长格式 (TimeSeriesLong)[类似 SQL]
版本: 0.1
这是 SQL 类系统中常见的响应格式。2 有关更简单(但不完整)的示例,请参见Grafana 文档:表格式中的多个维度。它目前作为后端某些查询 SQL 类数据的数据源中的一种数据转换而存在3,有关该代码如何工作,请参见此 Go 示例。
之所以称为“长”格式,是因为它比“宽”格式需要更多的行来存储相同的系列,因此它会变得更长。
示例
类型: Time 名称: T 标签: nil | 类型: String 名称: host 标签: nil | 类型: Number 名称: cpu 标签: nil |
2022-04-27 5:00 | a | 1 |
2022-04-27 5:00 | b | 6 |
2022-04-27 6:00 | a | 4 |
2022-04-27 6:00 | b | 8 |
2022-04-27 7:00 | a | 2 |
2022-04-27 7:00 | b | 5 |
2022-04-27 8:00 | a | 3 |
2022-04-27 8:00 | b | 9 |
应具备以下属性:(另见共享属性):
- 第一个时间字段用作时间戳
- 时间字段可以包含重复时间戳(但必须按时间升序排序)
- 可以可选地存在字符串字段。对于每个字符串字段
- 列/字段名称是维度(例如“label”)名称
- 该字段中对应的字符串值(按行)是标签值
- 通过遍历数据帧表响应的行来构建系列。
- 任何值字段/列的名称成为每个系列的名称
- 不使用字段的 labels 属性
剩余数据
- 第一个之后的任何额外时间字段
- 任何没有类型声明或声明不同的额外帧
附加属性或注意事项
- 在此格式中,完整的维度(例如“host”=值)是从字段中的值中提取的,而不是像其他格式那样在字段 schema 中声明。
- 由于维度表示在所有派生系列都存在的字段中,因此不能包含混合维度键,所有系列将拥有相同的维度键集合。例如,不能同时拥有 net.bytes{host="a"}和 net.bytes{host="a",int="eth0"}一起 - 第一个必须变为 net.bytes{host="a",int=""}
- 目前不清楚布尔类型的字段应被视为值字段(例如,上/下指标)还是维度(概念上会被视为标签)
时间序列格式之间的转换
源 | 目标 | 修改数据 | 属性和注意 |
宽 | 多 | 否* |
|
多 | 宽 | 是 |
|
宽 | 长† | 是 |
|
长 | 宽 | 是‡ |
|
长 | 多 | 否 |
|
多 | 长 | 是 |
|
注意
*在时间序列格式中,只有转换为“多”格式时,底层时间序列数据才不会被操作
†实践中,我还没有看到转换为长格式的情况,只看到读入的情况。也许在某个时候可能会被请求作为导出格式,但目前这主要是为了说明目的。
‡这被 SQL 数据源用于从“长”格式中提取时间序列(通过 go sdk/data pkg)。回想起来,我有点希望我们当初选择了 LongToMany。请参见此问题评论中的“相关内容”。
- 这是因为排序通常会消耗大量资源,并且在大多数情况下最好由数据源背后的数据库完成。↩
- 我不认为我们当前的 SQL 数据源严格遵循此规则,但一些 Azure 数据源遵循。这要么是由于对这种格式的意图以及升级到 Grafana 8 的误解,和/或对破坏性更改的缺乏了解,或两者兼而有之。↩
- 当使用“Format As=Time Series”进行查询时,会发生这种转换。在这种管道阶段进行转换的问题在于,虽然它为用户提供了表格式的常见时间序列数据,但它使得数据的“表格视图”与查询返回的 SQL 数据不一致。TODO: 稍后定义这个通用概念,也许称之为“所见非所得”,“数据沟通不畅”,或其他名称。这意味着我们要么需要返回两样东西(有点像 exemplars?),要么应该移动操作,或者采取其他措施。↩