[comment]: # ({c34725dc-f445d963})
# 预测性触发器函数

[comment]: # ({/c34725dc-f445d963})

[comment]: # ({9dac6d6d-9dac6d6d})
#### 概述

有时，问题发生前会出现一些征兆。可以识别这些征兆，以便提前采取措施来预防问题，或至少将问题的影响降到最低。

Zabbix 提供了基于历史数据预测被监控系统未来行为的工具。这些工具通过预测型触发器函数来实现。

[comment]: # ({/9dac6d6d-9dac6d6d})

[comment]: # ({827f1d5f-24b155a7})
#### 函数

在设置触发器之前，必须先定义什么是问题状态，以及需要多少时间来采取行动。然后，有两种方式可以设置一个用于指示潜在异常情况的触发器。第一种：当系统预计会在“采取行动时间”之后处于问题状态时，触发器必须触发。第二种：当系统将在少于“采取行动时间”的时间内达到问题状态时，触发器必须触发。对应可使用的触发器函数是 **forecast** 和 **timeleft**。请注意，这两个函数所基于的统计分析本质上是相同的。你可以根据自己的偏好选择任意一种方式来设置触发器，并获得类似的结果。

[comment]: # ({/827f1d5f-24b155a7})

[comment]: # ({9f0f6aee-c80922f6})
#### 参数

这两个函数使用的参数集几乎相同。请参考[支持的函数](/manual/config/triggers/expression#functions)列表。

[comment]: # ({/9f0f6aee-c80922f6})

[comment]: # ({33e199b9-a93b071e})
##### 时间间隔

首先，您应指定 Zabbix 为得出预测结果而需要分析的历史时间段。您可以像使用 **avg**、**count**、**delta**、**max**、**min** 和 **sum** 函数时一样，通过 `time period` 参数以及可选的时间偏移，以熟悉的方式完成此设置。

[comment]: # ({/33e199b9-a93b071e})

[comment]: # ({55d83c24-b922d0f2})
##### 预测时间范围

（仅 **forecast**）<br>
参数 `time` 指定 Zabbix 应根据历史数据中发现的依赖关系向未来外推多远。无论是否使用
`time_shift`，`time` 始终从当前时刻开始计算。

[comment]: # ({/55d83c24-b922d0f2})

[comment]: # ({1ccaacd1-625c0c85})
##### 达到阈值

（**timeleft** 专用）<br>
参数 `threshold` 指定被分析的监控项必须达到的值，
无论是从上方还是从下方达到都没有区别。一旦我们确定了 f(t)
（见下文），就应求解方程 f(t) = `threshold`，并返回
距离当前时刻最近且位于当前时刻右侧的根；如果不存在这样的根，则返回
1.7976931348623158E+308。

::: notetip
当监控项的值接近阈值并随后穿过
该阈值时，**timeleft** 会假定该交点已经发生在过去，
因此会切换到与 `threshold` 水平的下一个交点（如果存在）。
最佳实践是将预测作为常规问题诊断的补充，而不是替代。^[1](#footnotes)^
:::

[comment]: # ({/1ccaacd1-625c0c85})

[comment]: # ({4d1deb86-976bd98e})
##### 拟合函数

默认的 `fit` 是 *linear* 函数。但如果您的被监控系统
更复杂，您还有更多选项可供选择。

|`fit`|x = f(t)|
|-----|--------|
|*linear*|x = a + b\*t|
|*polynomialN*^[2](#footnotes)^|x = a~0~ + a~1~\*t + a~2~\*t^2^ + ... + a~n~\*t^n^|
|*exponential*|x = a\*exp(b\*t)|
|*logarithmic*|x = a + b\*log(t)|
|*power*|x = a\*t^b^|

[comment]: # ({/4d1deb86-976bd98e})

[comment]: # ({3c1c1442-add67840})
##### 模式

（**forecast** 专用）<br>
每次对触发器函数进行求值时，它都会从指定的历史周期中获取数据，并将指定的函数拟合到这些数据上。因此，如果数据略有不同，拟合后的函数也会略有不同。如果我们只是简单地计算该拟合函数在未来某个指定时间点的值，那么你将无法了解从现在到未来该时刻之间，被分析监控项的预期行为。对于某些 `fit` 选项（如 *polynomial*），未来的单一数值可能会产生误导。

|`mode`|**forecast** 结果|
|------|-------------------|
|*value*|f(now + `time`)|
|*max*|max~now\ <=\ t\ <=\ now\ +\ `time`~ f(t)|
|*min*|min~now\ <=\ t\ <=\ now\ +\ `time`~ f(t)|
|*delta*|*max* - *min*|
|*avg*|根据[定义](https://en.wikipedia.org/wiki/Mean_of_a_function)，f(t) 在 (now <= t <= now + `time`) 区间内的平均值|

[comment]: # ({/3c1c1442-add67840})

[comment]: # ({3f946ebf-5ff2285e})
#### 详细信息

为避免使用超大数字进行计算，我们将指定时间段内第一个值的时间戳加上 1 ns 视为新的零时间（当前 epoch 时间的量级为 10^9^，epoch 的平方为 10^18^，双精度约为 10^-16^）。增加 1 ns 是为了给 *logarithmic* 和 *power* 拟合提供全部为正的时间值，因为它们涉及计算 log(t)。时间偏移不会影响 *linear*、*polynomial*、*exponential*（除了使计算更容易且更精确）拟合，但会改变 *logarithmic* 和 *power* 函数的形状。

[comment]: # ({/3f946ebf-5ff2285e})

[comment]: # ({d180bad2-660cda6b})
#### 可能的错误

在以下情况下，函数会返回 -1：

-   指定的评估周期不包含任何数据；
-   数学运算结果未定义^[3](#footnotes)^；
-   数值计算问题（遗憾的是，对于某些输入数据集，双精度浮点格式的范围和精度会变得不足）^[4](#footnotes)^。

::: notetip
如果所选拟合方式不能很好地描述所提供的数据，或者数据量过少而无法进行准确预测，则不会标记任何警告或错误。
:::

[comment]: # ({/d180bad2-660cda6b})

[comment]: # ({e57d3e18-06e0a230})
#### 示例与错误处理

要在主机即将耗尽可用磁盘空间时收到警告，您可以使用如下触发器表达式：

    timeleft(/host/vfs.fs.size[/,free],1h,0)<1h

但是，错误代码 -1 可能会生效，并使您的触发器进入问题状态。通常来说，这是有益的，因为您会收到一个警告，表明您的预测未能正确工作，您应该更仔细地检查它们以找出原因。但有时这也不好，因为 -1 可能仅仅意味着在过去一小时内没有获取到有关主机可用磁盘空间的数据。如果您收到了过多误报，请考虑使用更复杂的触发器表达式 ^[5](#footnotes)^：

    timeleft(/host/vfs.fs.size[/,free],1h,0)<1h and timeleft(/host/vfs.fs.size[/,free],1h,0)<>-1

对于 **forecast**，情况会稍微复杂一些。首先，-1 可能会或可能不会使触发器进入问题状态，这取决于您使用的是类似 `forecast(/host/item,(...))<...` 的表达式，还是类似 `forecast(/host/item,(...))>...` 的表达式。

此外，如果监控项值为负数是正常情况，那么 -1 也可能是一个有效的预测值。但这种情况在现实世界中的概率可以忽略不计（请参见运算符 **=** 的[工作方式](/manual/config/triggers/expression)）。因此，如果您希望将 -1 视为问题或不视为问题，请分别添加 `... or forecast(/host/item,(...))=-1` 或 `... and forecast(/host/item,(...))<>-1`。

#### 脚注

^**1**^ 例如，像
    `timeleft(/host/item,1h,X) < 1h` 这样一个简单的触发器，可能会在监控项值接近 X 时进入问题状态，然后在达到值 X 后突然恢复。如果问题是监控项值低于 X，请使用：
    `last(/host/item) < X or timeleft(/host/item,1h,X) < 1h` 如果问题是监控项值高于 X，请使用：
    `last(/host/item) > X or timeleft(/host/item,1h,X) < 1h`

^**2**^ 多项式阶数可以为 1 到 6，*polynomial1* 等同于 *linear*。但是，请[谨慎](https://en.wikipedia.org/wiki/Runge's_phenomenon)使用高阶多项式。如果评估周期内包含的点少于确定多项式系数所需的数量，则会降低多项式阶数（例如，请求的是 *polynomial5*，但只有 4 个点，因此将拟合 *polynomial3*）。

^**3**^ 例如，拟合 *exponential* 或 *power* 函数时，需要计算监控项值的 log()。如果数据包含零或负数，您将收到错误，因为 log() 仅对正值有定义。

^**4**^ 对于 *linear*、*exponential*、*logarithmic* 和 *power* 拟合，所有必要的计算都可以显式写出。对于 *polynomial*，只有 *value* 可以在不增加额外步骤的情况下计算。计算 *avg* 需要计算多项式的原函数（解析地）。计算 *max*、*min* 和 *delta* 需要计算多项式的导数（解析地）并找到其根（数值地）。求解 f(t) = 0 需要找到多项式的根（数值地）。

^**5**^ 但在这种情况下，-1 可能会导致您的触发器从问题状态恢复。要获得完整保护，请使用：
    `timeleft(/host/vfs.fs.size[/,free],1h,0)<1h and ({TRIGGER.VALUE}=0 and timeleft(/host/vfs.fs.size[/,free],1h,0)<>-1 or {TRIGGER.VALUE}=1)`

[comment]: # ({/e57d3e18-06e0a230})
