Gauge 抓取点天生带毛刺:max_over_time 取窗口最大值负责「看得清」,deriv 拟合斜率负责「判得准」。覆盖 内存盘 m100 / m101 / m102 / m108 / m115 等面板 · 核心告警 deriv(RSS[1h]) > 5MB/h 的原理课。
护士每 15 秒给你量一次体温,单次读数总有毛刺(刚喝过热水、刚打了个冷颤)——max_over_time(x[1m]) 就是「取这一分钟里的最高体温」,把抓取瞬间的抖动抹平,曲线立刻干净好读。而医生真正关心的往往不是「现在几度」,而是「还在往上烧吗、烧得多快」——deriv(rss[1h]) 就是「过去 1 小时体温的上升速度」。
一个负责看得清(抹抖动定形态),一个负责判得准(算斜率定量)。四两内存盘的核心告警 deriv(RSS[1h]) > 5MB/h 就是靠后者抓「真累积」:每小时涨超 5MB 且持续,说明内存在被真吃掉,而不是正常的锯齿波动。
想象住院量体温。护士每 15 秒来读一次体温计(Prometheus 每 15/30 秒抓一个点),把读数记在本子上。你把本子上的数画成曲线——毛毛躁躁上下抖,因为你刚喝了口热水读数就高一点,手凉了一下读数就低一点。这不代表病情在抖,只是测量本身有噪声。怎么办?看「一分钟里的最高读数」:max_over_time(x[1m]) 在每个时刻往回看 1 分钟的窗口,取窗口里的最大值——毛刺被抹平,趋势一眼可见。内存盘的 RSS 面板(m101)、GC 待回收面板(m102)、连接池使用率面板(m115)都是这么干的。
第二步是定量。医生查房时不问「现在几度」,他问「和两小时前比,烧退了还是更高了?」——也就是斜率。deriv(x[1h]) 对过去 1 小时窗口内的所有点做一次「直线拟合」,算出这条直线的坡度:正数在涨、负数在降、绝对值越大涨得越猛。对内存来说,斜率就是「每小时涨多少字节」——这是唯一能把「真泄漏」和「正常波动」区分开的数字。
两条路一对比就懂:健康路是锯齿形——处理请求时内存涨上去,干完活 GC 收回来,一小时的净涨幅约等于零,deriv ≈ 0,不告警;危险路是线性爬升——每小时稳定涨超 5MB,一天就是 120MB,迟早顶到容器限额被 OOM 枪毙。所以告警不看「现在内存多少」(高位平台期可能完全健康),只看「是不是在持续爬」。
| 类比里的东西 | 系统里对应的东西 |
|---|---|
| 护士每 15 秒量一次体温 | Prometheus 每 15/30s 抓一个 Gauge 数据点(核心盘 15s、内存盘 30s) |
| 一分钟内的最高体温 | max_over_time(x[1m]):窗口内取最大,抹掉抓取毛刺(m101/m102/m115 的标准写法) |
| 体温上升速度(度/小时) | deriv(rss[1h]):对窗口内点做线性拟合,得出累积斜率 |
| 持续低烧且越烧越高 | deriv > 5MB/h 持续 = 真累积,触发内存告警(m100 口径卡的 5 条核心告警之一) |
| 烧到 39 度但稳定不升 | 平台期:deriv ≈ 0,allocator 留着内存复用,不是泄漏(详见泄漏判定课) |
siliang_process_resident_memory_bytes{job="siliang_backend_worker"},把时间窗缩到 15m:看到每条 worker 线毛毛躁躁在抖。max_over_time(siliang_process_resident_memory_bytes[1m]):曲线立刻变干净,这就是 m101 面板的真实写法。deriv(siliang_process_resident_memory_bytes[1h]):数值变得很小,健康 worker 的线在 0 附近上下抖(正负交替)。# m101 进程内存面板的口径:1 分钟窗口取最大 max_over_time(siliang_process_resident_memory_bytes[1m])
# 哨兵面板口径:5m 窗口,非零即违规 max_over_time(siliang_sandbox_tracked_threads[5m])
# 斜率 = 累积速度;告警口径:持续 > 5MB/h(以 m100 口径卡为准) deriv(siliang_process_resident_memory_bytes[1h])
# 错配演示:Gauge 求 rate 毫无意义 rate(siliang_process_resident_memory_bytes[5m]) # ✗ deriv(siliang_process_resident_memory_bytes[1h]) # ✓ 斜率
# m109 字节分布面板解释了冲高:处理 200MB 视频内存必先涨 200MB+ # 1h 窗口的 deriv 冲高后回落 = 正常;持续为正 = 真累积
# 发布后看一眼:斜率转负 = 新版本把内存还回来了 deriv(siliang_process_resident_memory_bytes[1h]) # 负数 = 回落
max_over_time / min_over_time / avg_over_time 是一家人。四两面板选 max,因为抓取瞬间可能恰好取到一个偏低的点,取最大值更接近真实水位。
# 一家三口:取最大 / 取最小 / 取平均 max_over_time(x[1m]) # 面板主流口径 avg_over_time(x[1m]) # 想看均值时用
job="siliang_backend_worker" 排除——你手写查询时也要带上,否则曲线数量翻倍。
# 标准姿势:先按 job 过滤,再按 instance 看 worker max_over_time(siliang_process_resident_memory_bytes{job="siliang_backend_worker"}[1m])
① 进程内存 RSS / VMS(m101)——内存盘的体温计,曲线就是 max_over_time(...[1m]) 抹过抖动的;判断涨没涨、什么形态全靠它:📊 进程内存 RSS / VMS ↗
② GC 各代待回收对象数(m102)——gen2 待回收持续爬升是比 RSS 更早的泄漏预警器,同样用 max_over_time[1m] 让爬升看得清:🧹 GC 各代待回收对象数 ↗
③ 数据库连接池使用率(m115)——分子分母都套 max_over_time[1m] 再相除,抹掉抓取瞬间的抖动,避免毛刺造成假告警:🔌 数据库连接池使用率 ↗
④ 泄漏哨兵(m108)——「应恒 0」的防回退警报器用 max_over_time[5m]:窗口内出现过非零就揪出来,抖动逃不掉:🚨 沙箱跟踪与清理哨兵 ↗
⑤ 监控口径与告警规则卡(m100)——内存盘 5 条核心告警里最著名的一条就是 deriv(RSS[1h]) > 5MB/h,本课讲的正是它:📖 监控口径与告警规则(先读我)↗
原始抓取点抖来抖去,肉眼判断不了趋势。两步走:先 max_over_time 抹平看形态,再 deriv 定量看斜率。
# 第一步:抹毛刺(m101 口径),看形态:锯齿 / 爬升 / 平台期 max_over_time(siliang_process_resident_memory_bytes{job="siliang_backend_worker"}[1m]) # 第二步:算斜率,定量回答「每小时涨多少字节」 deriv(siliang_process_resident_memory_bytes{job="siliang_backend_worker"}[1h]) # 判读:deriv 在 0 附近正负交替 = 锯齿健康; # 持续为正且大 = 真累积,进入排查流程
判读收尾:曲线平滑了、斜率数字说话了,才算「看懂了这张图」。
内存高位本身不是告警理由,「持续爬升」才是。用 deriv 抓持续性,用 for 子句要求它持续足够久才响。
# 内存真累积告警(Prometheus rule,数值口径以内存盘「先读我」卡为准) groups: - name: siliang-memory rules: - alert: BackendRssClimbing expr: | deriv(siliang_process_resident_memory_bytes{job="siliang_backend_worker"}[1h]) > 5 * 1024 * 1024 # 5MB/h:每小时涨超 5MB 且持续 for: 30m # 持续半小时才响,避免单次冲高误报(时长以口径卡为准) labels: severity: warning annotations: summary: "worker {{ $labels.instance }} RSS 持续爬升,疑似真累积"
看到告警先别慌:对照 m101 看形态是爬升还是平台期,再进在途/哨兵面板找凶手。
两条都停在高位、长得一模一样的曲线,一个健康一个有病。deriv 一眼定案。
# 平台期:冲高后停住(allocator 留着复用)→ 斜率 ≈ 0 deriv(siliang_process_resident_memory_bytes{instance="253-backend-0"}[1h]) # 预期:在 0 附近抖 → 不是泄漏,别动手 # 真泄漏:爬完没停 → 斜率持续为正 deriv(siliang_process_resident_memory_bytes{instance="253-backend-1"}[1h]) # 预期:稳定 > 5MB/h → 按内存盘排查链下钻(在途 → 哨兵 → GC)
发布滚动完成后最怕「新版比旧版更能吃」。盯 30 分钟 deriv,转负就是好信号。
# 发布完成后跑一次:按 instance 看 4 个 worker 各自的斜率 deriv(siliang_process_resident_memory_bytes{job="siliang_backend_worker"}[1h]) by (instance) # 预期:旧 worker 内存随流量迁走回落(deriv 负),新 worker 平稳; # 若新 worker deriv 持续为正且大 → 新版本引入泄漏,考虑回滚
抓取瞬间恰好赶上高峰,使用率闪到 95% 又立刻回落——直接画会天天狼来了。m115 的口径是分子分母都抹抖动再除。
# m115 数据库连接池使用率口径:分子分母都取 1m 最大值再相除 max_over_time(siliang_db_pool_connections{state="checked_out",job="siliang_backend_worker"}[1m]) / on(instance, pool) max_over_time(siliang_db_pool_capacity{job="siliang_backend_worker"}[1m]) # 判读:80% 黄 / 95% 红;贴 100% = 池打满,请求开始排队
# 错: rate(siliang_process_resident_memory_bytes[5m]) # → Gauge 求 rate,曲线无意义 # 对: deriv(siliang_process_resident_memory_bytes[1h]) # → 每小时涨多少字节
deriv(x[1m]) 的输出抖得像股票分时图,没法判读。原因:1m 窗口内点太少,拟合斜率被单点噪声主导。正解:告警和判读用 [1h] 窗口,抓持续性;面板显示才用 [1m] 抹抖。
# 错: deriv(siliang_process_resident_memory_bytes[1m]) # → 噪声放大器 # 对: deriv(siliang_process_resident_memory_bytes[1h]) # → 稳定的累积速度
# 错: 看到单点 deriv 大就报警 # → 冲高也响,天天狼来了 # 对: deriv(...[1h]) 持续 > 5MB/h 才响 # → 冲高回落不响,真爬升必响
max_over_time(请求数_total[1m]) 画出来永远缓慢单调上扬,毫无信息量。原因:Counter 一直涨,窗口最大值永远是最新值,等于什么都没算。正解:Counter 配 rate/increase,max_over_time 留给 Gauge。
# 错: max_over_time(siliang_http_requests_total[1m]) # → 永远在涨,无信息 # 对: rate(siliang_http_requests_total[5m]) # → 才是 QPS
# 错: max_over_time(checked_out[1m]) / max_over_time(capacity[1m]) # → 错配 # 对: ... / on(instance, pool) max_over_time(siliang_db_pool_capacity[1m]) # → 按 worker×池 对齐
# 错: deriv(rss[15m]) > 0 # → 锯齿上行沿天天误报 # 对: deriv(rss[1h]) > 5*1024*1024 for: 30m # → 只抓真累积
# 错: deriv(siliang_process_resident_memory_bytes[1h]) # → 双计 # 对: deriv(siliang_process_resident_memory_bytes{job="siliang_backend_worker"}[1h])
参考答案:max_over_time 是「取这一分钟里的最高体温」——把测量本身的毛刺抹平,让曲线看得清;deriv 是「过去一小时体温的上升速度」——不管现在几度,只回答还在不在往上烧、每小时涨多少。前者管看得清,后者管判得准。
参考答案:因为绝对值高不等于有问题——大流量过后 allocator 留着内存复用,RSS 停在高位走平台期是完全健康的常态,按绝对值报警会天天误报。deriv 抓的是「持续爬升」这个真正的病灶:每小时涨超 5MB 且持续,才说明内存被真吃掉了,才需要去查在途面板和泄漏哨兵。
参考答案:不该。健康锯齿的上行沿 deriv 就是正的,处理请求时内存本来就要涨。要同时满足「涨得多(超 5MB/h)」和「持续(1h 窗口、for 30m 不回落)」才算真信号;单点为正、很快回落的多半只是刚搬完一个大文件。
参考答案:只喂 Gauge。Counter 只增不减,窗口内的最大值永远是最新的那个数,等于什么都没算、画出来是一条永远上扬的无信息曲线;Gauge 可上可下,取窗口最大值才有「抹平抓取抖动」的价值。Counter 要看速度请配 rate。