监控世界的第一课:每个指标只有两种性格——只增不减的里程表、可上可下的体温计。性格决定看法,看法决定 PromQL 写法。覆盖 核心盘 + 内存盘几乎所有面板的「底层语法」。
监控里的每个数字只有两种性格:Counter(计数器)像汽车里程表,从开机那一刻只往前滚、永不后退,名字几乎都带 _total 后缀;Gauge(温度计)像体温计,可升可降,读数就是「此刻的状态」。请求数、错误数是里程表;内存、在线人数、队列深度是体温计。
性格决定看法:里程表的绝对读数没有意义,要看「每小时跑多快」——所以 Counter 配 rate();体温计直接读数——所以 Gauge 直接画曲线。主图谱 howto 卡把它浓缩成两句话:带 _total 的都是计数器,要配 rate() 看速度;其他多半是温度计,直接读数。类型认错,PromQL 必错,这是 89 张面板通用的第一语法。
想象你租了辆车自驾游。仪表盘上有两个数字:一个是里程表,显示这辆车出厂以来总共跑过 38217 公里;一个是时速表,显示此刻 90 km/h。你从来不会盯着里程表读数安排行程——它只涨不跌,读到天荒地老也只是「越跑越多」;你关心的是时速表:现在快不快、堵车时掉到多少。里程表回答「历史总量」,时速表回答「此刻状态」,两个表谁也替代不了谁。
服务器世界一模一样。siliang_http_requests_total(累计请求数)是里程表:每个 worker 从启动开始只往上加,从不回头;siliang_process_resident_memory_bytes(内存占用)是体温计:忙时涨、闲时落,围绕一个水位上下呼吸。四两 89 张面板里的每个指标,先判断它是里程表还是体温计,才知道该用什么姿势看它。
为什么这是整个课程的第一课?因为看错类型,画出来的图就彻底报废:给里程表直接画绝对值,你得到一条从 0 涨到几百万、永远上扬的斜线——除了证明「服务运行过」之外毫无信息量;反过来给体温计套上 rate(),输出的数字没有任何物理意义。接下来四课讲的 rate、increase、histogram 全都建立在「先认对类型」之上。
| 类比里的东西 | 系统里对应的东西 |
|---|---|
| 汽车里程表(只往前滚) | Counter:siliang_http_requests_total、siliang_db_lock_errors_total,名字带 _total,只增不减 |
| 体温计(可升可降) | Gauge:siliang_process_resident_memory_bytes(内存)、siliang_ws_online_users(在线人数)、队列深度,没有固定后缀 |
| 里程表读数没意义,要看时速 | Counter 配 rate() 算「每秒涨多少」→ QPS、错误率、GC 回收速率 |
| 时速表直接读数 | Gauge 直接画曲线:现在多少就是多少,套加工函数反而画蛇添足 |
| 把里程表读数当时速看 | 事故现场:对 _total 画绝对值 → 一条永远上扬的斜线,毫无信息量 |
rate(siliang_http_requests_total[5m]),Counter 配 rate,正是「看时速」的写法。sum(siliang_ws_online_users{...}):Gauge 直接求和读数,正是「读体温」的写法。siliang_http_requests_total(不带 rate)——曲线永远上扬:亲眼看到「里程表」本表,顺便理解为什么它没信息量。_total 后缀——这是全 Prometheus 世界最可靠的暗号。进程重启会归零重新累计,但「只涨」的方向性不变。
# Counter 示例:开机以来的累计 HTTP 请求总数 siliang_http_requests_total{job="siliang_backend_worker"} # 读数永远 = 本次启动以来的请求总和,只涨不跌
# Gauge 示例:此刻的在线用户数(可上可下) sum(siliang_ws_online_users{job="siliang_backend_worker"}) # 下班了会掉、上班了会涨——体温计逻辑
_total = Counter(配 rate);_bucket = 直方图水桶(配 histogram_quantile,第 5 课);其余基本都是 Gauge(直接读)。拿到陌生指标先看后缀再动笔。
# 三种后缀对号入座 …_requests_total # Counter → rate() …_duration_seconds_bucket # Histogram → histogram_quantile() siliang_db_pool_connections # 无后缀 → Gauge 直接读
rate / increase;Gauge 直接读,或配 max_over_time(抹抖动)、deriv(算斜率)。喂错对象,曲线必废。
# 对号入座:谁配谁 rate(siliang_http_requests_total[5m]) # ✓ Counter 配 rate max_over_time(siliang_process_resident_memory_bytes[1m]) # ✓ Gauge 抹抖动
_bucket。
# Histogram 的桶:同一指标有一叠 le 不同的序列 siliang_llm_call_duration_seconds_bucket{le="0.5"} # 意思是:耗时 ≤ 0.5 秒的调用累计有多少个
# 5xx 错误率 = 错误速率 ÷ 总速率 sum(rate(siliang_http_requests_total{status=~"5.."}[5m])) / sum(rate(siliang_http_requests_total[5m]))
max_over_time(x[1m]) 取窗口最大值——四两内存盘 RSS、GC、连接池面板的标准写法(第 6 课细讲)。
# m101 进程内存面板口径:1 分钟窗口取最大 max_over_time(siliang_process_resident_memory_bytes[1m])
job="siliang_backend_worker" 排除它。你手写查询时也要带上,否则数值翻倍。
# 标准姿势:先按 job 过滤,再看 worker sum(siliang_ws_online_users{job="siliang_backend_worker"}) # 不带过滤 → server job 混入,在线人数虚高一倍
① HTTP QPS(按 handler)(c16)——Counter 配 rate 的教科书案例:siliang_http_requests_total 这个里程表被算成每秒速度:🟢 HTTP QPS(按 handler)↗
② 在线用户数(c1)——Gauge 直接读数的典型大数字面板:siliang_ws_online_users 可上可下,此刻多少人就是多少:🟣 在线用户数(协作画布去重)↗
③ 进程内存 RSS / VMS(m101)——内存盘的体温计,锯齿/爬升/平台期三种形态全靠直接读曲线判断:📊 进程内存 RSS / VMS(每 worker)↗
④ 数据库锁冲突(近 1h 次数)(c34)——Counter 的另一种看法:配 increase() 算「近一小时总共涨了几次」,锁专项修复的复发哨兵:🔴 数据库锁冲突(近 1h 次数)↗
⑤ GC 各代回收速率(m126)——siliang_python_gc_collections_total 是「垃圾车累计出车次数」里程表,配 rate 变成出车频率:🧹 GC 各代回收速率 ↗
新同事甩给你一个指标名问「这图怎么画」。先看后缀定类型,再定函数,最后再谈配色。
# 第一步:看后缀定类型(以 siliang_ws_connections_total 为例) siliang_ws_connections_total # _total 结尾 → Counter 里程表 # 第二步:Counter 配 rate 算速度(c21 连接结果分布的口径) sum(rate(siliang_ws_connections_total[5m])) by (outcome) # → 每秒几次敲门 / 几次被拒,曲线有起伏才有信息 # 对照:Gauge 版的兄弟指标直接读(c20 口径) sum(siliang_ws_connections_active{job="siliang_backend_worker"}) # → 此刻多少条对讲机在线,直接画
判读收尾:同一个「连接」话题,_total 看速率分布、无后缀看当前存量——类型认对,两张图各答各的问题。
HTTP 流量有两个天然成对的指标:累计完成数(Counter)和此刻在途数(Gauge)。混在一张图里看不懂,分开设两张才清楚。
# 累计完成数:Counter,配 rate 看 QPS(c16 口径) sum(rate(siliang_http_requests_total[5m])) by (handler) # 此刻在途数:Gauge,直接读(c18 口径,含 ops 排空参考线) sum(siliang_http_requests_in_progress) by (method) + sum(siliang_ops_http_in_flight) # 判读:QPS 高但在途平稳 = 处理得快,健康; # QPS 平稳但在途持续高位 = 请求在排队,出事了
「错误多不多」不能看错误绝对数(流量大时错误天然多),要看占比。分子分母都是 Counter,先各自 rate 再除。
# LLM 错误率(c13 口径,clamp_min 防止除零出现 NaN) rate(siliang_llm_calls_total{outcome="error"}[5m]) / clamp_min(rate(siliang_llm_calls_total[5m]), 1) # 判读(c13 卡):<5% 绿 / 5-10% 黄 / >10% 红; # >10% 多半是 provider 限流(429)或 key 失效——切流量,不是修 bug
Counter 重启会归零。曲线上一夜之间从几百万砸回 0 的断崖,就是一次重启的铁证——比翻登录记录还快。
# 原始 Counter:断崖 = 重启归零(谁在什么时候重启一目了然) siliang_http_requests_total{job="siliang_backend_worker"} # 更正式的问法:启动时间戳 1h 内变了几次手(k5 口径,第 3 课细讲) count by (service) (changes(container_start_time_seconds[1h])) # 发布窗口外出现 > 0 = 计划外重启,要查
老板不问「累计请求数是多少」,问「昨天跑了多少量、这周涨得多快」。里程表读数要加工才能进周报。
# 「昨天总共处理多少请求」→ increase:窗口内总共涨多少(第 3 课) sum(increase(siliang_http_requests_total[24h])) # 「最近涨得多快」→ deriv 算斜率(第 6 课,喂 Gauge 用) deriv(siliang_process_resident_memory_bytes[1h]) # 原则:绝对读数不进周报,加工后的「速度/总量/斜率」才说话
# 错: siliang_http_requests_total # → 永远上扬的斜线 # 对: rate(siliang_http_requests_total[5m]) # → 有起伏的 QPS
# 错: rate(siliang_process_resident_memory_bytes[5m]) # → 无意义的数 # 对: deriv(siliang_process_resident_memory_bytes[1h]) # → 每小时涨多少字节
# 错: 看到 _total 掉 0 就喊「服务没人用了」 # → 其实是刚重启 # 对: 断崖后曲线重新从 0 爬升 = 重启;配合 k5 重启面板确认
_bucket 是「累计计数」的一叠桶,单只桶没有直接意义。正解:桶必须交给 histogram_quantile 插值算分位数(第 5 课)。
# 错: siliang_llm_call_duration_seconds_bucket # → 一叠上扬线 # 对: histogram_quantile(0.95, sum(rate(…_bucket[5m])) by (le, model))
container_start_time_seconds 画成曲线,看到一条平平的线偶尔跳一下,不知所云。原因:它的值是「本次启动的时间戳」,本质是状态量,画绝对值没意义。正解:用 changes() 数它变了几次手 = 重启次数(k5 口径)。
# 错: container_start_time_seconds # → 平线+跳变,看不懂 # 对: changes(container_start_time_seconds[1h]) # → 重启次数
# 错: 借出数 / 容量 # → 两组线乱配对 # 对: … / on(instance, pool) … # → 按 worker×池 精确对齐
# 错: sum(siliang_ws_online_users) # → 双计翻倍 # 对: sum(siliang_ws_online_users{job="siliang_backend_worker"})
参考答案:仪表盘上有里程表和时速表。里程表(Counter)只往前滚、从不后退,记录的是「历史总量」,比如累计请求数,名字带 _total;时速表(Gauge)可快可慢,显示「此刻状态」,比如在线人数、内存占用。里程表的绝对读数没人关心,要看的是速度——所以 Counter 要配 rate();时速表看一眼就知道现在几码——所以 Gauge 直接读数。
参考答案:Counter 从启动起只增不减,画出来必然是一条从 0 涨到几百万、永远上扬的斜线——它只证明「服务运行过」,看不出高峰低谷、看不出故障时刻。改法是套 rate(x[5m]):算「过去 5 分钟平均每秒涨多少」,曲线立刻有了起伏,QPS、错误率全是从这里来的。
参考答案:是 Gauge。判断依据:没有 _total 后缀,且语义上「借出的连接」能借能还——可上可下就是体温计。看法是直接读数或套 max_over_time(x[1m]) 抹抖动:四两 m115 的池使用率面板就是分子(checked_out 借出数)除以分母(capacity 容量),再按 on(instance, pool) 对齐。
参考答案:Counter 只增不减,掉 0 几乎必然是进程重启归零重新累计,不是流量消失。接下来看:① 曲线重启后是否恢复爬升;② k5 的 changes(container_start_time_seconds[1h]) 确认重启时间与次数;③ 对照发布记录——发布窗口内的重启属预期,窗口外的是计划外重启,要查。