Flux 初学者前五大障碍及学习使用 Flux 的资源

导航至

您是 InfluxDB v2.0 和 Flux 的新手吗?学习新的时间序列脚本和查询语言让您感到害怕吗?或许您是 InfluxDB v1.x 的用户,熟悉 InfluxQL,但您不确定学习 Flux 是否值得?或者,您错过了 InfluxDays North America 2020 上的《Flux 初学者前十大障碍》会议?

本系列博客(本篇为第一篇)旨在展示 Flux 的强大功能,并帮助您解决新旧 InfluxDB 用户面临的前五大障碍。

  • 第一篇(您正在阅读的这篇)介绍了 Flux 初学者面临的一些常见障碍。
  • 第二篇(链接)介绍了中级 Flux 用户的一些额外障碍。

在我们深入探讨 Flux 之前,让我们先介绍一下它。Flux 是一种函数式查询和脚本语言,最初从 InfluxDB v1.8+ 开始提供,是 InfluxDB v2.0 和 Cloud 中支持的主要语言。它是开源的,类似于 JavaScript。虽然 v2.0 API 支持通过向后兼容性使用 InfluxQL,但 Flux 在数据处理方面提供的功能非常强大。一个简单的查询可能看起来像这样

from(bucket:"example-bucket")
  |> range(start:-1h)
  |> filter(fn:(r) =>
    r._measurement == "my-measurement" and
    r.my-tag-key == "my-tag-value"
  )

使用 from() 函数从数据桶中查询数据。对于初学者来说,数据桶结合了 InfluxDB v1.x 中的数据库和保留策略的概念。保留策略决定了时间序列数据过期的速度,每个数据桶在 InfluxDB v2.x 中都有一个保留策略。

我喜欢将时间序列数据的实际流想象成流入数据桶。我的数据桶的大小等同于我的保留策略的持续时间。随着时间的推移,数据桶会填满,旧的时间序列数据就会过期。

关于熟悉 Flux

  • 使用管道符号(|>)操作符对数据进行额外的数据转换。
  • 使用range()函数来查询特定时间范围内该桶的数据。
  • 使用filter()函数来过滤特定的测量值、字段和标签。

障碍1:忽视了方便编写Flux的UI工具

这个障碍很重要,因为它突出了使用InfluxDB UI的优势,解决方案揭示了UI中你可能不知道的工具。这个障碍将在本文中多次提及,我鼓励你在学习Flux时将UI放在首位。

解决方案1:使用InfluxDB UI

InfluxDB v2.0是一个成熟且统一的产品。要学习如何编写Flux,你真的需要学习如何使用InfluxDB原生UI,反之亦然。UI被设计成让你能够创建Flux脚本、任务和警报,而不需要自己编写任何Flux。对于学习如何编写Flux脚本来说,InfluxDB UI中最有用的部分是数据探索器。在数据探索器中,你可以访问两个Flux编写工具:Flux查询构建器和Flux脚本编辑器。

Flux-Query-Builder

Flux查询构建器。在右侧面板(粉色)中应用聚合函数。点击脚本编辑器按钮(橙色)来在Flux查询构建器和Flux脚本编辑器之间切换。

Flux查询构建器允许你

  • 可视地创建底层的Flux脚本
  • 从特定时间范围的桶中选择数据
  • 对测量值、标签和字段应用过滤器
  • 应用聚合函数

值得注意的是,Flux查询构建器默认应用聚合函数。这种聚合可以减少大量读取的查询时间,并增强查询的可视化。当查询大量时间序列数据时,通常更倾向于看到准确反映数据一般趋势的平滑数据版本,而不是原始数据。聚合函数提供了前者。话虽如此,用户并不总是意识到右侧面板正在应用聚合函数。如果你想查看原始数据,切换到Flux查询构建器并从你的脚本中删除聚合函数。

Flux-Script-Editor

Flux脚本编辑器。这是Flux查询构建器上方生成的输出对应的Flux脚本。在右侧面板(粉色)中搜索并注入函数。注入按钮将示例(绿色)注入其中。默认情况下,使用查询构建器应用了一个aggregateWindow()函数(蓝色)。

Flux脚本编辑器允许你

  • 查看Flux查询构建器中的选择产生的Flux脚本
  • 编写自己的Flux脚本
  • 过滤Flux函数,将函数注入你的Flux脚本中,并查看有关这些函数的应用内文档

我几乎从不手动编写Flux。相反,我过滤出我想要使用的函数,注入它,并根据需要修改它。我鼓励你也尝试同样的方法。

障碍2:误解了带注释的CSV,有时甚至编写错误

理解InfluxDB v2.0的输出格式——标记CSV,并不是一件简单的事情。要理解InfluxDB v2.0中数据是如何组织的,可能会有些棘手。我首先承认,如果你是从InfluxQL或SQL转换过来的,这一点尤其正确。然而,一旦你理解了InfluxDB的输出格式以及Flux如何在标记CSV上操作,那么Flux真的就会变得容易理解。跨过这道坎后,我真心相信你会对结果感到满意。每次我尝试用InfluxQL编写复杂的查询时,我都会感到沮丧。与Flux相比,InfluxQL子查询难以阅读和编写。请参阅从子查询到Flux,以了解其中的证据。

解决方案2:理解标记CSV并使用Flux仓库中的array.from()函数、to()函数和universe目录

要详细了解标记CSV,请参阅如何解释标记CSV。然而,让我们在这里简要回顾一下。让我们假设我们的模式如下

Annotated-CSVs

然后我们可以使用以下Flux查询来查找庇护所A中成年虎斑猫的数量

from(bucket: "cats-and-dogs")
 |> range(start: 2020-05-15T00:00:00Z, stop: 2020-05-16T00:00:00Z)
 |> filter(fn: (r) => r["_measurement"] == "cats")
 |> filter(fn: (r) => r["_field"] == "adult")
 |> filter(fn: (r) => r["shelter"] == "A")
 |> filter(fn: (r) => r["type"] == "calico")
 |> limit(n:2)

相应的标记CSV将如下所示

#group,false,false,true,true,false,false,true,true,true,true
#datatype,string,long,dateTime:RFC3339,dateTime:RFC3339,dateTime:RFC3339,double,string,string,string,string
#default,_result,,,,,,,,,
,result,table,_start,_stop,_time,_value,_field,_measurement,shelter,type
,,0,2020-05-15T00:00:00Z,2020-05-16T00:00:00Z,2020-05-15T18:50:33.262484Z,8,adult,cats,A,calico
,,0,2020-05-15T00:00:00Z,2020-05-16T00:00:00Z,2020-05-15T18:51:48.852085Z,7,adult,cats,A,calico

前三行带有井号的行是注释。注释包含关于我们的时间序列的元数据。我建议关注前两个注释。第一个注释,分组注释描述了哪些列是分组键的一部分。当某一列的所有行都相同的时候,该列就是分组键的一部分。在我们的上述示例查询中,我们已经通过单个字段类型“成年”;单个“庇护所”标签,“A”;以及单个“类型”标签,“虎斑”进行了筛选。这些值在行之间是恒定的,因此这些列被设置为true。因此,它们也是分组键的一部分。

理解标记CSV可以帮助你更深入地了解Flux是如何转换你的数据的。使用原始数据视图在可视化效果和标记CSV输出之间切换。有些人喜欢用表格可视化来调试Flux。这两种视图对于理解Flux都有帮助。我认为在表格可视化和Raw数据视图之间的偏好很大程度上是个人喜好。然而,我更喜欢检查Raw数据视图来调试我的Flux脚本。

原始数据视图<figcaption> 原始数据视图。两个表都在一个面板中可视化显示.</figcaption>

我喜欢使用原始数据视图,因为它将所有表格整合在一个面板中。相比之下,在表格可视化中,您必须点击多个表格才能查看所有数据。

表格可视化<figcaption> 表格可视化。表格是分开的。通过在左侧面板中选择表格来搜索并切换表格.</figcaption>

此外,我发现原始数据视图中的标记CSV中的元数据极其有用。例如,你可能需要确保在执行某些列的数学运算时拥有正确的数据类型。此外,分组键注释允许你一目了然地了解你的数据形状。我发现单面板视图比跨比较大量表格的形状和内容更容易操作。

在您的 Flux 工具集中,array.from()to() 函数是必不可少的工具。有了它们,您可以轻松地将数据写入 InfluxDB。我发现使用 array.from() 创建模拟数据,然后在该数据上测试 Flux 函数,是理解 Flux 工作原理的绝佳方法。您可以通过手工编写 标记 CSV,但这样做很容易出错。array.from() 函数无疑可以为您节省大量时间。

有关如何使用 array.from() 的更多信息,请参阅 如何使用 Flux 构建表格

最后,我想向您介绍 universe 目录。在这个目录中,您可以找到针对每个 Flux 函数的测试,包括在应用 Flux 函数之前(inData)和之后(outData)的数据。

Flux-test-in-the-universe-directory<figcaption>Flux 仓库中 universe 目录中 Flux 测试的示例,包括标记 CSV</figcaption>

这个资源非常宝贵,因为它允许您深入了解 Flux 如何转换标记 CSV。将测试中的标记 CSV 复制到您的 InfluxDB 实例,并使用 csv.from() 函数来尝试自己编写函数。

障碍 3:数据布局设计导致基数逃逸和缓慢的 Flux 查询

系列基数 是桶中唯一测量、标签集、字段键组合的数量。最小化系列基数对于提高查询性能和减少您的 InfluxDB 实例至关重要。避免基数逃逸,或快速增长的基数的最快方法是确保您的初始架构设计良好。解决方案 3 将向您概述如何设计良好的架构。

解决方案 3:牢记一般数据布局建议

有关数据布局建议的详细信息,请参阅 数据布局和架构设计最佳实践。一般来说,遵守以下指南是个好主意

  • 在标签中编码元数据。测量和标签是索引的,而字段值则不是。常见的查询元数据应存储在标签中。
  • 限制系列数量或尝试减少系列基数。
  • 保持桶和测量名称简短简单。
  • 避免在测量名称中编码数据。
  • 当您需要为数据分配不同的保留策略或需要身份验证令牌时,请将数据分离到不同的桶中。

遵循这些指南将帮助您控制您的基数并提高您的 Flux 查询 性能。此外,如果您能预测常见的查询,并让这种考虑影响您的架构设计,那就更好了。如果您知道您将经常查询大量数据子集,您可能想为此数据分配一个标签。

有关使用 Flux 详细解决基数逃逸的解决方案,请参阅 监控任务和找到基数逃逸的来源 以及 使用 InfluxDB 解决系列基数逃逸

顺便提一下,如果您对为什么基数不会成为未来 InfluxDB 用户关注的焦点感兴趣,我鼓励您阅读 宣布 InfluxDB IOx – 使用 Rust 和 Arrow 构建的未来 InfluxDB 核心技术 并观看第一个 InfluxDB IOx 技术研讨会视频

障碍4:在InfluxDB中存储错误的数据以及未充分利用源函数

障碍3与障碍4相关。管理你的系列基数最佳方法之一是确保你在InfluxDB中存储正确的数据类型。想象一下你正在使用InfluxDB为工业物联网用例写入传感器数据。也许你正在使用InfluxDB进行预测性维护。你预测某个特定的流量计需要更换。关于你的流量计,你有很多额外的信息,包括购买日期、购买ID、制造商、保修信息、项目ID等。一些用户可能会倾向于将此类数据作为标签写入InfluxDB – 不要这样做。

解决方案4:根据需要使用源函数将相关数据拉入InfluxDB

与其将此类描述性数据作为标签并重复地将此类数据与每次流量测量一起写入InfluxDB,不如将此类关系数据存储在你选择的RDBMS中。使用源函数按需将相关数据拉入你的InfluxDB实例 – 例如,在预测需要更换流量计后,拉入流量计的项目ID。InfluxDB允许你从各种来源拉入数据,如PostgreSQL、MySQL、Snowflake、SQLite、SQL Server、Athena和BigQuery。

障碍5:不清楚如何跨字段求和以及进行多个聚合

这个障碍与障碍1和障碍2密切相关。你需要使用InfluxDB UI来可视化多个聚合,你需要很好地理解带注解的CSV,以便正确分组你的数据以进行字段求和。

解决方案5:使用UI进行多个聚合投影以及跨字段求和

在InfluxDB中进行多个聚合投影时,你有几种选择。最简单的是使用UI为每个聚合创建多个标签。想象一下我们想要可视化特定字段的计数和平均值。每个标签将包含一个查询,分别用于平均值和计数。

让我们进一步探讨这个多个聚合投影的障碍,首先对字段进行求和。

  • 供电,ES1
  • 供电 ES2
  • 配送电力,ED1
  • 配送电力,ED2

我们想要同时可视化两个序列:总供电(EST)和总配送电力(EDT),其中EST和EDT分别是ES1 + ES2和ED1 + ED2的总和。我们将使用两个标签同时可视化EST和EDT。为了对两个字段求和,我们将在一个查询标签中执行以下Flux查询

from(bucket: "p1")
  |> range(start: -24h)
  |> filter(fn: (r) => r["_measurement"] == "MyMeasurement")
  |> filter(fn: (r) => r["_field"] == "ED1" or r["_field"] == "ED2")
//group by time to isolate the values you want to sum together
  |> group(columns: ["_time"], mode:"by")
  |> sum(column: "_value")
//to ungroup your data provide a group without any columns
  |> group()
  |> yield(name: "delivered")

Graph-visualization<figcaption> 应用Flux之前我们数据的图形可视化</figcaption>

首先,我们在一个标签中筛选两个字段。在原始数据视图中可以看到,我们的数据按字段分开成单独的表。

Raw Data View <figcaption> 应用过滤器后我们能源数据的原始数据视图</figcaption>

我们必须首先按时间分组我们的数据,这样当应用sum()函数时,我们可以在单个时间戳上对字段进行求和。注意现在“ED1”和“ED2”这两个字段在同一张表中。表格共享相同的时间戳。

Raw-Data-View-of-our-energy-data-after<figcaption> 应用过滤和时间分组后的能源数据原始数据视图</figcaption>

对于 InfluxQL 用户或 SQL 用户来说,这一步可能不太直观。仔细查看标注的 CSV 文件可以解释为什么需要按时间分组。相比之下,如果我们不按时间分组,那么我们预计将得到整个范围每个字段的 求和。在查询末尾应用一个空的 分组 实际上会将我们的数据取消分组。这将确保我们的数据包含在一个表中,而不是多个表,每个时间戳一个表。这种最后的取消分组将使我们的可视化从由多种颜色表示的多个数据点变为由一种颜色表示的一条线。

<figcaption> 应用过滤、按时间分组、求和和取消分组后的能源数据原始数据视图</figcaption>

现在只需打开一个新标签页,并使用剩余的“ES1”和“ES2”字段执行类似的查询,以同时可视化能源总数。

最后,对于那些更习惯于在单独的列中看到字段的 InfluxQL 和 SQL 用户,可能会发现 fieldsAsCols 函数很有用。这个函数会旋转你的数据。实际上,整个 Schema 包都是很有用的。

你还可以使用 Flux 重新结构化数据以执行多个聚合函数。请参考 使用 InfluxDB v2.0 进行下采样 中的“另请参阅:Flux 灵活操作”部分,了解如何使用 unionjoinreduce 在 Flux 中执行多个聚合。

解决 Flux 初学者面临的 Flux 挑战的下一步

希望这篇文章能帮助你解决作为 Flux 初学者所遇到的 Flux 问题。如果你打算学习 Flux,请向我们寻求帮助,并 分享你的故事!在评论部分、我们的 社区网站 或我们的 Slack 频道中分享你的想法、关注点或问题。我们很乐意获得你的反馈并帮助你解决遇到的问题!你的好问题是我们撰写这些文章的灵感来源。一旦你准备好,并认为自己是一个中级 Flux 用户,请务必阅读本系列的下一篇文章: 中级 Flux 用户的前五大挑战以及优化 Flux 的资源