预测说下季度要扩容一倍,结果只涨两成。偏差不是算错,是这份预测隐瞒了它依赖的条件:给了一个数,没说它在什么前提下成立、误差多大、什么信号出现就该作废。预测的价值不在那个数,而在区间宽度、假设清单、失效信号三样东西上。
1. 预测失效的四个来源
失效原因可以归成四类,每类的应对方式不同。混在一起谈"预测不准"没有意义。
| 来源 | 机制 | 应对方式 |
|---|---|---|
| 基数错 | 水位取错(含缓存、只取单日高峰) | 用同期同口径水位并注明窗口 |
| 增长率错 | 用异常期拟合 | 多段窗口对比,给斜率区间 |
| 突发事件 | 大促、投放、渠道接入 | 显式列为情景,不进基线 |
| 架构变化 | 缓存、读写分离、协议变更 | 历史分段,旧段不拟合 |
前两类是"算"的问题,后两类是"结构"的问题。架构变化最易被忽略:历史数据物理上还在,语义上已失效。
2. 单点预测为什么必然错
单点预测隐含两个假设:增长率不变、结构不变。两者在真实系统里几乎从不成立,所以它的错误是结构问题,不是概率问题。
| 单点预测的隐含假设 | 现实中 | 结果 |
|---|---|---|
| 增长率恒定 | 有阶段、有饱和 | 高增长期外推必高估 |
| 结构不变 | 缓存、分片、批量化随时上 | 曲线换轨,历史失效 |
| 无外部事件 | 大促、投放 | 峰值被系统性低估 |
| 口径不变 | 指标定义随需求调整 | 新旧数据不可比 |
替代方案不是"更准的单点",而是"带条件的区间":下界、上界、以及使上界成立的触发条件。
3. 区间预测的三种口径
区间不是"给个范围显得谨慎"。三种口径回答三个不同的问题,混用会让决策错位。
| 口径 | 表达 | 回答的问题 | 用在哪 |
|---|---|---|---|
| 分位区间 | P50 / P90 | 常态需要多少 | 日常容量与预算 |
| 情景区间 | 基线 / 乐观 / 压力 | 不同前提的边界 | 大促与投放评审 |
| 上下界 | 下限与上限 | 最少留多少、最多撑到哪 | 采购与合同 |
// 预测记录的最小结构:数、区间、口径、假设、失效条件
horizon: "2027-Q1"
metric: peak_qps
baseline: BASELINE_QPS // 取最近 N 个同口径峰值的中位
interval:
p50: P50_QPS
p90: P90_QPS
scenarios:
plan: PLAN_QPS // 含已知投放
stress: STRESS_QPS // 含大促倍数
assumptions: [ "无新增渠道", "缓存命中率维持", "增长率取近 8 周" ]
invalidate_if: [ "单请求资源变化 ±20%", "新增渠道上线", "缓存策略调整" ]
口径要写在数旁边:区间脱离口径没有意义,P90 与"压力情景"混着引用是决策失误的常见起点。
4. 假设清单必须写明
假设不写,等于把校验责任转给读者。清单让人事后能判断"错在假设还是错在数据"。
| 必写项 | 例子 | 不写的后果 |
|---|---|---|
| 增长率取自哪段 | 近八周同口径峰值 | 争论用了哪段数据 |
| 是否含季节性 | 不含春节回落 | 旺季预测偏低 |
| 是否含大促 | 基线不含、压力含 | 大促前临时翻倍 |
| 口径与单位 | 峰值 QPS,非均值 | 两套数被并排比较 |
| 结构变更 | 下季度上线本地缓存 | 上线后预测脱节 |
// 假设清单模板:每条都要能被证伪,否则不是假设而是口号
// A1 增长率:取 GROWTH_WINDOW 内同口径峰值的斜率中位
// A2 季节性:已剔除节假日回落段,未剔除则注明
// A3 事件:基线不含大促;投放按 MARKETING_PLAN 计入 plan
// A4 结构:假设无缓存变更;若变更见 invalidate_if
// A5 口径:peak_qps = 一分钟窗口内的最大请求数
不可被证伪的假设没有价值。写"业务稳步增长"不如写"斜率取近八周,连续两周为负则本预测作废"。
5. 线性外推的失效边界
线性外推不是错的方法,它有明确适用区间:增长稳定、结构未变、周期够长。越过任一条,误差会放大得更快。
| 条件 | 成立时可用 | 不成立时 |
|---|---|---|
| 增长阶段稳定 | 线性或对数修正 | 分段拟合或情景法 |
| 结构未变 | 直接外推 | 只取变更后的数据段 |
| 窗口够长 | 覆盖一个业务周期 | 先补观测 |
| 无饱和迹象 | 未见增速放缓 | 用饱和曲线,别用直线 |
// 斜率要按窗口对比,而不是只取一个窗口
// 近 4 周 SLOPE_4W;近 8 周 SLOPE_8W;近 12 周 SLOPE_12W
// 判据:三者同向且量级接近 → 可线性外推;否则先解释差异再预测
6. 架构变化会让历史数据完全失效
缓存上线、读写分离、批量化、协议压缩,任何一项都会改变"每单位业务量耗多少资源"。此时总量曲线仍连续,但描述的是另一个系统。
| 变更 | 改变了什么 | 预测要怎么改 |
|---|---|---|
| 加缓存 | 后端 QPS 与业务量解耦 | 分段建模,按命中率折算 |
| 读写分离 | 写路径与读路径容量分开 | 两个指标分别预测,不该合并 |
| 批量化 | 单请求成本下降 | 按新口径重算 |
| 协议变更 | 带宽与序列化开销变 | 带宽单列一条预测 |
// 历史分段:把变更日标出来,拟合只用变更后的段
// segment: [START_DATE, CHANGE_DATE) ── 旧结构,仅作参考
// segment: [CHANGE_DATE, +∞) ── 新结构,用于拟合
// 若变更后数据不足一个业务周期 → 预测标注"低置信",改用上界法
7. 预测给区间,决策给余量
预测描述需求可能落在哪,决策描述愿承担多少不足风险。混成一个数,等于让预测承担它担不起的责任。
| 输入 | 由谁给 | 形式 |
|---|---|---|
| 需求区间 | 预测 | P50 / P90 / 压力 |
| 风险偏好 | 业务负责人 | 可接受多大比例时间不足 |
| 余量策略 | 架构 | 固定比例或按恢复时间反推 |
| 扩容动作 | 运维 | 提前期、批次、上限 |
// 容量决策的最小算法:不追求"刚好够",追求"不足的概率可接受"
// 目标容量 = 区间上界 × 安全系数
// 安全系数由两项反推:扩容提前期 / 故障恢复时间
// 若 提前期 > 恢复时间,安全系数必须覆盖整个提前期内的增长
余量是用钱买时间,但不是越多越好:系数定太高,等于用现金掩盖预测能力的缺失。
8. 比总量更有用的三个信号
总量是结果,信号是原因。盯总量只能事后发现不够,盯信号能换来提前量。
| 信号 | 定义 | 为什么更早 |
|---|---|---|
| 增长斜率 | 单位时间增量 | 斜率变化先于总量 |
| 单请求资源 | CPU 毫秒 / 内存 / IOPS | 结构变化最先体现在这里 |
| 峰值均值比 | 峰值 / 均值 | 比上升即突发性增强 |
// 三个信号一起看:任一突变都说明"总量预测的假设已变"
// peak_avg_ratio: peak_qps / avg_qps
// cpu_per_request: container_cpu_seconds / request_count
// growth_slope: 见第 5 节窗口对比
// 触发:任一信号较上季变化超 ±SIGNAL_THRESHOLD → 重做预测
9. 季节性业务的处理
有季节性的业务,年度同比常比环比可靠,但要小心节假日漂移与活动节奏变化。
| 做法 | 适用 | 陷阱 |
|---|---|---|
| 年度同比 | 春节、双十一 | 农历日期漂移错位 |
| 去季节化 | 看真实趋势 | 去除过度 |
| 双基线 | 同比 + 去季节 | 矛盾时无人仲裁 |
| 事件日历 | 活动写进情景 | 漏项则压力情景失效 |
// 季节性的最小处理:把"日历效应"当成显式输入而不是拟合噪声
// baseline_deseasonalized = actual / seasonal_index[week_of_year]
// 预测时再乘回 seasonal_index,并单独叠加 event_calendar 的倍数
10. 复盘:上期预测 vs 实际
不复盘,同样的错误会以同样的形式重复。复盘的关键不是算误差,是归因。
| 归因项 | 判断方式 | 归属 |
|---|---|---|
| 基数偏差 | 起始水位与实际水位差 | 口径问题 |
| 增长率偏差 | 实际斜率落在区间哪侧 | 建模问题 |
| 事件未计入 | 峰值对得上已知事件 | 情景清单 |
| 结构变更 | 单请求资源与假设不符 | 变更管理 |
// 误差复盘:绝对误差不够,要看落在区间内外
// err = (actual - forecast) / forecast
awk -F, '{ e=($2-$1)/$1; printf "%s %.1f%%\n", $3, e*100 }' forecast.csv
─── 连续两期同向偏差 → 不是随机误差,是假设系统性偏离
误差要看分布不看平均值:均值为零可能只是高估与低估互相抵消。
11. 用什么判断预测能力在变好
| 度量 | 定义 | 健康信号 |
|---|---|---|
| 误差分布 | 各期误差分位数 | P90 绝对误差收窄 |
| 区间覆盖率 | 落在 P90 内的比例 | 接近设计值 |
| 假设命中率 | 仍成立假设 / 总假设 | 上升 |
| 扩容偏差 | 实际需求 / 已扩容量 | 落在目标区间内 |
覆盖率最有用:它同时检验模型与纪律。口径说 P90,实际只有六成落入,说明区间给窄了或假设没覆盖真实波动。
12. 排错:预测失准的常见形态
| 现象 | 原因 | 处理 |
|---|---|---|
| 连续两期高估 | 斜率取自单窗口 | 多窗口取斜率 + 饱和修正 |
| 大促前临时扩容 | 基线无事件情景 | 建日历,压力单列 |
| 缓存后预测全错 | 历史段跨结构变更 | 分段拟合,只用新段 |
| 没人反驳也没人信 | 只给单点、未写假设 | 补区间与失效条件 |
| 扩容后仍不足 | 余量按均值算 | 按峰值与峰均比重算 |
13. 边界:什么情况下精细预测不值钱
| 情形 | 判断依据 | 更合适的做法 |
|---|---|---|
| 早期增长不稳 | 斜率符号在变 | 粗糙上界 + 快速扩容 |
| 即将重写 | 预测期跨重写 | 只反推重写后目标容量 |
| 容量充裕、成本占比低 | 扩容成本 ≪ 预测成本 | 固定系数,定期复核 |
| 弹性扩容成熟 | 提前期很短 | 靠信号触发 |
实用判据:若扩容提前期短于误差收敛时间,精度就不重要——把预算花在缩短提前期上回报更高。
14. 自测
- 预测失效的四个来源里,哪两个不是"算"的问题?各自的应对是什么?
- 为什么单点预测的错误是结构问题而不是概率问题?
- P90 区间与"压力情景"回答的是同一个问题吗?混用会导致什么?
- 斜率窗口对比出现三个方向不一致时,应该先做什么?
- 预测与扩容决策各给什么?为什么不该合并成一个数?
- 区间覆盖率偏低,可能的两类原因分别是什么?