跳到主要内容

时间序列类型格式

所有基于时间序列的格式共享的属性

  • 帧应按时间列/字段按升序排序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:0016
2022-04-27 6:0048
2022-04-27 7:0025
2022-04-27 8:0039

应具备以下属性:(另见共享属性)

  • 第一个 Time 类型的字段是所有时间序列的时间索引。
  • 应只有一个包含数据类型声明的帧。
  • 应至少有一个属于值字段类型 的字段
  • 如果存在多个数值字段,帧中时间字段与每个值字段的组合会创建每个时间序列(指标)
  • 时间字段不应包含重复值(重复时间戳)。

剩余数据

  • 任何没有类型声明或声明不同的额外帧
  • 帧中的任何字符串字段

注意

  • 一个 Go 示例对此进行了近似说明,请参见此处

时间序列多格式 (TimeSeriesMulti)

版本: 0.1

TimeSeriesMulti 格式每帧包含一个时间序列。如果响应包含多个时间值可能不对齐的系列,则必须使用此格式而非 TimeSeriesWide。之所以称为“多”,是因为数据存在于多个数据帧中。

示例:

帧 0

类型: Time
名称: T
标签: nil
类型: Number
名称: cpu
标签: {"host": "a"}
2022-04-27 5:001
2022-04-27 6:004
2022-04-27 7:002
2022-04-27 8:003

帧 1

类型: Time
名称: T
标签: nil
类型: Number
名称: cpu
标签: {"host": "b"}
2022-04-27 5:006
2022-04-27 6:008
2022-04-27 7:005
2022-04-27 8:009

应具备以下属性:(另见共享属性)

  • 每个帧应至少包含一个时间列和一个数值列。此类型的每个字段的第一次出现用于该系列。
  • 不同的帧可以有不同的字段长度(但在同一帧内,它们必须长度相同)
  • 每个时间字段不应包含重复值(重复时间戳)

剩余数据

  • 每个帧中第一个数值或时间字段之后的任何字段
  • 任何没有类型声明或声明不同的额外帧
  • 帧中的任何字符串字段

注意

  • Go 示例请参见此处
  • 多格式是唯一可以在不操作数据的情况下从其他格式转换而来的格式。因此,它是一种可以包含所有其他类型系列信息的类型。

时间序列长格式 (TimeSeriesLong)[类似 SQL]

版本: 0.1

这是 SQL 类系统中常见的响应格式。2 有关更简单(但不完整)的示例,请参见Grafana 文档:表格式中的多个维度。它目前作为后端某些查询 SQL 类数据的数据源中的一种数据转换而存在3,有关该代码如何工作,请参见此 Go 示例

之所以称为“长”格式,是因为它比“宽”格式需要更多的行来存储相同的系列,因此它会变得更

示例

类型: Time
名称: T
标签: nil
类型: String
名称: host
标签: nil
类型: Number
名称: cpu
标签: nil
2022-04-27 5:00a1
2022-04-27 5:00b6
2022-04-27 6:00a4
2022-04-27 6:00b8
2022-04-27 7:00a2
2022-04-27 7:00b5
2022-04-27 8:00a3
2022-04-27 8:00b9

应具备以下属性:(另见共享属性):

  • 第一个时间字段用作时间戳
  • 时间字段可以包含重复时间戳(但必须按时间升序排序)
  • 可以可选地存在字符串字段。对于每个字符串字段
    • 列/字段名称是维度(例如“label”)名称
    • 该字段中对应的字符串值(按行)是标签值
  • 通过遍历数据帧表响应的行来构建系列。
  • 任何值字段/列的名称成为每个系列的名称
  • 不使用字段的 labels 属性

剩余数据

  • 第一个之后的任何额外时间字段
  • 任何没有类型声明或声明不同的额外帧

附加属性或注意事项

  • 在此格式中,完整的维度(例如“host”=值)是从字段中的值中提取的,而不是像其他格式那样在字段 schema 中声明。
  • 由于维度表示在所有派生系列都存在的字段中,因此不能包含混合维度键,所有系列将拥有相同的维度键集合。例如,不能同时拥有 net.bytes{host="a"}和 net.bytes{host="a",int="eth0"}一起 - 第一个必须变为 net.bytes{host="a",int=""}
  • 目前不清楚布尔类型的字段应被视为值字段(例如,上/下指标)还是维度(概念上会被视为标签)

时间序列格式之间的转换

目标修改数据属性和注意
*
  • 一个帧到多个帧
  • 将宽格式帧中的每个值(数值)字段连同时间字段的副本转换为多格式时,会移至其自己的独立帧中
  • 多个帧到一个帧
  • 必须执行合并操作:多格式中所有帧的所有时间字段必须合并为宽格式帧中的一个时间字段
  • 每个值字段被移入宽格式帧
  • 所有值(数值)字段必须与宽格式帧中的时间字段长度相同,因此可能需要用零值(可能是 null)填充值字段,从而有效地创建以前可能不存在的数据点
  • 一个帧到一个帧
  • 标签从值字段中提取,并成为字符串字段,其名称与所有标签中找到的所有键匹配。标签值成为相应字段的字段值。
  • 由于字符串字段将存在,一个系列存在的标签/维度键必须存在于所有系列中,因此系列可能会被修改,创建原本不存在的标签键(值可能为 null)。这实际上可能会创建原本不存在的系列
  • 一个帧到一个帧
  • 字符串字段成为值(数值)字段上的标签(标签键来自字段名称,标签值来自字段的值)。
  • 长格式帧中单个时间字段中的重复时间戳在宽格式帧的时间字段中去重
  • 由于长格式数据中可能缺少行(时间戳),可能需要在系列中插入 null 以使它们共享相同的时间字段(这是宽格式的一个属性)
  • 一个帧到多个帧(每帧一个系列)
  • 当该时间匹配到一个系列时,会构建每个帧的时间字段
  • 多格式帧中的标签成为长格式帧中的字符串字段,并且字符串列存在于长格式帧的所有行中。因此,标签键可能会被添加到系列中

注意

*在时间序列格式中,只有转换为“多”格式时,底层时间序列数据才不会被操作

实践中,我还没有看到转换为长格式的情况,只看到读入的情况。也许在某个时候可能会被请求作为导出格式,但目前这主要是为了说明目的。

这被 SQL 数据源用于从“长”格式中提取时间序列(通过 go sdk/data pkg)。回想起来,我有点希望我们当初选择了 LongToMany。请参见此问题评论中的“相关内容”。


  1. 这是因为排序通常会消耗大量资源,并且在大多数情况下最好由数据源背后的数据库完成。
  2. 我不认为我们当前的 SQL 数据源严格遵循此规则,但一些 Azure 数据源遵循。这要么是由于对这种格式的意图以及升级到 Grafana 8 的误解,和/或对破坏性更改的缺乏了解,或两者兼而有之。
  3. 当使用“Format As=Time Series”进行查询时,会发生这种转换。在这种管道阶段进行转换的问题在于,虽然它为用户提供了表格式的常见时间序列数据,但它使得数据的“表格视图”与查询返回的 SQL 数据不一致。TODO: 稍后定义这个通用概念,也许称之为“所见所得”,“数据沟通不畅”,或其他名称。这意味着我们要么需要返回两样东西(有点像 exemplars?),要么应该移动操作,或者采取其他措施。