一次“都说不清”的复盘
一次持续三十分钟的故障之后,团队开复盘会。监控大盘上写着可用性 99.95%,按请求计算确实只失败了万分之五;客服系统里却有一千多个用户投诉,按用户计算,受影响比例接近百分之三;如果按时间计算,这半小时完全不可用,月度可用性直接被拉低。
三个数字都“真实”,但没人能说清这次故障到底算不算严重。产品说用户体验很差,运维说指标在目标以内,研发说错误集中在某个入口。会议开了两个小时,最后只得到一个模糊结论:下次注意。
问题不在监控精度,而在口径。可用性按请求计、按用户计和按时间计,回答的是三个不同的问题:
- 按请求计:服务端处理成功的比例是多少?
- 按用户计:有多少用户在这次故障中受到了影响?
- 按时间计:这段时间服务整体是否可用?
如果不先约定用哪一个,讨论就会变成各说各话。术语和指标不是文档装饰,它们是评审时能否形成同一个判断的前提。这份附录把卷一会反复出现的词和公式集中在一起,供阅读时随时回查。
术语速查
下表只给一句话定义和本卷的主要使用位置。需要展开推导的地方,正文会有对应章节;本表不替代章节,只帮助读者快速对齐词义。
| 术语 | 一句话定义 | 在本卷哪里用到 |
|---|---|---|
| DAU | 一天内产生有效行为的去重用户数,口径必须明确“有效行为”是什么 | 第 1、12 章的产品规模假设 |
| MAU | 一个月内产生有效行为的去重用户数,常用于看留存与活跃度 | 第 1、12 章的规模曲线 |
| QPS | 每秒请求数,必须说明是平均、峰值还是瞬时 | 第 1、12 章的容量估算 |
| 并发数 | 同一时刻正在处理中的请求数,不等于 QPS | 第 1、12 章的副本与线程模型 |
| p95 | 95% 的请求延迟不超过该值,关注大多数用户的体验 | 第 1、11 章的体验底线 |
| p99 | 99% 的请求延迟不超过该值,关注尾部与慢请求 | 第 1、11 章的告警与 SLO |
| 读写比 | 同一模块内读请求与写请求的数量比 | 第 1、8 章的形状分类与缓存决策 |
| 缓存命中率 | 读请求中由缓存直接返回的比例 | 第 7、8 章的缓存与回源保护 |
| 可用性 | 服务在约定口径下可用的比例,按请求、用户或时间计算 | 第 1、11 章的 SLO |
| SLI | 用来衡量服务质量的具体指标,例如成功率或 p95 延迟 | 第 11 章的用户视角指标 |
| SLO | 对 SLI 设定的目标值,例如 30 天内成功率不低于 99.9% | 第 1、11、12 章 |
| 错误预算 | 在 SLO 允许范围内可以消耗的失败量 | 第 1、11 章的发布与告警 |
| burn rate | 错误预算被消耗的速度,越高表示越危险 | 第 1、11 章的告警阈值 |
| 回源 | 缓存未命中或失效后,请求回到上游服务或数据库取数据 | 第 7、8 章 |
| 灰度 | 只让一部分流量或用户使用新版本,观察后再扩大范围 | 第 10 章 |
| 双写 | 同一份数据同时写入新旧两套结构或两个存储 | 第 10 章的数据兼容 |
| 幂等 | 同一个操作执行多次与执行一次的效果相同 | 第 3、6、9 章 |
| 最终一致 | 写入后经过一段时间,各副本最终达到一致状态 | 第 8、9 章 |
| 热点 | 访问量远高于平均值的单个 key、用户或内容 | 第 8 章的热 key |
| 工作集 | 在一段时间内被频繁访问、需要留在缓存中的数据集合 | 第 8 章的缓存容量 |
| 副本因子 | 每份数据保存的副本数量 | 第 12 章的存储与可用性 |
| 容量余量 | 当前容量与目标负载之间的缓冲比例 | 第 12 章的弹性与预算 |
| 写放大 | 一次逻辑写入在存储层产生的实际写入量 | 第 9、12 章的异步与成本 |
| 冷启动 | 新实例、新缓存或新用户从没有状态到可服务的过渡 | 第 8、12 章的预热 |
| MTTR | 从故障发生到恢复的平均时间 | 第 11、12 章的复盘 |
| RTO | 故障后业务恢复所需的最长可接受时间 | 第 1、12 章的底线 |
| RPO | 故障后可以接受丢失的数据时间窗口 | 第 1、12 章的底线 |
术语表里最容易引起争议的是“用户”和“请求”。同一个词在不同团队里可能指账号、设备、会话或自然人。评审时如果发现双方对“用户”的定义不同,先停下来对齐口径,再继续讨论数字。
指标口径与公式
下表给出本卷常用的计算方式。公式只是定义,不代表可以直接用于生产;每个指标都要说明窗口、数据来源和聚合方式。
| 指标 | 公式 | 常见误用 |
|---|---|---|
| 平均 QPS | 总请求数 ÷ 统计窗口秒数 | 把日平均当成峰值,低估晚高峰 |
| 峰值 QPS | 窗口内最大一秒的请求数 | 用五分钟平均值代替瞬时峰值 |
| 并发数 | 峰值 QPS × 平均延迟秒数(Little 定律) | 把并发数当成线程数或连接数 |
| p95 延迟 | 将延迟排序后取第 95 百分位 | 把多个实例的 p95 再平均 |
| p99 延迟 | 将延迟排序后取第 99 百分位 | 用 p99 代替所有用户体感 |
| 请求可用性 | 成功请求数 ÷ 总请求数 | 忽略重试请求,重复计算成功 |
| 用户可用性 | 未受影响用户数 ÷ 活跃用户数 | 把一次失败请求的用户算成完全不可用 |
| 时间可用性 | 可用分钟数 ÷ 统计窗口分钟数 | 把局部功能故障算成整体不可用 |
| 错误预算 | (1 − SLO) × 总请求数 | 把预算当成“可以随便失败”的额度 |
| burn rate | 实际错误率 ÷ 允许错误率 | 用瞬时值触发告警,造成抖动 |
| 缓存命中率 | 命中请求数 ÷ 总读请求数 | 忽略不同 key 的命中率差异 |
| 回源率 | 回源请求数 ÷ 总读请求数 | 把回源率低当成缓存一定正确 |
| 容量余量 | (当前容量 − 目标负载) ÷ 当前容量 | 只看 CPU,不看连接、带宽和存储 |
| 单位成本 | 月度总成本 ÷ DAU 或 ÷ 请求量 | 把一次性投入摊进单月,误导趋势 |
| 写放大 | 实际写入字节数 ÷ 逻辑写入字节数 | 只算存储,不算网络与复制流量 |
公式的价值在于让假设显式。比如“并发数 = 峰值 QPS × 平均延迟”看起来简单,但它假设请求到达稳定、延迟分布可用平均值代表。真实系统里延迟长尾很重,这个公式只能用来判断量级,不能用来定线程池大小。
同名指标的三组冲突
可用性:按请求、按用户还是按时间
按请求计的可用性适合衡量服务端处理能力,因为它直接反映成功与失败的比例。但它会掩盖“少数用户反复失败”的问题:如果一万个请求里只有一个用户失败了一百次,请求可用性仍然很高,用户体验却可能极差。
按用户计的可用性适合衡量影响面,但它对“轻度失败”不敏感。一个用户只失败了一次,和一个用户完全无法使用,可能都被算成“受影响”。
按时间计的可用性适合衡量整体服务是否在线,但它会把局部功能故障和全局不可用混在一起。内容流挂了、下单正常,按整体时间算可能仍然是“可用”。
本卷的做法是:SLO 以请求可用性为主,辅以用户影响面和关键路径的时间可用性。第 1.3 节会把这三个口径拆成定 SLO 前必须回答的问题,说明它们各自回答什么。
QPS:平均、峰值还是瞬时
平均 QPS 适合做容量规划,因为它对应一天的总量;峰值 QPS 适合做扩容和限流,因为它决定最坏时刻需要多少资源;瞬时 QPS 适合做故障分析,因为它能暴露毛刺和重试风暴。
最常见的错误,是用日平均 QPS 乘一个拍脑袋的峰值系数,然后把结果当成“峰值”。峰值系数应该来自真实曲线或压测,而不是“大概三倍”。如果产品有明显的活动流量,峰值可能出现在几分钟内,平均值完全无法代表。
第 12.1 节会给出从 DAU 到 QPS 的完整换算,包括请求数、活跃窗口和峰值系数。读者应该把那里的数字当成案例假设,而不是通用系数。
DAU:登录、活跃还是去重设备
DAU 至少有三个常见口径:
- 登录过的账号数;
- 产生过一次有效行为的账号数;
- 打开过 App 的去重设备数。
三个口径可能相差几倍。一个用户在多台设备上登录,设备口径会重复计算;一个用户登录后什么也没做,登录口径会高估活跃;一个用户只看了一眼推送就退出,有效行为口径可能不把他算进去。
本书在需要规模假设时,会明确写“DAU 指产生过一次内容浏览、发布或交易行为的去重账号数”。如果你的产品口径不同,代入容量公式前要先换算。
p99:单实例、聚合还是分桶
p99 不是可以简单平均的指标。两个实例的 p99 分别是 100 毫秒和 200 毫秒,合并后的 p99 不是 150 毫秒,而是取决于两个实例的请求量分布和长尾形状。
常见错误包括:把多个实例的 p99 做算术平均;把不同接口的 p99 混在一起;用 p99 代表所有用户的体感。正确做法是先明确聚合范围,再决定是加权合并原始分布,还是分接口、分实例分别看。
第 11.1 节会说明如何从用户视角定义 SLI,避免把服务端指标直接当成用户体验。
指标之间的关系
指标不是孤立的。理解它们之间的关系,比记住单个公式更有用。
Little 定律
并发数、QPS 和延迟之间的关系可以近似写成:
并发数 ≈ QPS × 平均延迟(秒)
这个公式适合判断量级:如果峰值 QPS 是 1000,平均延迟是 50 毫秒,那么平均并发大约是 50。它不适合直接用来定线程池,因为延迟分布、请求类型和排队策略都会影响实际并发。
错误预算与 burn rate
错误预算把 SLO 翻译成可消耗的失败量。30 天 99.9% 的 SLO,允许的失败比例是 0.1%。burn rate 表示当前错误率是允许错误率的多少倍:burn rate 为 1 表示刚好用完预算,为 10 表示按当前速度十天就会耗尽一个月的预算。
burn rate 的好处是让告警与 SLO 对齐。传统阈值告警容易在低流量时误报,也容易在缓慢恶化时漏报;burn rate 关注的是“离预算耗尽还有多远”,更接近业务风险。
命中率与回源率
缓存命中率和回源率是一对互补指标,但不能互相替代。命中率高不一定代表缓存正确,因为可能缓存了过期数据;回源率低也不一定代表健康,因为可能是缓存把错误结果也缓存了。
评估缓存时,至少要同时看:命中率、回源率、回源延迟、缓存数据年龄和失效时的并发。只盯命中率,容易在缓存集中失效时被打穿。
容量余量与成本
容量余量不是越大越好。余量越大,峰值越安全,但闲置成本也越高。合理的余量取决于扩容速度、故障恢复时间、流量可预测性和预算约束。
第 12.3 节会把余量、弹性和预算放在一起讨论。一个常见的判断是:如果扩容需要十分钟,而流量峰值只持续五分钟,那么余量必须覆盖这五分钟;如果扩容是秒级,余量可以更小,但要多付弹性成本。
怎么在评审中使用这张表
这份附录不是用来背的,而是用来在评审时快速对齐的。建议按下面的顺序使用。
第一,先确认名词。评审开始时,把“用户”“请求”“可用性”“峰值”这几个词的口径写在白板上。不要假设大家都同意。
第二,再确认公式。看到“我们的可用性是 99.99%”时,问一句:按请求、按用户还是按时间?统计窗口多长?重试算不算?
第三,最后确认假设。看到“我们只需要三台机器”时,问一句:DAU 多少、请求数多少、峰值系数多少、单机容量怎么来的?如果假设错一倍,结论会不会翻转?
这三步通常只需要几分钟,却能避免大部分“数字都对、结论不同”的争论。
产品规模类指标:DAU、MAU 与留存
DAU 和 MAU 经常被当成“用户数”,但它们回答的是不同问题。DAU 看日常活跃,MAU 看月度覆盖,二者相除可以得到一个粗略的活跃度比例。这个比例突然下降,通常说明产品粘性变化;突然上升,可能是活动拉新或口径变化。
使用规模指标时,要注意三件事。
第一,去重维度。按账号、设备、会话还是自然人去重,结果可能相差很大。多端登录的产品尤其容易重复计算。
第二,有效行为定义。打开 App 算不算活跃?只看推送算不算?只浏览不互动算不算?定义不同,DAU 可能差一个量级。
第三,时间窗口。自然日、滚动 24 小时和业务日(例如凌晨四点切换)会得到不同数字。跨时区产品还要考虑用户所在时区。
留存指标同样依赖口径。次日留存、七日留存和滚动留存回答的问题不同;新用户口径和全量用户口径也不能混用。本书在容量章节只使用 DAU 作为规模输入,不展开留存模型,因为留存影响的是增长预测,不是单次容量估算。
延迟指标:平均、分位与长尾
平均延迟是最容易被误用的指标。它把长尾平均掉了,可能看起来很好,实际却有一批用户每次都卡在几秒。分位指标(p95、p99)就是用来暴露长尾的。
但分位指标也有边界。
- p99 只说明最慢的 1% 请求,不说明这些请求属于谁。可能是同一批用户反复超时,也可能是随机分布。
- 分位不可简单平均。合并两个实例的 p99,需要原始分布或加权分桶。
- 分位不说明失败。一个请求可能很快返回错误,延迟很低但用户体验很差。
本书在讨论体验底线时,会同时看成功率、p95 和 p99。成功率回答“有没有失败”,p95 回答“大多数人多快”,p99 回答“最慢的一批有多慢”。三个指标一起看,才能判断用户的实际体感。
缓存与容量指标:命中、回源与余量
缓存相关指标至少有四个:命中率、回源率、回源延迟和缓存数据年龄。只盯命中率,容易忽略“命中但数据过期”的情况;只看回源率,容易忽略“回源成功但很慢”的情况。
容量相关指标也不只是 CPU。连接数、带宽、磁盘 IOPS、存储增长速度和副本数都会影响容量。一个服务 CPU 只有三成,但连接池已满,仍然会在峰值时不可用。
容量余量的计算要明确分母。是相对当前负载的余量,还是相对目标峰值的余量?是单机余量,还是集群余量?不同分母会得到完全不同的结论。第 12.3 节会给出一个可复算的例子。
指标采集的边界
指标再精确,也有采集边界。至少有三类问题需要提前说明。
第一,采样。高流量系统通常不会记录每一个请求,而是采样后估算。采样率、采样方式和误差范围会影响指标的可信度。一个 p99 可能来自几百个样本,也可能来自几百万个样本,两者的置信度不同。
第二,重试。客户端重试会让一次用户操作产生多次服务端请求。如果指标按请求统计,重试会放大分母;如果按用户统计,重试可能被合并。必须明确重试是否计入。
第三,时钟。多实例的时钟不一致会影响延迟计算和事件排序。跨机房系统尤其要注意,不能假设所有实例的时间戳完全可比。
这些边界不需要在每次评审里展开,但至少要知道它们存在。看到一个指标时,先问“它是怎么采集的”,再问“它能不能回答我的问题”。
从产品问题到指标:一张对应表
评审时最容易出现的情况,是产品问“为什么这么慢”,工程回答“QPS 很高”。两者都没有错,但不在同一个坐标系里。下面这张表把常见的产品问题翻译成可采集的指标。
| 产品问题 | 候选 SLI | 口径提醒 | 可能的误判 |
|---|---|---|---|
| 用户能不能打开首页? | 首页请求成功率、首屏时间 | 区分服务端成功与客户端渲染成功 | 服务端 200 但页面白屏 |
| 内容流是不是变慢了? | 列表接口 p95、p99 | 分端、分网络、分缓存命中 | 平均值正常但长尾恶化 |
| 私信是不是实时? | 消息端到端延迟、投递成功率 | 区分发送成功与对方收到 | 服务端写入成功但推送失败 |
| 下单会不会重复? | 订单幂等冲突率、重复扣款数 | 以用户可见结果为准 | 接口幂等但补偿重复执行 |
| 资料修改什么时候生效? | 资料读取一致性窗口 | 区分本人可见与他人可见 | 本人看到新值,他人仍是旧值 |
| 活动期间会不会挂? | 峰值 QPS、错误率、饱和度和排队时长 | 区分瞬时峰值与五分钟均值 | 平均容量足够但瞬时被打穿 |
| 故障时用户看到什么? | 降级页面比例、错误码分布 | 区分主动降级与被动失败 | 降级成功但用户不知道 |
| 成本是不是失控? | 单位请求成本、单位 DAU 成本 | 区分固定成本与变动成本 | 总量增长但单位成本下降 |
这张表不是标准答案,而是一个翻译模板。每个产品都可以按自己的关键路径替换行。重要的是:产品问题必须落到一个可采集的 SLI 上,否则就无法判断改动是否有效。
选择 SLI 时,还要注意“可控性”。一个受第三方支付渠道影响的成功率,不适合直接作为团队 SLO,除非团队能对渠道质量做出反应。把不可控因素放进 SLO,会让指标失去指导意义。
一张口径卡应该写什么
指标口径最容易丢失的地方,不是文档,而是交接。一个面板用了两年,维护的人换了三批,新同学只看到曲线,不知道分子分母怎么算。半年后有人问“为什么这个月的可用性突然变好”,没人答得上来。
解决办法不是写一份很长的文档,而是给每个关键指标建一张口径卡。卡片不需要复杂,能把下面这些字段写清楚就够。
| 字段 | 要写什么 | 例子 |
|---|---|---|
| 名称 | 指标的唯一名称,避免同名不同义 | content_feed_availability |
| 定义 | 一句话说明它衡量什么 | 内容流列表请求的成功比例 |
| 分子 | 哪些请求算成功 | 状态码 2xx 且响应体通过校验 |
| 分母 | 哪些请求进入统计 | 网关收到的列表请求,排除健康检查 |
| 窗口 | 统计的时间范围和切换点 | 滚动 30 天,按自然日展示 |
| 数据源 | 数据来自哪里 | 网关访问日志,不取客户端埋点 |
| 排除项 | 哪些情况不计入 | 计划内维护、主动限流返回的 429 |
| 聚合方式 | 多实例、多地域怎么合并 | 先合并原始请求,再算比例 |
| 负责人 | 谁解释、谁变更 | 内容链路值班负责人 |
| 变更记录 | 口径何时改过、为什么 | 2026-10-01 排除健康检查 |
口径卡的价值在于强制回答“分子分母是什么”。很多争议不是数据错,而是两个人看的分子分母不同。把这两行写下来,争论通常会缩小一半。
卡片也不必一开始就完美。先写清楚名称、定义、分子、分母和窗口,已经能避免大部分误解。随着指标被用于告警、SLO 或考核,再把排除项、聚合方式和变更记录补齐。
指标变更的管理
指标口径可以变,但不能悄悄变。一个看似无害的调整,例如把健康检查从分母里排除,可能让可用性从 99.5% 跳到 99.9%,让历史趋势失去可比性。
变更管理至少做三件事。
第一,记录变更原因。不是为了审计,而是为了未来的人能理解为什么改。原因可以很简单:“旧口径把主动限流算成失败,导致告警误报。”
第二,保留旧口径一段时间。重大变更时,建议同时展示新旧两条曲线,至少覆盖一个完整的业务周期。这样能看到差异来自真实变化还是口径调整。
第三,通知使用方。指标的使用者不只是维护面板的人,还包括产品、运营、客服和财务。一个口径变更如果没有通知到使用方,很容易在月度汇报时变成“数据对不上”。
指标变更不是坏事。产品和系统在变,口径本来就应该跟着变。坏事的是变更没有记录、没有过渡、没有通知,最后让所有人对数字失去信任。
指标与产品阶段
同一个指标,在不同产品阶段的重要性不同。起步期最该看的是核心路径成功率和用户能否完成关键动作;增长期开始关注峰值、缓存命中率和故障半径;规模期则要同时看单位成本、容量余量和跨区域一致性。
这不意味着早期不需要其他指标,而是说不要把规模期的完整指标套到起步期。一个只有十万 DAU 的产品,如果一开始就维护几十个 SLI、十几个告警规则,团队很快会被噪音淹没,真正重要的信号反而被忽略。
选择指标时,可以先问:当前阶段最可能以什么方式坏?答案通常指向两三个关键指标。先把这几条做准,再逐步扩展。指标体系的复杂度,应该和系统的复杂度一起增长。
如果不知道从哪里开始,可以按四个顺序筛选:
- 先结果后过程。先看用户是否完成关键动作,再看服务端内部指标。
- 先少后多。先维护三到五个能驱动行动的指标,再补解释性指标。
- 先可解释后精确。一个口径清楚、略有误差的指标,比一个精确但没人懂来源的指标更有用。
- 先有行动后有指标。如果一个指标变差时团队不知道该做什么,它暂时不适合进入告警。
这四个顺序不能保证选对指标,但能避免一开始就陷入指标沼泽。
指标也应该有退出机制。一个曾经重要、后来不再驱动行动的指标,可以降级为参考,而不是继续占用告警和值班注意力。
保留退出记录,也能防止半年后有人重新把它加回来。
小结
术语和指标的价值,不在于让文档看起来专业,而在于让评审有一个共同的坐标系。DAU、QPS、p95、并发、可用性、错误预算这些词,每个都有多种口径;不先对齐口径,讨论就会变成各说各话。
这份附录给出了一张术语表、一张公式表和几组同名指标冲突。读者不需要记住所有定义,但应该记住三个问题:这个数字来自哪里?它的窗口和口径是什么?如果它错一倍,结论会不会翻转?
需要展开推导时,回到正文对应章节:第 1.2 节 讲规模曲线,第 1.3 节 讲可用性底线,第 8.2 节 讲一致性窗口,第 11.1 节 讲用户视角的 SLI,第 12.1 节 讲 DAU 到 QPS 的换算。附录只做速查,不替代这些章节的判断过程。
常见错误与边界
- 把不同口径的可用性放在一起比较。按请求 99.95% 和按用户 97% 可能描述的是同一次故障。
- 用平均 QPS 做扩容。平均流量决定成本,峰值流量决定是否会被打垮。
- 把多个实例的 p99 做算术平均。长尾不能这样合并,必须先看分布和权重。
- 把命中率当成缓存健康的唯一指标。命中率高但数据过期,用户看到的仍然是错的。
- 把错误预算当成“可以失败”的许可。预算的目的是控制风险,不是鼓励消耗。
- 忽略重试对指标的污染。一次用户请求重试三次,可能被算成三次成功或三次失败。
- 用 DAU 直接乘请求数,不区分活跃窗口和峰值。日活只说明总量,不说明集中程度。
- 把附录公式直接用于生产容量。公式只给量级,真实容量必须靠压测和真实流量校准。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。