rate/increase函数计算结果中为何出现异常的巨大数值?
rate与increase仅适用于递增类型的指标数据,即指标数值随时间增长逐渐增大。此处基于Prometheus Engine源码解释异常原理,可参考源码。
下面的截图中展示了rate与increase函数针对时间窗口内数据点的详细计算过程:先计算首位两个数值的差值resultFloat,然后开始遍历余下的数值点,若出现数值下降时则会将前一个数值prevValue累加到值resultFloat中,从而造成最终的结果值变得异常巨大。
switch {
case len(samples.Histograms) > 1:
numSamplesMinusOne = len(samples.Histograms) – 1
firstT = samples.Histograms[0].T
lastT = samples.Histograms[numSamplesMinusOne].T
var newAnnos annotations.Annotations
resultHistogram, newAnnos = histogramRate(samples.Histograms, isCounter, metricName, args[0].PositionRange())
annos.Merge(newAnnos)
if resultHistogram == nil {
// The histograms are not compatible with each other.
return enh.Out, annos
}
case len(samples.Floats) > 1:
numSamplesMinusOne = len(samples.Floats) – 1
firstT = samples.Floats[0].T
lastT = samples.Floats[numSamplesMinusOne].T
resultFloat = samples.Floats[numSamplesMinusOne].F – samples.Floats[0].F
if !isCounter {
break
}
// Handle counter resets:
prevValue := samples.Floats[0].F
for _, currPoint := range samples.Floats[1:] {
if currPoint.F < prevValue {
resultFloat += prevValue
}
prevValue = currPoint.F
}
default:
return enh.Out, annos
}
resultFloat += prevValue
}
prevValue = currPoint.F
}
default:
// TODO: add RangeTooShortWarning
return enh.Out, annos
}
排查方式:
-
使用PromQL查询先确定是否出现数值下降,首先将时间筛选框缩小到出现异常的时间段,并执行以下的PromQL语句(需将“xxxx_metric”更换为真实指标数据),若此查询的结果集中存在数据点则能够说明出现了“数值下降”的现象。
Query语句:(xxxx_metric{} - xxxx_metric{} offset 1s) < 0 Step 参数:1s -
针对出现异常数值的时间线,可使用SQL方式将原始的异常数值点查询出来,例如:
* | select *, from_unixtime(__time_nano__/1000000.0) from "时序库名.prom" where __name__='指标名' and element_at(__labels__, '用于筛选的某个labelKey')='用于筛选的某个labelValue' order by __time_nano__ // 建议在SQL中添加多个 element_at 函数来进一步缩小时间线范围。 -
SQL结果集中已按时间戳排序,可直接观察到已写入的原始脏数据点。
已写入时序数据,使用PromQL为什么查不到数据?
首先确认下述两种场景是否存在问题。
-
场景一:PromQL语法是否正确,时序库的查询框中会自动提示语法解析情况,若存在问题请按照提示修改;
例如,在 Metricstore (PromQL) 模式下输入查询语句
count(demo_api_request_duration_seconds_bucket),查询框下方会显示绿色 语法正确 标签及语法解析树,表明语法校验通过。
-
场景二:参见下面的截图进入时序库的“自定义分析”页面,并执行下述的SQL确认对应时间段内有无数据。
此SQL中的
__name__字段表示MetricName。* | select * from "MetricStore名字.prom" where __name__ = 'demo_api_request_duration_seconds_bucket'在Metrics探索页面右上角,单击更多操作,然后选择自定义分析。
输入 SQL 语句后,单击右上方的查询/分析按钮执行查询,查询结果将以表格形式展示在下方预览区域,包含
__name__、__labels__、__time_nano__、__value__等字段,据此确认对应时间段内是否存在数据。
上述两种场景检查没有问题,则可能是Prometheus计算引擎中的特殊的“选点”逻辑和“lookback-delta”机制造成的。
Prometheus计算引擎提供了一种名为PromQL的查询语言,该语言在执行计算时不一定会将全部数据点都纳入计算点,在执行计算前实际还存在一个“选点”过程。PromQL语法所包含的所有算子、函数、运算符在数据“选点”这个流程上可分成两类:带“[1m]、[1h]”等操作符的RangeVectorSelector 和 InstantVectorSelector。
下面分别列举了几项包含两种VectorSelector的PromQL示例。
RangeVectorSelector:
rate(http_requests_total[5m])
delta(http_requests_total[5m])
count_over_time(http_requests_total[5m])
InstantVector:
http_requests_total
absent(http_requests_total)
count(http_requests_total)
RangeVectorSelector的“选点”流程是将“[xx]”操作符所覆盖时间范围内的数据点都纳入计算,如下图,操作符“count_over_time ( up [30s] )”表示每次计算时都会将当前时间点及往前30秒的数据点都纳入到计算中。

PromQL中的“lookback-delta”机制仅对InstantVectorSelector有效,在“选点”时会根据“lookback-delta”参数值大小往前找特定时间区间,并将最近时间点的数据点作为当前时间点的数据值。在绝大多数情况下,原始数据的写入时间点和查询时间点是没有对齐的,在“选点”时会往前回溯n分钟(SLS默认3分钟,可通过自定义参数调整,请参见时序指标查询API),并将最近的数据点作为当前时间点的数据。
下图示例中,查询的startTime为09分28秒,该时间点不存在原始数据点,则会往前找到06分28秒,而这段时间都没有写入数据,则表示09分28秒没有选取到数据点。同理,09分43秒则选取了09分40秒的数据点、09分58秒选取了09分55秒的数据点。

由于该机制的特殊性,在“选点”时还存在一种特殊场景:在已经不存在数据的时间点,使用PromQL却查出了数据。下图示例中,10分00秒后已经不存在数据,但10分13秒、10分28秒、10分43秒、12分58秒等时间点都因为“lookback-delta”的机制往前回溯3分钟并选取到了10分00秒的数据点。

两类VectorSelector的“选点”逻辑是完全不一致的,而查不到数据通常也和这两类“选点”逻辑有关系,下面分别对两类逻辑查不到数据的可能性场景做分析。
-
RangeVectorSelector
操作符
“[xx]”中表示的时间区间较小,而运算的步长“step”参数较大,可能导致单次计算前的“选点”流程并没有选取到数据点。例如,原始写入的指标数据
“up”为每小时(整点)一个点,查询参数为:startTime: 10:30:00 endTime : 18:30:00 step : 1h query : count_over_time(up[10m])预期会返回7个数据点,而实际的查询结果为空,原因分析:PromQL计算逻辑会从startTime开始每间隔step执行一次计算,即在“11:30:00、12:30:00、13:30:00、14:30:00、15:30:00、16:30:00、17:30:00、18:30:00”这些时间点都会计算一次计算,但每次计算前的“选点”仅选取了近10分钟范围内所有点,致使每次“选点”都没有选到数据点。
-
InstantVectorSelector
“lookback-delta”参数较小,而运算的步长“step”参数较大,可能导致单次计算前的“选点”流程没有找到最近的数据点。例如,原始写入的指标数据
“up”为每小时(整点)一个点,查询参数为:startTime: 10:30:00 endTime : 18:30:00 step : 1h query : count(up)Prometheus中默认的
“lookback-delta”参数为5分钟,SLS时序库中默认为3分钟。在每次计算前的“选点”仅会往前找3分钟,即在“11:30:00、12:30:00、13:30:00、14:30:00、15:30:00、16:30:00、17:30:00、18:30:00”这些时间点都近会往前找3分钟,并将时间最近的数据点作为当前时刻的数据值。由于原始数据的分布较稀疏,上述几次“选点”都没有找到数据点,致使最后的计算结果同样为空。
解决方案
-
调整查询参数中的
startTime、endTime和step参数,将参数与写入时间点对齐后,就能够选取到写入时间点的数据。例如,下面的查询会返回7个数据点。在实际的应用场景中,指标数据点采集时刻并不是非常规整的,并且查询时也并不会刻意去对齐参数,所以不推荐使用此方案。
startTime: 10:00:00 endTime : 18:00:00 step : 1h query : count(up) -
调小step参数,例如我们将step参数设置为3分钟,以上述InstantVectorSelector场景为例,一定有多次“选点”能够选取原始的数据点。此方案是让“选取”流程变得更密集,以确保能够选取到原本较稀疏的数据点。
-
调大“lookback-delta”参数或者调大“[xx]”操作符的数值,调整此两项参数则是通过扩大“选点”的时间区间,将尽可能多的数据点纳入选择范围。
特定时间点之后已不再写入数据,为什么使用PromQL仍能查出在该时间点之后的数据?
原因与Prometheus计算引擎中的“lookback-delta”机制有关,请参见“lookback-delta”机制。
为什么查询结果中数据点的时间与写入时间不一致?
原因与Prometheus计算引擎中的“lookback-delta”机制有关,请参见“lookback-delta”机制。
针对相同时间范围,为什么最新执行结果与历史执行结果不一致?
存在两种可能性:
-
如果时间框选择的是“相对时间”模式,可能是 start 和 end 参数变化后,计算引擎在执行“lookback-delta”操作时选取到的数值点发生了变化,从而造成前后两次结果不一致。此现象符合Prometheus标准,属于正常情况。
-
如果前后两次执行相隔较长且查询的时间范围也一致,可能是历史执行时,预期内将参与计算的数据还未到SLS侧(写入数据存在延迟)。前后两次参与计算的数据不一致造成了结果不一致。若属于此类现象,可在PromQL语句中使用 offset 语法将查询区间往前平移一段时间,去除因写入延迟造成的结果错误。
单次Query计算资源超限,请求被拒绝
如果Query请求返回的错误中包含了“too many time Series or items,xxxxxx” 、“too many time Series or items in parallelMaster node,xxxxx”等文本提示,则说明该次Query读取了很大的数据量,已触发了计算层的内存限制。

解决方案
-
缩小查询区间。
-
若强依赖于上述时间范围的执行时序计算,建议开启并发计算,参考并发计算配置即可。
vector cannot contain metrics with the same labelset错误
-
原因一:
__labels__字段值中的LabelName不符合字母序。写入MetricStore的
__labels__字段由多组Label (LabelName#$#LabelValue)组成,且所有Label之间使用竖线(|)连接。在时序标识中,要求所有Label按照LabelName的字母序排序。更多信息,请参见时序标识。当
__labels__字段值中的LabelName不符合字母序时,会出现该错误。您可以通过如下SQL语句确认__labels__字段值中的LabelName是否按照字母序排序。* | select * from ( select __labels__, array_join(array_sort(split(__labels__, '|')), '|') as rightLabels from "MetricStore名字.prom" where __name__!='' ) where __labels__ != rightLabels -
原因二:
__labels__字段值中存在LabelValue为空问题。在Prometheus Engine中,LabelValue为空的Label会被视作无效。Prometheus Engine执行计算时会删除无效Label,因此出现该错误。您可以通过下述步骤确认
__labels__字段值中是否存在LabelValue为空问题。-
缩小查询时间范围,定位到出现此错误的大致时间区间。
-
执行如下SQL语句。
* | select __labels__ from "MetricStore名字.prom" where __name__!='' and regexp_like(__labels__, '.*#\$#\|.*|.*#\$#$')如果有返回结果,则表示存在LabelValue为空问题。建议修正数据上报端代码逻辑,删除无效Label。
-
PromQL查询时缺失Label信息,但原始数据中存在该Label
__labels__字段值中的LabelName未按照字母序排序时会出现该问题。您可以通过如下SQL语句确认__labels__字段值中的LabelName是否按照字母序排序。
* | select * from (
select __labels__, array_join(array_sort(split(__labels__, '|')), '|') as rightLabels from "MetricStore名字.prom" where __name__!=''
) where __labels__ != rightLabels
PromQL中涉及by/without计算时,结果不符合预期
__labels__字段值中的LabelName未按照字母序排序时会出现该问题。您可以通过如下SQL语句确认__labels__字段值中的LabelName是否按照字母序排序。
* | select * from (
select __labels__, array_join(array_sort(split(__labels__, '|')), '|') as rightLabels from "MetricStore名字.prom" where __name__!=''
) where __labels__ != rightLabels
指标探索中没有数据
可能原因是默认查询时间范围内没有数据。
在选择查询时间范围时,确保该时间范围内存在数据。
使用指标探索功能时,为保证响应速度,系统默认查询范围为最近5分钟。
在控制台中,查询到的时序数据缺失较多时间线
在日志服务控制台的MetricStore查询分析页面中执行查询时,存在limit参数,该参数会限制SQL查询或PromQL查询的返回数据的数量。建议适当调大该参数。

PromQL计算结果中存在Warning信息
可能原因是在拉取原始数据时,因Shard读取能力上限问题,未将该时间段内的数据读取完整。建议缩小查询时间范围或者分裂Shard提升整体吞吐能力。

exceeded maximum resolution of 11,000 points per timeseries. Try decreasing the query resolution (?step=XX)错误
在Prometheus中,限制每条时间线最大存在11000个数据点,即 (endTime - startTime) / step的值最大为11000。建议缩小查询区间或者调大step参数。