Flux 初学者面临的 5 大障碍以及学习使用 Flux 的资源
作者:Anais Dotis-Georgiou / 产品, 用例, 开发者
2020 年 12 月 16 日
导航至
您是 InfluxDB v2.0 和 Flux 的新手吗?您是否对学习新的时间序列脚本和查询语言感到畏惧?也许您是 InfluxDB v1.x 用户,并且熟悉 InfluxQL,但您不相信学习 Flux 是值得的?或者您是否错过了在 InfluxDays 北美 2020 上的 Flux 初学者面临的 10 大障碍 会议?
本系列博客分为两部分,旨在展示 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 方面
- 使用管道转发 (|>) 运算符对您的数据应用额外的 Data Transformation。
- 使用 range() 函数从该存储桶中查询特定时间的数据。
- 使用 filter() 函数来筛选特定的 measurement、字段和标签。
障碍 1:忽略有助于编写 Flux 的 UI 工具
这个障碍很重要,因为它突出了使用 InfluxDB UI 的优势,并且解决方案公开了 UI 中您可能没有意识到的工具。本文的其余部分将多次引用此障碍,我鼓励您在尝试学习 Flux 时始终将 UI 放在首位。
解决方案 1:使用 InfluxDB UI
InfluxDB v2.0 是一款成熟且统一的产品。要学习如何编写 Flux,您真的需要学习如何使用 InfluxDB 原生 UI,反之亦然。UI 旨在使您无需编写任何 Flux 即可创建 Flux 脚本、任务和警报。InfluxDB UI 中最有助于学习如何编写 Flux 脚本的部分是数据浏览器。在数据浏览器中,您可以访问两个 Flux 编写工具:Flux 查询构建器和 Flux 脚本编辑器。
<figcaption> Flux 查询构建器。在右侧面板(粉红色)中应用聚合函数。单击脚本编辑器按钮(橙色)以在 Flux 查询构建器和 Flux 脚本编辑器之间切换。</figcaption>
Flux 查询构建器允许您
- 可视化地创建底层 Flux 脚本
- 从存储桶中选择特定时间范围内的数据
- 对 measurement、标签和字段应用过滤器
- 应用聚合函数
值得注意的是,Flux 查询构建器默认应用聚合函数。应用此聚合是为了减少读取量大的查询的查询时间,并增强查询的可视化效果。当查询大量时间序列数据时,看到数据集的平滑版本(准确概括了数据的总体趋势)通常比原始数据更有帮助。聚合提供了前者。话虽如此,用户并不总是意识到右侧面板正在应用聚合函数。如果您想查看原始数据,请切换到 Flux 查询构建器并从脚本中删除聚合函数。
<figcaption> Flux 脚本编辑器。结果是 Flux 查询构建器生成的输出的相应 Flux 脚本。在右侧面板(粉红色)中搜索和注入函数。“注入”按钮注入示例(绿色)。默认情况下,aggregateWindow() 函数应用于您的数据与查询构建器(蓝色)。</figcaption>
Flux 脚本编辑器允许您
- 查看 Flux 查询构建器中您的选择生成的 Flux 脚本
- 编写您自己的 Flux 脚本
- 筛选 Flux 函数、将函数注入到您的 Flux 脚本中以及查看有关这些函数的应用内文档
我几乎从不手动编写 Flux。相反,我筛选出我想使用的函数,注入它,然后根据需要进行更改。我鼓励您尝试相同的方法。
障碍 2:误解注释 CSV,有时编写不正确
理解 注释 CSV(InfluxDB v2.0 的输出格式)并非易事。要理解 InfluxDB v2.0 中数据的结构方式可能很棘手。我也承认,如果您来自 InfluxQL 或 SQL,则尤其如此。但是,一旦您了解 InfluxDB 的输出格式以及 Flux 如何在注释 CSV 上运行,那么 Flux 就会真正开始点击。在您跨过这座桥梁之后,我真诚地相信您会对您最终到达的地方感到满意。每次我尝试使用 InfluxQL 编写复杂查询时,我都会感到沮丧。与 Flux 相比,InfluxQL 子查询难以阅读和编写。请查看 从子查询到 Flux 以获得一些证据。
解决方案 2:理解注释 CSV 并使用 array.from() 函数、to() 函数和 Flux 仓库中的 universe 目录
要详细了解注释 CSV,请参阅 如何解释注释 CSV。但是,让我们在这里简要回顾一下。假设我们的 模式 如下所示
然后我们可以使用以下 Flux 查询来查找 Shelter 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
带有井号的前三行是注释。注释包含有关我们时间序列的元数据。我建议关注前两个注释。第一个注释,即组注释,描述了哪些列是组键的一部分。当该列中的所有行都相同时,该列是组键的一部分。在我们上面的示例查询中,我们按单个字段类型“adult”;单个“shelter”标签“A”;和单个“type”标签“calico”进行了筛选。这些值在各行中是恒定的,因此这些列设置为 true。因此,它们也是组键的一部分。
理解注释 CSV 使您能够更深入地了解 Flux 如何转换您的数据。使用原始数据视图在您的可视化和注释 CSV 输出之间切换。有些人更喜欢查看表格可视化来调试他们的 Flux。这两种视图对于理解 Flux 都有帮助。我认为在表格可视化和原始数据视图之间的偏好在很大程度上是个人喜好。但是,我更喜欢检查原始数据视图来调试我的 Flux 脚本。
<figcaption> 原始数据视图。两个表都在一个窗格中可视化。</figcaption>
我更喜欢原始数据视图,因为它将所有表格合并到一个面板中。相比之下,您必须在表格可视化中在表格之间单击才能查看您的所有数据。
<figcaption> 表格可视化。表格是分开的。通过在左侧面板中选择一个表格来搜索表格并在表格之间切换。</figcaption>
此外,我发现 原始数据视图中注释 CSV 中的元数据非常有用。例如,您可能需要确保在对特定列执行数学运算时数据类型正确。此外,组键注释使您可以一目了然地了解数据的形状。我发现单窗格视图比交叉比较大量表格集合的形状和内容更容易使用。
array.from() 和 to() 函数是您的 Flux 工具箱中必不可少的工具。有了它们,您可以轻松地将数据写入 InfluxDB。我发现使用 array.from() 创建虚拟数据,然后在其上测试 Flux 函数是了解 Flux 工作原理的好方法。您可以手动编写 注释 CSV,但是那样很容易犯错误。array.from() 函数肯定会为您节省大量时间。
有关如何使用 array.from() 的更多信息,请参阅 如何使用 Flux 构建表格。
最后,我想向您介绍 universe 目录。在此目录中,您将找到每个 Flux 函数的测试,其中包含在应用 Flux 函数之前 (inData
) 和之后 (outData
) 的数据。
<figcaption> Flux 存储库中 universe 目录中 Flux 测试的示例,其中包含注释 CSV。</figcaption>
这个资源非常宝贵,因为它使您能够深入了解 Flux 如何转换注释 CSV。从测试中复制注释 CSV,并使用 csv.from() 函数将其写入您的 InfluxDB 实例,以便亲自试用函数。
障碍 3:数据布局设计导致失控的基数和缓慢的 Flux 查询
序列基数是存储桶中唯一 measurement、标签集、字段键组合的数量。最小化序列基数对于提高查询性能和降低 InfluxDB 实例的成本非常重要。避免失控基数或快速增长基数的最快方法是确保您的初始架构设计良好。解决方案 3 将引导您概述如何设计良好的架构。
解决方案 3:牢记一般数据布局建议
要详细了解数据布局建议,请参阅 数据布局和模式设计最佳实践。一般来说,最好遵循以下指南
- 在标签中编码元数据。Measurement 和标签被索引,而字段值则未被索引。常用的查询元数据应存储在标签中。
- 限制序列数量或尝试减少序列基数。
- 保持存储桶和 measurement 名称简短明了。
- 避免在 measurement 名称中编码数据。
- 当您需要为该数据分配不同的保留策略或需要身份验证令牌时,将数据分隔到不同的存储桶中。
遵循这些指南将帮助您控制基数并提高您的 Flux 查询 性能。此外,如果可以,请尝试预测常用查询,并让该考虑因素影响您的模式设计。如果您知道您将经常查询数据的较大子部分,您可能需要为该数据分配一个标签。
要详细了解如何使用 Flux 解决失控基数,请参阅 监控任务并查找失控基数的来源 和 使用 InfluxDB 时解决失控序列基数。
顺便说一句,如果您有兴趣了解为什么基数不会成为未来 InfluxDB 用户关注的问题,我鼓励您阅读 宣布 InfluxDB IOx – 使用 Rust 和 Arrow 构建的 InfluxDB 未来核心 并观看第一个 InfluxDB IOx 技术讲座视频。
障碍 4:在 InfluxDB 中存储错误的数据并过度使用源函数
障碍 3 与障碍 4 相关。管理序列基数的最佳方法之一是确保您在 InfluxDB 中存储正确类型的数据。想象一下,您正在使用 InfluxDB 为工业物联网用例编写传感器数据。也许您正在使用 InfluxDB 进行预测性维护。您预测某个流量计需要更换。您有很多关于流量计的附加信息,包括购买日期、购买 ID、制造商、保修信息、项目 ID 等。一些用户可能倾向于将该数据作为标签包含在内并将其写入 InfluxDB – 不要这样做。
解决方案 4:根据需要使用源函数将相关数据拉入 InfluxDB
与其将该类型的描述性数据作为标签包含在内,并将此数据与每个流速 measurement 冗余地写入 InfluxDB,不如将此关系数据存储在您选择的 RDBMS 中。使用 源函数 将相关数据临时拉入您的 InfluxDB 实例——例如,在预测需要更换流量计后拉入流量计的项目 ID。InfluxDB 允许您从各种来源(如 PostgreSQL、MySQL、Snowflake、SQLite、SQL Server、Athena 和 BigQuery)拉入数据。
障碍 5:不清楚如何跨字段求和以及投影多个聚合
此障碍与障碍 1 和障碍 2 密切相关。您需要使用 InfluxDB UI 可视化多个聚合,并且您需要充分理解注释 CSV 才能正确分组数据以跨字段求和。
解决方案 5:使用 UI 投影多个聚合和跨字段求和
在投影 InfluxDB 中的多个聚合时,您有几个选项。最简单的方法是使用 UI 为每个聚合创建多个选项卡。想象一下,我们想要同时可视化特定字段的计数和平均值。每个选项卡将分别包含平均值和计数的查询。
让我们通过首先将字段求和来进一步解决这个多聚合投影障碍。想象一下,我们有 4 个字段
- 供电量,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")
<figcaption> 应用任何 Flux 之前的数据图形可视化</figcaption>
首先,我们在一个选项卡中筛选两个字段。原始数据视图显示我们的数据按字段分隔到不同的表格中。
<figcaption> 应用过滤器后能源数据的原始数据视图 </figcaption>
我们必须首先按时间对数据进行分组,这样当我们应用 sum() 函数时,我们会在该单个时间戳上将字段加总在一起。请注意,现在两个字段“ED1”和“ED2”在同一个表中。表共享相同的时间戳。
<figcaption> 应用过滤器和按时间分组后能源数据的原始数据视图 </figcaption>
对于 InfluxQL 用户或 SQL 用户来说,此步骤有点违反直觉。仔细查看带注释的 CSV 解释了为什么按时间分组是必要的。 相比之下,如果我们不按时间分组,那么我们希望获得整个范围内每个字段的 sum() 。在查询末尾应用一个空的 group() 实际上会取消对数据的分组。这将确保我们的数据包含在一个表中,而不是多个表,每个时间戳一个表。 最终的取消分组将我们的可视化效果从由多种颜色表示的多个数据点更改为由一种颜色表示的一条线。
<figcaption> 应用过滤器、按时间分组、求和和取消分组后能源数据的原始数据视图 </figcaption>
现在只需打开一个新标签页,并对剩余的“ES1”和“ES2”字段执行类似的查询,即可同时可视化两个能源总量。
最后,更习惯于在一个行中跨单独的列显示字段的 InfluxQL 和 SQL 用户可能会发现 fieldsAsCols() 函数很有用。 此函数会透视您的数据。 实际上,整个 Schema 包都很有帮助。
您还可以使用 Flux 重组数据以执行多个聚合函数。 请参阅 使用 InfluxDB v2.0 进行降采样 中的“题外话:Flux 灵活性”部分,了解如何使用 union()、join() 或 reduce() 来使用 Flux 执行多个聚合。
Flux 初学者应对 Flux 障碍的后续步骤
我希望这篇文章能帮助您解决作为 Flux 初学者遇到的 Flux 问题。 如果您计划学习 Flux,请向我们寻求帮助并分享您的故事! 在评论区、我们的 社区网站 或我们的 Slack 频道中分享您的想法、疑虑或问题。 我们很乐意获得您的反馈并帮助您解决遇到的任何问题! 您的好问题是这些帖子的灵感来源。 一旦您准备就绪并认为自己是 Flux 中级用户,请务必阅读本系列中的下一篇文章:中级 Flux 用户面临的 5 大障碍以及优化 Flux 的资源。