如何使用 influxdb non_negative_derivative 获得一致的值?

How do I get consistent values with influxdb non_negative_derivative?

将 grafana 与 influxdb 结合使用,我试图显示作为计数器的某个值的每秒速率。如果我使用 non_negative_derivative(1s) 函数,则速率的值似乎会根据 grafana 视图的时间宽度发生显着变化。我正在使用 last 选择器(但也可以使用 max,因为它是一个计数器,所以值相同)。

具体来说,我正在使用:

SELECT non_negative_derivative(last("my_counter"), 1s) FROM ...

根据 influxdb docs non-negative-derivative:

InfluxDB calculates the difference between chronological field values and converts those results into the rate of change per unit.

所以对我来说,这意味着在扩展时间视图时,给定点的值不应该改变那么多,因为该值应该是单位变化率( 1s 在我上面的示例查询中)。

在石墨中,它们具有特定的 perSecond 功能,效果更好:

perSecond(consolidateBy(my_counter, 'max'))

关于我在上面的涌入查询中做错了什么有什么想法吗?

如果您希望每秒的结果不变,您需要 GROUP BY time(1s)。这将为您提供准确的 perSecond 结果。

考虑以下示例:

假设计数器的值每一秒都这样变化

0s → 1s → 2s → 3s → 4s
1  → 2  → 5  → 8  → 11

根据我们如何对上面的序列进行分组,我们会看到不同的结果。

考虑我们将事物分组到 2s 个桶中的情况。

 0s-2s   →    2s-4s
(5-1)/2  →  (11-5)/2
   2     →      3

1s

 0s-1s  →  1s-2s  →  2s-3s  →  3s-4s
(2-1)/1 → (5-2)/1 → (8-5)/1 → (11-8)/1
   1    →    3    →    3    →    3

寻址

So to me, that means that the value at a given point should not change that much when expanding the time view, since the value should be rate of change per unit (1s in my example query above).

rate of change per unit 是归一化因子,与 GROUP BY 时间单位无关。当我们将导数区间更改为 2s 时,解释我们之前的示例可能会提供一些见解。

确切的等式是

∆y/(∆x/tu)

考虑我们将事物分组到 1s 个桶中的情况,导数间隔为 2s。我们应该看到的结果是

 0s-1s    →  1s-2s    →  2s-3s    →  3s-4s
2*(2-1)/1 → 2*(5-2)/1 → 2*(8-5)/1 → (11-8)/1
   2      →    6      →    6      →    6

这可能看起来有点奇怪,但如果您考虑一下它的意思,它应该是有道理的。当我们指定 2s 的导数区间时,我们要求的是 2s 变化率对于 1s GROUP BY 桶。

如果我们对 2s 个具有导数区间 2s 的桶应用类似的推理,那么

 0s-2s     →    2s-4s
2*(5-1)/2  →  2*(11-5)/2
   4       →      6

我们在这里要求的是 2s 变化率对于 2s GROUP BY 桶以及在第一个间隔中的 2s 变化率变化将是 4,第二个间隔 2s 变化率将是 6.

@Michael-Desa 给出了很好的解释。

我想用我们公司感兴趣的一个非常常见的指标的解决方案来补充这个答案:"What is the maximum "每秒操作“特定测量字段上的值?”。

我将使用我们公司的真实示例。

场景背景

我们将大量数据从 RDBMS 发送到 redis。传输该数据时,我们会跟踪 5 个计数器:

  1. TipTrgUp -> 通过业务触发器更新(存储过程)
  2. TipTrgRm -> 通过业务触发器(存储过程)删除
  3. TipRprUp -> 通过无人值守的自动修复批处理进行更新
  4. TipRprRm -> 通过无人值守的自动修复批处理删除
  5. TipDmpUp -> 通过批量转储进程更新

我们制作了一个指标收集器,将这些计数器的当前状态发送到 InfluxDB,间隔为 1 秒(可配置)。

Grafana 图 1:低分辨率,没有真正的最大操作数

这是有用的 grafana 查询,但在缩小时不会显示真正的最大操作数(我们知道在正常工作日,没有特殊转储或维护时它将达到大约 500 次操作数- 否则它会进入数千):

SELECT
    non_negative_derivative(max(TipTrgUp),1s) AS "update/TipTrgUp"
   ,non_negative_derivative(max(TipTrgRm),1s) AS "remove/TipTrgRm"
   ,non_negative_derivative(max(TipRprUp),1s) AS "autorepair-up/TipRprUp"
   ,non_negative_derivative(max(TipRprRm),1s) AS "autorepair-rm/TipRprRm"
   ,non_negative_derivative(max(TipDmpUp),1s) AS "dump/TipDmpUp"
FROM "$rp"."redis_flux_-transid-d-s"
WHERE
    host =~ /$server$/
    AND $timeFilter
GROUP BY time($interval),* fill(null)

旁注:$rp 是保留策略的名称,在 grafana 中模板化。我们使用 CQ 对持续时间更长的保留策略进行下采样。另请注意 1s 作为导数参数:它是必需的,因为使用 GROUP BY 时默认值不同。这在 InfluxDB 文档中很容易被忽略。

24 小时看到的图表如下所示:

如果我们简单地使用 1s 的分辨率(如@Michael-Desa 所建议的),大量的数据会从 influxdb 传输到客户端。它工作得相当好(大约 10 秒),但对我们来说太慢了。

Grafana 图 2:低分辨率和高分辨率,真正的最大操作数,性能较慢

然而,我们可以使用 subqueries 将真正的 maxops 添加到该图中,这是一个轻微的改进。传输到客户端的数据少了很多,但 InfluxDB 服务器必须做大量的数字运算。系列 B(在别名中添加 maxops):

SELECT
    max(subTipTrgUp) AS maxopsTipTrgUp
   ,max(subTipTrgRm) AS maxopsTipTrgRm
   ,max(subTipRprUp) AS maxopsRprUp
   ,max(subTipRprRm) AS maxopsTipRprRm
   ,max(subTipDmpUp) AS maxopsTipDmpUp
FROM (
    SELECT
        non_negative_derivative(max(TipTrgUp),1s) AS subTipTrgUp
       ,non_negative_derivative(max(TipTrgRm),1s) AS subTipTrgRm
       ,non_negative_derivative(max(TipRprUp),1s) AS subTipRprUp
       ,non_negative_derivative(max(TipRprRm),1s) AS subTipRprRm
       ,non_negative_derivative(max(TipDmpUp),1s) AS subTipDmpUp
    FROM "$rp"."redis_flux_-transid-d-s"
    WHERE
        host =~ /$server$/
        AND $timeFilter
    GROUP BY time(1s),* fill(null)
)
WHERE $timeFilter
GROUP BY time($interval),* fill(null)

给出:

Grafana 图 3:低分辨率和高分辨率,真正的最大操作数,高性能,由 CQ 预先计算

我们对这类指标的最终解决方案(但仅当我们需要实时视图时,子查询方法适用于临时图)是:使用连续查询来预先计算真正的最大操作数。我们这样生成 CQ:

CREATE CONTINUOUS QUERY "redis_flux_-transid-d-s.maxops.1s"
ON telegraf
BEGIN
    SELECT
        non_negative_derivative(max(TipTrgUp),1s) AS TipTrgUp
       ,non_negative_derivative(max(TipTrgRm),1s) AS TipTrgRm
       ,non_negative_derivative(max(TipRprUp),1s) AS TipRprUp
       ,non_negative_derivative(max(TipRprRm),1s) AS TipRprRm
       ,non_negative_derivative(max(TipDmpUp),1s) AS TipDmpUp
    INTO telegraf.A."redis_flux_-transid-d-s.maxops"
    FROM telegraf.A."redis_flux_-transid-d-s"
    GROUP BY time(1s),*
END

从现在开始,在 grafana 中使用这些 maxops 测量变得微不足道。当下采样到保留时间更长的 RP 时,我们再次使用 max() 作为选择器函数。

B 系列(在别名中附加 .maxops

SELECT
    max(TipTrgUp) AS "update/TipTrgUp.maxops"
   ,max(TipTrgRm) AS "remove/TipTrgRm.maxops"
   ,max(TipRprUp) as "autorepair-up/TipRprUp.maxops"
   ,max(TipRprRm) as "autorepair-rm/TipRprRm.maxops"
   ,max(TipDmpUp) as "dump/TipDmpUp.maxops"
FROM "$rp"."redis_flux_-transid-d-s.maxops"
WHERE
    host =~ /$server$/
    AND $timeFilter
GROUP BY time($interval),* fill(null)

给出:

当放大到 1s 精度时,您可以看到图形变得完全相同:

希望这对台湾有所帮助

这里的问题是 $__interval 宽度会根据您在 Grafana 中查看的时间范围而变化。

获得一致结果的方法是从每个间隔中取样(mean()median()max() 都同样有效),然后通过 derivative($__interval)。这样,您的导数会随着缩放 in/out.

的变化而变化以匹配您的间隔长度

因此,您的查询可能如下所示:

SELECT derivative(mean("mem.gc.count"), $__interval) FROM "influxdb"
WHERE $timeFilter GROUP BY time($__interval) fill(null)