每个大对象都有「典型体重」:P95 分布就是物证尺——它解释了 RSS 为什么冲高:处理 200MB 的视频,内存必然先涨 200MB+;b64_json 天然比 URL 形态大 33%+。覆盖内存盘 📏 字节分布组 3 张面板(m109 m110 m123)· 仪表盘 siliang-memory。
快递站每收一件包裹都要过秤,秤不记每一件的重量,只往一叠"累积水桶"里记账:不超过 1MB 的几件、不超过 10MB 的几件……P95 就是把所有包裹从轻到重排队,站在 95% 位置那件的体重——它代表"典型的大件有多重"。m109 / m110 / m123 就是 backend 的三张体重秤:文件/媒体一类、图像/整包一类、XAI 生图响应一类。
它解决一个具体问题:RSS 冲高时,说明内存里突然多了几百 MB,但不知道是谁搬进来的。拿冲高时刻来对时间戳——哪类东西的体量足够解释这次冲高,搬运工就是谁。顺带记住一个天然事实:b64_json 格式把图片塞进 JSON 文本,天生比 URL 形态大 33%+。
先懂秤的原理。延迟、体积这类"每次都不一样"的数,系统不存单值,存的是一叠累积水桶(bucket,每个桶带个上限标签 le):不超过 1MB 的有几件、不超过 10MB 的有几件……查询时用 histogram_quantile 从桶里插值算出分位数。所以面板名字里都有 _bytes_bucket 和 "p95"——P95 回答的不是"最重的那件多重",而是"典型的大件有多重",比最大值稳得多(最大值会被一个极端件带跑)。
再看三张秤各称什么。m109 称文件/媒体三类活:ZIP 打包的产物、视频转存的文件、TTS 合成的音频。它就是"RSS 为什么冲高"的答案:要处理一个 200MB 的视频,程序必须先把它读进内存——内存必然先涨 200MB+,这不是泄漏,是搬运的物理代价。m110 称五类图像资产:快照源图、分割图层、skill 整包、provider 生图返回、Seedance 参考图。m123 单独称 xAI 的生图响应,并且 P50/P95 两条线对照——P50 是典型件,P95 是大件,两条线的距离就是"件与件的差距"。
最后是把秤变成破案工具。上一课的在途线回答"谁在搬",本课的分布回答"搬的典型多大"——两个时间戳一对:RSS 冲高时刻 × 在途跳起的工 × 该工的典型体重,三者量级对得上,案子就结了。还有一个高阶用法:分布本身"突然变大"(比如 provider 生图 p95 翻倍),那不是流量问题,是上游变更——先怀疑它换了默认分辨率或返回格式(b64_json 就天然比 URL 大 33%+)。
| 类比里的东西 | 系统里对应的东西 |
|---|---|
| 快递过秤的累积账本 | histogram bucket:每个桶带上限标签 le,只记"不超过 X 的几件" |
| 排队第 95% 位置的包裹 | P95 体重:典型大件的代表,比最大值稳(不被单件极端值带跑) |
| 文件/媒体行李秤 | m109:ZIP 产物 / 视频转存 / TTS 音频的 P95;200MB 视频 → 内存先涨 200MB+ |
| 图像资产秤 | m110:快照源图 / 分割图层 / skill 整包 / provider 生图 / Seedance 参考图 |
| 生图响应秤(p50/p95 双读数) | m123:xAI 返回体积分布;b64_json 天然比 URL 形态大 33%+ |
| 调监控录像对时间 | RSS 冲高时刻 × 在途跳起 × 典型体重,三连击锁定搬运工 |
# 指标名长这样:_bytes_bucket,带 le 标签 siliang_xai_image_response_bytes_bucket{le="1000000"}
# m123 口径:p95 / p50 双线对照 histogram_quantile(0.95, sum(rate(siliang_xai_image_response_bytes_bucket[5m])) by (le))
# 错:没有 by (le);对:必须 by (le)(+业务维度) … sum(rate(…_bytes_bucket[5m])) by (le)
# m123 用法:判断响应是否异常膨胀 # p95 阶跃 ≈ 格式切换(URL → b64_json)或分辨率变大
# 冲高量级 × 典型体重 对得上 = 正常搬运 # 对不上且在途滞留 → 才升级成泄漏排查
# 与昨天对比(offset 是通用 PromQL) … bytes_bucket[5m] offset 1d # 昨天的同期体重
# m123 双读数(照抄主图谱口径) histogram_quantile(0.95/0.50, sum(rate( siliang_xai_image_response_bytes_bucket[5m])))
它是什么:三类文件/媒体活的"典型体重"(P95 直方图分布):ZIP 打包产物、视频转存文件、TTS 合成音频。它是趋势参考——直接解释了 RSS 为什么冲高:处理一个 200MB 的视频,内存必然先涨 200MB+。
回答什么问题:这次内存冲高,是不是有活正在搬大文件?大文件典型多大?
✅ 三条线量级平稳、与业务节奏同步;冲高量级 ≈ 对应产物 P95 时属正常搬运
🚨 RSS 冲高量级对不上任何产物的体重、且在途滞留——才升级成泄漏排查。动作:拿冲高时刻对时间戳,谁在什么时候搬了大文件一对照便知
# 面板 PromQL(主图谱口径:三类 bucket 各算一条 p95) histogram_quantile(0.95, sum(rate( siliang_file_batch_archive_bytes_bucket[5m])) by (le)) # 同款:…_video_transfer_bytes_bucket / …_tts_audio_bytes_bucket
联动:主图谱 m109 ↗ · 相关课程:大对象在途(谁在搬) · P50/P95:排队
它是什么:五类图像资产的典型体重:快照源图、分割图层、skill 整包、provider 生图返回、Seedance 参考图。图像是解码放大最狠的一类(磁盘几 MB、内存几十 MB),所以这张秤对解释内存冲高特别有用。
回答什么问题:内存冲高时刻,是谁的大图在处理?上游给的图是不是变大了?
✅ 五类体量各自平稳;与图像类业务量同步起伏
🚨 provider 生图的 p95 突然变大——先怀疑上游换了默认分辨率。动作:对时间戳定位"内存冲高时刻是谁的大图在处理",并向上游确认变更
# 面板 PromQL(主图谱口径:五类 bucket 并列,此处举两类) histogram_quantile(0.95, sum(rate( siliang_snapshot_compose_source_bytes_bucket[5m])) by (le)) # 同款:_image_segmentation_layer / _openapi_skill_package / # _provider_image_response / _seedance_reference_bytes_bucket
联动:主图谱 m110 ↗ · 相关课程:防护与缓存(安检门拦超大) · 直方图水桶与 topk
它是什么:xAI 返回生图的体积分布,P50/P95 双线对照——P50 是典型响应的体重,P95 是最重那批的代表。关键常识:b64_json 格式会把图片塞进 JSON 文本,天然比 URL 形态大 33%+。
回答什么问题:生图响应是不是异常膨胀了(格式切换 / 分辨率变大)?膨胀了多少内存峰值?
✅ p50/p95 平稳、两条线距离稳定;b64 格式的大 33% 属格式代价,计入基线
🚨 p95 阶跃式上涨 = 响应异常膨胀——要么上游换了分辨率,要么返回格式切到了 b64_json。动作:对时间戳与 RSS 峰值互证,膨胀会直接放大内存峰值
# 面板 PromQL(照抄主图谱口径:p95 / p50 双读数) histogram_quantile(0.95/0.50, sum(rate(siliang_xai_image_response_bytes_bucket[5m])))
联动:主图谱 m123 ↗ · 相关课程:LLM 健康(provider 门诊) · P50/P95:排队
内存冲高报警,按这个顺序三步定案:谁在搬 → 搬多大 → 量级对不对得上。
# 第 1 步:锁定冲高时刻与量级(m101 口径) max_over_time(siliang_process_resident_memory_bytes{job="siliang_backend_worker"}[1m]) # 第 2 步:同一时间轴看在途——谁在那一刻跳起(m106/m107 口径) max_over_time(siliang_file_batch_archive_active[1m]) max_over_time(siliang_image_segmentation_active[1m]) # 第 3 步:查搬运工的典型体重(m109 口径) histogram_quantile(0.95, sum(rate(siliang_video_transfer_bytes_bucket[5m])) by (le)) # 定案:冲高量级 ≈ 在途工的 P95 体重 = 正常搬运;对不上 = 泄漏排查
体重是上游给的——用 offset 与昨天同期对比,阶跃即变更。
# 今天的 provider 生图 p95(m110 口径) histogram_quantile(0.95, sum(rate( siliang_provider_image_response_bytes_bucket[5m])) by (le)) # 昨天同期(offset 是通用 PromQL) histogram_quantile(0.95, sum(rate( siliang_provider_image_response_bytes_bucket[5m])) by (le) offset 1d) # 判读:阶跃式上涨(如翻倍)= 上游换默认分辨率/格式 → 找供应商确认 # 连带检查:安检门拦截速率是否同步抬头(m114 口径)
格式切换是"所有件一起变大",所以 p50 会先动——这是它与"偶发大件"的区别。
# p95 与 p50 双读数(m123 口径) histogram_quantile(0.95, sum(rate(siliang_xai_image_response_bytes_bucket[5m])) by (le)) histogram_quantile(0.50, sum(rate(siliang_xai_image_response_bytes_bucket[5m])) by (le)) # 读法: # p50 与 p95 同步阶跃 ≈ +33% → 格式切到 b64_json(格式代价,计入基线) # p50 平稳、只有 p95 阶跃 → 出现一批大件(分辨率变化),单独查
体重秤的所有查询都长一个样——把模板背下来,le 永远不丢。
# 通用模板:histogram_quantile + rate + sum by (le) histogram_quantile(<分位数>, sum(rate(<…_bytes_bucket>[5m])) by (le, <业务维度>)) # 实例:Seedance 参考图 p95(m110 五类之一) histogram_quantile(0.95, sum(rate(siliang_seedance_reference_bytes_bucket[5m])) by (le)) # 自查:结果数量 = le 桶数才对;只有一条线 = 忘了 by (le)
判读收尾:查询返回的序列数应等于桶数(或桶数 × 维度组合数),只有一条"诡异的总分位数"就是漏了 le。
上游悄悄变大是常态,每周看一眼趋势,别等内存峰值爆了才发现。
# 三张秤的周巡检(拉 7d 窗口看趋势) histogram_quantile(0.95, sum(rate(siliang_file_batch_archive_bytes_bucket[5m])) by (le)) histogram_quantile(0.95, sum(rate(siliang_tts_audio_bytes_bucket[5m])) by (le)) histogram_quantile(0.95, sum(rate(siliang_xai_image_response_bytes_bucket[5m])) by (le)) # 关注:台阶式抬升(上游变更)/ 缓慢爬升(数据量自然增长) # 台阶 = 记变更单;爬升 = 评估内存峰值预算
# 错: histogram_quantile(0.95, sum(rate(…_bucket[5m]))) # 对: histogram_quantile(0.95, sum(rate(…_bucket[5m])) by (le))
# 错: 用 p95 估算内存峰值上限 # 对: 峰值预算用 p99/最大值;p95 看典型与趋势
# 错: b64 比 URL 大 → 报 bug # 对: 大 33%+ = b64 格式天然代价;阶跃才立案
# 错: 只看当前 p95 绝对值 # 对: …bucket[5m] offset 1d # → 和昨天同期比
# 错: histogram_quantile(0.95, sum(…_bucket) by (le)) # 对: histogram_quantile(0.95, sum(rate(…_bucket[5m])) by (le))
# 错: p95 够大 → 冲高合理,结案 # 对: 在途同时跳起且事后归零 → 才算正常搬运
# 错: siliang_tts_audio_bytes[5m] # 对: siliang_tts_audio_bytes_bucket[5m]
参考答案:每件都存单值成本高且没法聚合,所以存一叠累积水桶(bucket),每桶带上限标签 le:不超过 X 的有几件。查询时用 histogram_quantile 在桶之间插值:把所有件按轻重排队,站在 95% 位置那件的体重就是 P95——它代表"典型大件",比最大值稳定。写查询的命根子是 rate → sum by (le) → histogram_quantile,漏了 by (le) 全盘作废。
参考答案:这是搬运的物理代价——程序必须把视频读进内存才能转存,内存先涨 200MB+ 是必然。判断是不是泄漏看三点:在途线(m106)当时是否跳起、干完后是否归零;RSS 冲高后是否回落或进入平台期(deriv≈0);冲高量级是否 ≈ 该类活典型体重(m109 的 P95)。三者在途、归零、量级都对得上,就是正常搬运。
参考答案:所有响应一起变大、且涨幅恰好是 b64 的编码膨胀率——最可能是返回格式从 URL 切到了 b64_json(b64 把图片塞进 JSON 文本,天然比 URL 形态大 33%+)。这是格式代价不是 bug,但要计入内存峰值基线。若只有 p95 阶跃而 p50 不动,则是出现了一批大件,更可能是分辨率变化,要单独追查。