PromQL 如何在多个值并存时选择 start_time 最大的数据?
结论
可以实现,前提是将启动时间作为数值型指标上报。仅添加 start_time label 不够:标准 PromQL 无法直接把标签中的字符串转成数字,再选出数值最大的一条。
可以用启动时间指标配合 topk(1, ...) 选出最新进程,再通过 and on(...) 保留该进程对应的 count 数据。
为什么仅添加 label 不够
下面两条数据属于不同的时间序列:
count{job="worker",instance="node1:9000",start_time="1730000000"} 100
count{job="worker",instance="node1:9000",start_time="1730000300"} 3
Prometheus 通过指标名和完整的 label 集合标识时间序列。start_time 不同,新旧数据就会分别存储。如果查询把这两条序列相加,结果就是 103。参考:Prometheus 数据模型
下面的查询也无法按启动时间筛选:
topk by (job, instance) (1, count{job="worker"})
topk 按 count 的样本值排序,会选中旧进程的 100,不会根据 start_time label 决定顺序。
先上报数值型启动时间
每次进程运行时,同时上报业务指标和一个记录启动时间的 Gauge。查询可见的数据如下,启动时间使用 Unix 时间戳,单位为秒:
count{job="worker",instance="node1:9000",start_time="1730000000"} 100
count{job="worker",instance="node1:9000",start_time="1730000300"} 3
worker_start_time_seconds{job="worker",instance="node1:9000",start_time="1730000000"} 1730000000
worker_start_time_seconds{job="worker",instance="node1:9000",start_time="1730000300"} 1730000300
worker_start_time_seconds 是示例指标,需要自行上报。在一次运行期间,它的值始终为该进程的启动时间。
两类指标必须携带能关联到同一次运行的标签。这里使用 job、instance 和 start_time;如果已有唯一的 run_id,也可以用它关联。
再用 PromQL 筛选业务数据
假设同一个 job、instance 下的新旧进程互相替代,先选出每组启动时间最大的一条:
topk by (job, instance) (
1,
worker_start_time_seconds{job="worker"}
)
再用下面的完整查询筛选 count:
count{job="worker"}
and on (job, instance, start_time)
topk by (job, instance) (
1,
worker_start_time_seconds{job="worker"}
)
右侧的 topk 保留最新进程的标签,and 按指定标签匹配两侧序列,并保留左侧原有的样本值。示例中的结果为:
count{job="worker",instance="node1:9000",start_time="1730000300"} 3
and 在这里用于筛选序列,无需将业务值与启动时间相乘,也无需添加 group_left。参考:PromQL 聚合与集合运算符
使用时需要注意什么
分组必须符合进程的替代关系
示例假设 job、instance 在重启前后保持不变。如果 instance 随进程重启而变化,应改用稳定的业务身份,例如 worker_id,并相应调整 by(...) 和 on(...)。
如果多个进程需要同时计入统计,应分别为它们分组,否则 topk(1, ...) 会把正常并存的进程也筛掉。
启动时间指标必须与业务指标对应
如果最新进程的启动时间指标没有匹配的 count,上述查询会返回空结果,不会自动回退到旧进程。应尽量通过同一个采集端点同时暴露两类指标。
启动时间也需要足够的精度。如果两次启动的时间戳相同,就无法仅凭时间戳判断先后。此时需要提高启动时间的精度,并使用独立的运行标识区分序列。
样本时间戳不等于进程启动时间
timestamp(count{job="worker"}) 返回样本自身的时间戳,不能代替进程启动时间。label_replace() 也只能操作标签字符串,不能把标签内容转换成样本数值。参考:PromQL 函数说明
同时检查旧数据为何仍然可见
Prometheus 的即时查询会在回看窗口内选择样本,并排除已标记为 stale 的序列。旧数据保存在历史存储中,不代表它一定会参与当前计算。参考:Prometheus staleness 规则
出现翻倍时,应先查看未聚合的 count,确认哪些标签不同、哪些序列仍在上报。上述查询可以筛选新旧运行并存时的数据,但不会清理采集端持续暴露的旧数据。每次重启新增 start_time 标签值都会创建新序列,频繁重启时还需要考虑序列数量的增长。
备注:内容仅供参考。
