Flux 数据透视指南:使用列式数据
作者:Michael Hall / 产品, 用例, 开发者
2021 年 10 月 12 日
导航至
关系数据库是目前最常见的数据库类型,作为软件开发者,可以肯定地说,它们是我们大多数人入门的数据库类型,并且可能仍然经常使用。它们都有一个共同点,那就是数据结构的方式。然而,InfluxDB 的数据结构方式略有不同。
RDBMS 默认表结构
使用关系数据库,您会有一系列表,每个表都有自己固定的列布局,并且您以行的形式读取和写入数据。每一行都有一列,用于保存每个字段的值,您可以同时获得所有这些值,从而得到如下所示的表
时间 | 字段 1 | 字段 2 | 主机 |
<data_tstamp> | 123 | 789 | myhost1 |
<data_tstamp> | 456 | 101 | myhost2 |
InfluxDB 结果数据
InfluxDB 不遵循此模式。它以所谓的列式格式返回数据,这意味着您将一次获得一列数据,而不是一次获得一行数据。对于习惯于关系数据库的行格式的开发者来说,这通常是困惑的根源,但是当处理时间序列数据时,它有很多好处。
当您运行如下所示的基本 Flux 查询时
from(bucket: "telegraf")
|> range(start: -1d)
您将获得如下所示的返回数据
表 | _measurement* | _field* | _value | _start* | _stop* | _time | host* |
0 | mydata | 字段 1 | 123 | <query_start> | <query_stop> | <data_tstamp> | myhost1 |
0 | mydata | 字段 1 | 456 | <query_start> | <query_stop> | <data_tstamp> | myhost2 |
1 | mydata | 字段 2 | 789 | <query_start> | <query_stop> | <data_tstamp> | myhost1 |
1 | mydata | 字段 2 | 101 | <query_start> | <query_stop> | <data_tstamp> | myhost2 |
您首先会注意到,表的列标题不是您的字段名称,就像查询 SQL 数据库一样。相反,您的查询将返回具有这些相同列的结果,并且结果中的每个记录(在下面的查询中由 r
表示)将代表该时间点的单个字段值。
这就是为什么当您过滤 Flux 查询时,它看起来像这样
|> filter(fn: (r) => r["_measurement"] == "mydata")
|> filter(fn: (r) => r["_field"] == "field1")
这种数据结构允许在 InfluxDB 最常见的使用模式中进行更快的查询和聚合。由于 InfluxDB 一次处理一列的值,因此它可以仅对该字段的数据执行计算,而不是同时执行多个计算。
在列式和行式数据布局之间透视
然而,有时您会希望您的数据看起来更像传统数据库查询的结果。为此,您需要执行“透视”操作。透视数据是指将其从字段位于单独的行中翻转为字段位于单独的列中。
Flux 提供了一个 pivot
函数,它正是执行此操作
|> pivot(rowKey:["_time"], columnKey: ["_field"], valueColumn: "_value")
此函数将翻转您的数据,以便每行代表一个时间点 (rowKey:["_time"]
),您的字段将各自成为一列 (columnKey: ["_field"]
),并且每列中的值将是您的字段值 (valueColumn: "_value"
)。为您提供如下所示的结果
表 | _measurement* | _start* | _stop* | _time | host* | 字段 1 | 字段 2 |
0 | mydata | <query_start> | <query_stop> | <data_tstamp> | myhost1 | 123 | 789 |
0 | mydata | <query_start> | <query_stop> | <data_tstamp> | myhost2 | 456 | 101 |
使用 fieldsAsCols 代替
这是人们希望透视数据的最常见方式,因为它将其放入与他们习惯于从传统数据库中使用的相同的形状中。事实上,它非常常见,以至于我们在 schema
包中提供了一个快捷函数,专门用于此用例。
在将 schema
导入您的 Flux 脚本后,上面的代码行可以用以下代码替换:。
Flux
|> fieldsAsCols()
其他透视方式
标签作为列
即使它是最常见的透视方式,字段也不是您可以翻转到列标题中的唯一内容。假设您想要比较的不是不同字段的值,而是来自不同来源的同一字段的值。例如,如果您想比较来自同一时间的不同服务器主机的读数。
在我们之前的示例中,我们的数据有一个名为 host
的标签,其中包含此值。
表 | _measurement* | _field* | _value | _start* | _stop* | _time | host* |
0 | mydata | 字段 1 | 123 | <query_start> | <query_stop> | <data_tstamp> | myhost1 |
0 | mydata | 字段 1 | 456 | <query_start> | <query_stop> | <data_tstamp> | myhost2 |
1 | mydata | 字段 2 | 789 | <query_start> | <query_stop> | <data_tstamp> | myhost1 |
1 | mydata | 字段 2 | 101 | <query_start> | <query_stop> | <data_tstamp> | myhost2 |
如果我们想将来自我们所有主机的每个字段的值组合到单个记录中,我们可以使用
|> group()
|> pivot(rowKey:["_time", "_field"], columnKey: ["host"], valueColumn: "_value")
第一行重新分组了我们的数据,因为默认情况下,InfluxDB 会将来自不同主机(或任何标签值差异)的值放入单独的结果表中。由于 pivot
一次处理一个结果表,因此我们必须首先调用 group
将来自两个主机的值放入单个表中,然后我们可以围绕 host
值透视该组合表。
此查询为我们提供了一个如下所示的表结构
表 | _field | _start | _stop | _time | myhost1 | myhost2 |
0 | 字段 1 | <query_start> | <query_stop> | <data_tstamp> | 123 | 456 |
0 | 字段 2 | <query_start> | <query_stop> | <data_tstamp> | 789 | 101 |
组合列
您还可以使用 pivot
将字段名称与标签值组合起来。以上面的示例为基础,假设您想在单行中查看每个主机的 field1
和 field2
的值?
我们可以通过对之前的函数调用进行轻微修改来做到这一点
|> group()
|> pivot(rowKey:["_time"], columnKey: ["host", "_field"], valueColumn: "_value")
通过将 _field
数据从 rowKey
移动到 columnKey
,以及 host
数据,我们得到一个将两者组合到唯一列中的表。
表 | _start | _stop | _time | myhost1_field1 | myhost1_field2 | myhost2_field1 | myhost2_field2 |
0 | <query_start> | <query_stop> | <data_tstamp> | 123 | 789 | 456 | 101 |
进一步阅读
- pivot() 函数:关于 pivot() 函数的参考文档。
- schema.fieldsAsCol() 函数:关于 schema 包中 fieldsAsCol() 函数的参考文档。
- group() 函数:关于 group() 函数的参考文档。
- TL;DR InfluxDB 技术技巧:Flux 中使用 yield() 进行多次聚合:这篇文章教您如何使用多个 yield()。
- TL;DR InfluxDB 技术技巧 – 跨标签或字段聚合和取消分组:这篇文章教您如何使用 group() 组合结果表。