跳转到主要内容

时间序列类型格式

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

  • 帧应该按时间列/字段的升序排序1
  • 时间字段
    • 不应有空值
    • 字段名称仅用于显示,不应有标签
    • 对于每个帧,第一个时间字段之后的所有额外时间字段被视为剩余数据
  • 值字段
    • 值字段被称为这样,因为它是每个数据点(时间,值)的值所在的位置的字段。
    • 它可以是数值或布尔字段。对于数值
      • Go: Float64,*Float64,或 Int64 等
      • 在 JS 中 'number'
    • 系列名称来自值字段的名称属性

无效情况

  • 没有至少一个时间字段和一个值字段(除非是单个帧“无数据”情况)
  • 存在“无数据”情况(没有字段的帧)与数据并存
  • 可能是警告而不是错误
    • 重复项(通过名称+维度识别)
    • 未排序(时间不是从旧到新排序)

时间序列宽格式(TimeSeriesWide)

版本:0.1

宽格式在单个帧中有多个时间序列,它们共享相同的时间字段。它被称为“宽”,因为随着更多系列的增加,它变得更宽

示例

类型:时间
名称:T
标签:nil
类型:数字
名称:cpu
标签:{"host": "a"}
类型:数字
名称:cpu
标签:{"host": "b"}
2022-04-27 5:0016
2022-04-27 6:0048
2022-04-27 7:0025
2022-04-27 8:0039

它应该有以下属性:(也请参阅共享属性)

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

剩余数据

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

注释

  • Go 的一个示例可以在这里找到:这里

时间序列多格式(TimeSeriesMulti)

版本:0.1

TimeSeriesMulti 格式每个帧有一个时间序列。如果响应有多个系列,其中时间值可能不一致,则必须使用此格式而不是 TimeSeriesWide。该格式称为“多”,因为数据存在于多个数据帧中。

示例:

帧 0

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

帧 1

类型:时间
名称:T
标签:nil
类型:数字
名称: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-like]

版本:0.1

这是SQL-like系统中常见的响应格式2。请参阅Grafana 文档:表格格式中的多个维度以获取一些更简单的(但不是完整的)示例。它目前作为后端某些数据源中查询SQL-like数据的内部数据转换而存在3,请参阅此Go示例以了解该代码的工作原理

该格式被称为“长”格式,因为与“宽”格式相比,需要更多的行来存储相同的系列,因此它变得更长。

示例

类型:时间
名称:T
标签:nil
类型:字符串
名称:host
标签:nil
类型:数字
名称: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

它应具有以下属性:(也请参阅共享属性):

  • 第一个时间字段用作时间戳
  • 时间字段可以有不同的时间戳(但必须按升序排序)
  • 可能有字符串字段。对于每个字符串字段
    • 列/字段名称是维度(例如,“标签”)名称
    • 该字段中对应的字符串值(按行)是标签值
  • 通过迭代数据帧表响应的行来构建系列。
  • 任何值字段/列的名称成为每个系列名称
  • 字段标签属性未使用

剩余数据

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

附加属性或注意事项

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

时间序列格式之间的转换

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

注释

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

实际上,我还没有看到将数据转换为长格式的案例,只是读取它。也许将来某天可能会将其作为导出格式请求,但基本上目前这是用于说明的。

这是由SQL数据源用于从“长”格式中提取时间序列(通过go sdk/data包)使用的。事后看来,我有点希望我们选择了LongToMany。请参阅“相关”部分此问题评论


  1. 这是因为排序通常在资源方面代价高昂,并且在大多数情况下最好由数据源背后的数据库完成。
  2. 我不认为我们当前的SQL数据源严格遵循这一点,但某些Azure数据源确实如此。这可能是由于关于此格式意图的误解以及Grafana 8的升级和/或对破坏性变化的缺乏理解,或者两者兼而有之。
  3. 在查询时选择“格式为时间序列”时发生这种转换。在管道的这个阶段发生转换的问题在于,虽然它确实以表格格式提供了用户的“时间序列”,但它使得数据的“表格视图”与查询的SQL返回值不匹配。待办事项:稍后定义这个通用概念,可能称之为“所见非所得”,“数据误解”,等等。这意味着我们可能需要返回两件事(有点像示例?),或者操作应该移动,或者做其他事情。