Administrator
发布于 2023-02-17 / 14728 阅读
133

系统容量评估与水位管理

一次没顶住的流量

去年大促,预估峰值 8000 QPS,实际来了 1.4 万,订单服务被打挂 12 分钟。复盘发现:我们的容量评估靠的是"去年×2"的拍脑袋,没有真实压测支撑,对系统的真实拐点一无所知。那天凌晨告警炸了,扩容来不及,限流没配,全靠手动重启扛,资损不小。这次我把容量评估正经做了一遍,把流程固化下来。

压测方法

用全链路压测,从网关打标(流量染色)进影子库,避免污染真实数据。影子库是真实库的克隆,只接压测流量,这样能放心打满:

  • 单接口基准:找出最慢的 SQL 和锁,先优化单点;
  • 递增加压:从 2000 到 16000 QPS 每档跑 10 分钟,记录 RT 与错误率;
  • 拐点识别:错误率破 0.5% 或 P99 翻倍处即为容量上限;
  • 混合场景:不只压下单,把查、改、取消按比例混着打,贴近真实。

我们第一版只压了下单,结果大促时"查订单"先挂了——因为查请求量是下的 3 倍。混合场景才暴露这个盲区。

容量模型

建立简单的线性关系:单实例在 P99<200ms 下可扛 4500 QPS。那么目标 16000 QPS 需要 16000/4500≈3.6,向上取整 4 实例,再乘冗余系数 1.3 得 6 实例。冗余是给突发和单实例故障留的——4 个里挂 1 个还剩 3 个×4500=13500,低于 16000,所以得 6 个才安全。

水位CPU动作
60%安全常态
75%警戒准备扩容
85%危险自动扩容 + 限流

水位线不是拍的,是按"剩余容量够不够扛单实例故障"算的。75% 时还有 25% 余量,单实例挂了其余能顶;85% 就快没余量了,必须立刻动。

扩容阈值与预案

在 Prometheus 里配告警,K8s HPA 按 CPU 自动扩:

alert: HighCpu
expr: rate(container_cpu_usage[5m]) > 0.75
for: 10m

# HPA 配置节选
minReplicas: 4
maxReplicas: 12
targetCPUUtilizationPercentage: 60

触发后走预案:先 HPA 自动加实例(K8s 约 30 秒起一个),若 5 分钟未降则人工限流到 12000 QPS 保核心。限流用网关层,非核心接口(如商品推荐)先砍,保下单和支付。

容量评估的坑

  • 只压单接口,漏了混合场景的瓶颈接口;
  • 用平均值算容量,没看长尾 P99,结果平均没问题、1% 用户超时;
  • 忘了依赖项容量:下游 Redis 连接数、MySQL 连接池也得跟着扩,否则应用扩了下游先挂;
  • 缓存击穿:压测时缓存是热的,真实大促缓存可能被集中失效,要模拟缓存击穿场景。

我们后来在压测里专门加了"缓存失效"场景,果然发现 MySQL 在缓存全失效时连接池被打满,提前把连接池从 50 调到 200 才过关。

复盘成果

今年大促依此执行:提前按模型扩到 6 实例,HPA 设好,限流预案就绪。峰值 1.5 万 QPS 来了,CPU 峰值 70%,自动扩容到 8 个,零事故。对比去年那 12 分钟,这套评估流程值回票价。

依赖容量也要一起评

应用扩容了,下游不一定扛得住,这点最容易被漏。我们列了一张依赖容量表,逐项确认:

依赖单实例容量峰值所需
Redis 连接数1万6实例×50=300,够
MySQL 连接池200/实例需调到 200,原 50 不够
下游支付接口8000 QPS1.5万超了,需限流保护

发现 MySQL 连接池和支付接口是瓶颈,提前把连接池从 50 调到 200,支付接口加了客户端限流(不超过 7000 QPS)。否则应用扩得再欢,下游先挂,照样全链路失败。

把压测做成常态化机制

一次评估不够,业务在长、代码在变。我们把全链路压测做成每月一次的固定动作,用历史流量回放 + 递增加压,自动出容量报告。有次月度压测发现某次上线后单接口 RT 从 40ms 涨到 120ms(有人加了次没走索引的查询),在预案里就暴露并修掉了,没等到大促才炸。常态化比一次性大促前评估稳得多。

一次容灾演练

光有模型不练是纸面容量。我们做了次真实演练:大促模拟期,手动 kill 掉 2 个订单实例(6 个里),看 HPA 和剩余实例能不能顶住。结果 HPA 30 秒拉起新实例,剩余 4 个在扩容到位前 CPU 冲到 88%,P99 从 200ms 涨到 480ms 但没挂,限流兜底生效。演练暴露了"扩容速度跟不上瞬时跌实例"的窗口,我们后来把最小副本从 4 提到 6,留更厚余量。

容量基线与文档化

每次压测出的"单实例拐点""安全水位"都写成容量基线文档,跟代码一起版本管理。新同学接手服务,先看基线知道"这服务能扛多少、超了会怎样",不用从头踩。我们还有个容量看板,实时显示各服务当前 QPS / 容量基线的比例,超过 70% 标黄、85% 标红。值班同学扫一眼就知道谁快到顶了,提前扩容,而不是等告警。容量从"事后复盘"变成"事前可见"。

压测工具选型

全链路压测我们用的是基于 JMeter 改的分布式压测 + 流量染色,也试过 Gatling(写场景用 Scala/Java DSL,报告漂亮)。选型的经验:脚本要能版本管理、能参数化(不同 QPS 档)、能打标(影子流量)。小团队用 Gatling 足够,场景写成代码比 JMeter 的 GUI 好维护。关键是"压测脚本也是代码",进仓库、能重复跑,别靠某个人机器上的本地脚本。每月跑一次,趋势可比。

容量不是一次算清的

业务在涨、代码在变、依赖在换,今天的容量基线半年后就失效。我们把"容量评估"写进每个大促前的固定流程,也写进架构评审——新服务设计时要给容量目标,上线后压测验证是否达标。容量规划是持续动作,不是一次性项目。去年那 12 分钟故障,根子就是把它当一次性、还拍脑袋。流程化了之后,今年大促平稳,这笔投入回本了。

大促当天的值班机制

模型再好,人也得在。大促当天我们排了双人 oncall,一人盯容量看板(谁超 85% 水位)、一人盯告警群。预案做成 runbook:超阈值第一步点 HPA 扩容、第二步开限流、第三步切降级(关非核心接口)。每一步都有对应的命令和审批人,不用现场拍脑袋。我们还做了"容量沙盘推演"——提前把历史峰值、本次预估、扩容步骤在群里过一遍,确保真出事时大家动作一致。预案写在文档里不如演练过,推演发现的"扩容命令权限没给"这种坑,当场就补了。

一次真实的容量误判

演练也不是次次顺。有次我们按预估扩到 6 实例,结果大促有个新玩法导致查订单量涨了 5 倍(远超预估的 3 倍),6 实例里查接口先到 90% 水位。好在监控实时,我们临时把查接口单独扩容到 10 实例、且开了查询降级(复杂筛选先返回缓存结果),扛过去了。这次说明预估永远有偏差,真正靠的是"实时水位 + 快速扩容 + 降级预案"的组合,而不是那个数字本身。容量评估是底线,弹性响应才是保命的。

容量评估的边界

容量评估能管"已知流量",管不了"突发黑天鹅"。所以我们从不在容量模型里把冗余压到极限,永远留 1.3 倍以上,且限流兜底是最后一道。评估给你"该扩多少",限流给你"扩不过来时保命",演练给你"真出事时动作对"。三件套齐了,大促才敢睡安稳觉。去年那 12 分钟,恰恰是因为三样都缺。

小结

容量评估不是算命,是用压测数据建立模型 + 设水位线 + 备预案。关键在"知道拐点在哪里、扩容要多快、兜底的限流值多少、依赖项容量跟不跟得上"。别用"去年×2"偷懒,真实压测出来的数字才作数。建议把容量评估做成大促前的固定动作,并形成文档。

参考