
作为一名 SRE,编写高效且可读性强的 PromQL (Prometheus Query Language) 是日常工作中不可或缺的技能。糟糕的查询不仅会导致 Dashboard 加载缓慢,甚至可能导致 Prometheus OOM (Out of Memory)。本文将总结 PromQL 的查询技巧,重点关注性能优化和代码可读性。
Prometheus 的倒排索引对精确匹配(= 和 !=)进行了高度优化。相比之下,正则匹配(=~ 和 !~)需要扫描更多的倒排列表,消耗更多的 CPU。
pod=~".*"),这会拉取所有序列,极易导致性能问题。1 2 3 4 5 | # ✅ 高效:精确匹配 http_requests_total{status="500"} # ❌ 低效:不必要的正则匹配 http_requests_total{status=~"500"} |
|---|
空的标签匹配器(例如 {job=""} 或 {job=~".*"})会匹配所有可能的值,这在大型环境中是非常危险的操作。确保至少有一个具体的标签匹配器。
聚合操作(sum, avg, max, min 等)是数据分析的核心,但也最容易引发性能问题。
by 或 without在聚合时,始终明确指定保留的标签 (by) 或移除的标签 (without)。这不仅提高了查询的可读性,也能防止意外聚合了不该聚合的维度(例如不同的 instance 或 pod)。
1 2 3 4 5 | # ✅ 推荐:明确按 service 和 code 聚合 sum by (service, code) (rate(http_requests_total[5m])) # ❌ 不推荐:隐式聚合,如果未来引入新标签可能会破坏逻辑 sum(rate(http_requests_total[5m])) |
|---|
在计算的早期阶段减少时间序列的数量。如果你的表达式包含多个步骤,尽量在第一步就进行聚合,减少后续步骤处理的数据量。
rate vs irate vs increaserate: 计算范围向量内的每秒平均增长率。适合用于告警和长期趋势分析,因为它对瞬时峰值有平滑作用。irate: 基于范围向量内最后两个数据点计算瞬时增长率。适合用于高精度绘图,能灵敏捕捉瞬间峰值,但由于波动大,不建议用于告警。increase: 计算范围向量内的增量。increase(v[d]) 等同于 rate(v[d]) * d。性能提示: rate 和 increase 在计算时会处理重置(counter resets),这需要一定的计算资源。选择合适的时间窗口(如 [5m])很重要。窗口过小(小于抓取间隔)会导致无数据;窗口过大则平滑过度。
1 2 3 4 5 | # ✅ 推荐:告警规则中使用 rate rate(http_requests_total[5m]) > 10 # ✅ 推荐:排查瞬时抖动时使用 irate irate(http_requests_total[1m]) |
|---|
histogram_quantile 是计算 P99, P95 延迟的常用函数,但计算成本极高。
histogram_quantilebucket 指标进行 sum 聚合,再计算分位数。这是因为直方图的 bucket 是累积的,且通常跨多个实例。1 2 | # ✅ 正确顺序:先 sum by (le, ...),再 histogram_quantile histogram_quantile(0.99, sum by (le, service) (rate(http_request_duration_seconds_bucket[5m]))) |
|---|
高基数问题是 Prometheus 性能杀手。常见的错误包括:
user_id, email, client_ip 等取值无限的字段作为标签。查询优化: 如果必须查询包含高基数标签的指标,务必使用 sum by 聚合掉这些标签,或者使用 topk 限制返回的序列数量。
1 2 | # ✅ 限制返回数量,避免浏览器崩溃 topk(10, sum by (request_path) (rate(http_requests_total[5m]))) |
|---|
对于复杂且频繁执行的查询(例如用于 Dashboard 展示的聚合数据),应该使用 Recording Rules 将其预计算为新的时间序列。
优点:
1 2 3 4 5 6 | # rules.yaml 示例 groups: - name: example rules: - record: job:http_requests:rate5m expr: sum by (job) (rate(http_requests_total[5m])) |
|---|
良好的代码风格有助于团队协作。
job, 后 instance)。$__rate_interval 代替硬编码的 [5m],它会自动根据时间范围调整采样间隔,避免混叠效应。1 2 3 4 5 6 7 8 9 10 11 12 | # 优化前的单行查询 sum(rate(container_cpu_usage_seconds_total{image!="",name=~"^k8s_.*",namespace="default"}[1m])) by (pod_name) # 优化后的多行查询(在 Grafana 或代码中) sum by (pod) ( rate( container_cpu_usage_seconds_total{ namespace="default", image!="" }[$__rate_interval] ) ) |
|---|
以下是几个经过性能优化的常用查询模板:
1 2 3 4 5 6 7 | sum by (node) ( rate(node_cpu_seconds_total{mode!="idle"}[5m]) ) / sum by (node) ( rate(node_cpu_seconds_total[5m]) ) * 100 |
|---|
1 2 3 4 5 | topk(10, sum by (pod, namespace) ( container_memory_working_set_bytes{container!=""} ) ) |
|---|
1 2 3 4 5 | # 入站 sum by (pod) (rate(container_network_receive_bytes_total[5m])) # 出站 sum by (pod) (rate(container_network_transmit_bytes_total[5m])) |
|---|
总结: 优秀的 PromQL 应该是在满足监控需求的前提下,消耗最少的计算资源。时刻关注 cardinality(基数)和 execution time(执行时间),合理利用 Recording Rules,是迈向高级 SRE 的必经之路。
本文系转载,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文系转载,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。