计算错误率比值时返回空结果的PromQL/Thanos问题排查
问题排查与解决:PromQL返回空结果(legacy客户端部分状态码错误率为0)
问题根源
PromQL里如果除法的分母是0,会直接丢掉这条时间序列,不会返回NaN或者其他值。当legacy客户端的400状态码错误率为0时,计算v1/v2和它的比值时,这个状态码+版本的组合就被过滤了;要是所有要计算的组合都有分母为0的情况,就会直接返回Empty query result。
先排查确认
先单独验证各客户端各状态码的错误率是否正确,跑这几个查询:
# legacy各状态码错误率 sum by (status) (rate(http_response{version="legacy", status=~"4..|5.."}[5m])) / sum by (status) (rate(http_response{version="legacy"}[5m])) # v1各状态码错误率 sum by (status) (rate(http_response{version="v1", status=~"4..|5.."}[5m])) / sum by (status) (rate(http_response{version="v1"}[5m])) # v2各状态码错误率 sum by (status) (rate(http_response{version="v2", status=~"4..|5.."}[5m])) / sum by (status) (rate(http_response{version="v2"}[5m]))重点看legacy的400状态码是不是真的为0,同时确认401、503这些状态码有没有非0值。
检查你原来的比值查询是不是没处理分母为0的情况,比如这种写法就会出问题:
# 错误示例:没处理分母为0的情况 sum by (version, status) (rate(http_response{version=~"v1|v2", status=~"4..|5.."}[5m])) / sum by (version, status) (rate(http_response{version=~"v1|v2"}[5m])) / sum by (status) (rate(http_response{version="legacy", status=~"4..|5.."}[5m])) / sum by (status) (rate(http_response{version="legacy"}[5m]))这种写法遇到legacy某状态码错误率为0时,直接就把这个组合过滤掉了。
解决办法
办法1:处理分母为0的情况,同时对齐标签
用or vector(0)把分母为0的情况转成0(或者你想要的默认值),还要确保标签对齐:
# 计算v1/v2和legacy的错误率比值,处理分母为0 ( sum by (version, status) (rate(http_response{version=~"v1|v2", status=~"4..|5.."}[5m])) / sum by (version, status) (rate(http_response{version=~"v1|v2"}[5m])) ) / ( sum by (status) (rate(http_response{version="legacy", status=~"4..|5.."}[5m])) / sum by (status) (rate(http_response{version="legacy"}[5m])) or vector(0) # 把legacy错误率为0的情况转成0,避免被过滤 ) on(status) group_left(version) # 按status对齐标签,保留v1/v2的version标签
要是你希望分母为0时显示极大值(比如表示v1/v2错误率相对legacy无限大),可以把or vector(0)换成or vector(1e-10),这样既不会除以0,又能保留这条序列。
办法2:直接过滤掉legacy错误率为0的组合
如果不需要计算legacy错误率为0的状态码,那就先把这些组合过滤掉:
# 只计算legacy错误率大于0的状态码比值 ( sum by (version, status) (rate(http_response{version=~"v1|v2", status=~"4..|5.."}[5m])) / sum by (version, status) (rate(http_response{version=~"v1|v2"}[5m])) ) / ( sum by (status) (rate(http_response{version="legacy", status=~"4..|5.."}[5m])) / sum by (status) (rate(http_response{version="legacy"}[5m])) ) on(status) group_left(version) and sum by (status) (rate(http_response{version="legacy", status=~"4..|5.."}[5m])) > 0
这样只会返回legacy错误率大于0的状态码和v1/v2的比值。
办法3:用clamp_min()给分母设最小值
用clamp_min()确保分母不会是0,给它一个极小值:
( sum by (version, status) (rate(http_response{version=~"v1|v2", status=~"4..|5.."}[5m])) / sum by (version, status) (rate(http_response{version=~"v1|v2"}[5m])) ) / clamp_min( sum by (status) (rate(http_response{version="legacy", status=~"4..|5.."}[5m])) / sum by (status) (rate(http_response{version="legacy"}[5m])), 1e-10 # 设置极小值,避免除以0 ) on(status) group_left(version)
验证修改
跑修改后的查询后,检查结果:
- 对于legacy错误率为0的状态码,用办法1的话会返回
NaN(如果用or vector(0))或者极大值(如果用or vector(1e-10)); - 对于401、503这些legacy错误率非0的状态码,应该能正常显示比值。
内容的提问来源于stack exchange,提问作者c0tonet
相关产品推荐
相关产品推荐

