KEEL · 龙骨 · A CURRICULUM FOR THE AI ERA
05 · 观测驱动的性能优化闭环 — keel 龙骨
这一章回答:怎么把"系统有点慢"变成一个有数据、有顺序、可验收的工程过程。
这一章回答:怎么把"系统有点慢"变成一个有数据、有顺序、可验收的工程过程。
性能优化最容易变成玄学:大家凭感觉改,改完也不知道有没有效。把它变成闭环,核心是三件事:先有基线,再做单点变更,最后用同一套指标验证。
一、闭环五步
测量基线 → 定位瓶颈 → 单点优化 → 灰度验证 → 记录复盘
每一步的要求:
| 步骤 | 要求 | 常见错误 |
|---|---|---|
| 测量基线 | 确定观测对象与工具,先记录优化前的数字 | 还没测就开始改 |
| 定位瓶颈 | 用数据收敛到一处(慢 SQL?外部依赖?序列化?) | 同时怀疑七八个地方 |
| 单点优化 | 一次只改一处,能独立回滚 | 一次提交里改五处,事后不知道哪个生效 |
| 灰度验证 | 先小流量(10%)观察,再放量 | 直接全量上线 |
| 记录复盘 | 写下前后数据与结论 | 优化完就散了,下次重来 |
二、要看哪些指标
应用层(接口维度):
| 指标 | 为什么 |
|---|---|
| P50 / P95 / P99 | 平均值会被极少慢请求拉偏或被大量快请求掩盖;看 P99 才有意义 |
| 错误率与非 2xx 分布 | 优化不应以错误率上升为代价 |
| QPS 与并发数 | 判断是容量问题还是单请求性能问题 |
依赖层:
| 指标 | 健康信号 |
|---|---|
| 数据库慢查询数量 / 索引命中率 | 见第 01 章 |
| 锁等待时长、死锁次数 | 见第 02 章 |
| 连接池使用率 | 持续接近上限说明容量不足 |
| 缓存命中率 | ≥95%(热点场景) |
| MQ 堆积量与消费延迟 | 持续增长说明消费能力不足 |
| 下游依赖 P99 与错误率 | 决定是否需要熔断/降级 |
资源层: CPU、内存、磁盘 IO、网络。多数"数据库慢"最后查出来是磁盘 IO 打满。
三、区分两类指标:资源指标 vs 目标指标
一个实用提醒:CPU、内存这类是资源指标,用来排查;而延迟、错误率、吞吐这类是目标指标,用来判断好不好。这两者经常被混淆。
- 资源指标回答"系统内部发生了什么";
- 目标指标回答"用户体验到了什么";
- 告警应该主要挂在目标指标上(用户感受到的慢才是故障),资源指标用来定位原因。
四、压测:验证而不是表演
wrk -t4 -c100 -d60s http://localhost:8000/api/orders # 固定并发打一段时间
locust -f locustfile.py --headless -u 500 -r 50 -t 3m # 逐步加压
三条纪律:
- 一次只变一个变量(并发数、数据量、开关),否则无法归因;
- 跑到系统饱和为止,画出"QPS - 延迟 - 错误率"随并发变化的三条曲线,拐点就是容量上限;
- 压测环境要接近生产(数据量级、索引、配置),否则结果没有参考价值。
典型的发现:延迟在某个并发点之后陡升而 QPS 不再增长——这个点就是系统的实际承载上限,也应该是限流阈值的设定依据(见《后端高并发》第 03 章)。
五、一份优化记录应该长什么样
【现象】订单列表接口 P99 1.2s,超过目标 500ms
【定位】EXPLAIN 显示 type=ALL;慢日志中该 SQL 占总耗时 68%
【改动】添加联合索引 (tenant_id, status, created_at),改为覆盖索引
【验证】灰度 10%:P99 1.2s → 280ms;全量后 DB CPU 下降 35%
【副作用】写入耗时 +3ms(可接受),索引磁盘 +400MB
【回滚方案】DROP INDEX idx_tenant_status_created
【下次】该表数据量达 N 万时重新评估索引有效性
注意其中两栏:副作用与回滚方案。缺少它们的优化记录是不完整的——任何优化都是在交换某种代价。
动手:可观察结果
| 产出 | 判断标准 |
|---|---|
| 三条压测曲线 | QPS-延迟-错误率随并发的变化图,标出拐点 |
| 一次完整优化记录 | 包含上面七个字段,尤其是副作用与回滚方案 |
| 一个核心看板 | 至少有:接口 P99、错误率、DB 慢查询数、缓存命中率、MQ 堆积 |
| 一次灰度演练 | 10% 流量验证后再全量的完整过程 |
完成标志:给定一个"系统变慢"的报告,你能在不看代码的情况下,通过看板把范围收敛到某一类资源或某一个依赖。
故障注入
| 注入方式 | 观察 |
|---|---|
| 只优化不测量 | 事后能否说出这次改动带来了多少提升(通常说不出) |
| 一次改五处 | 出问题后能否定位是哪一处导致的 |
| 跳过错峰直接全量 | 优化有副作用时的影响面 |
| 压测环境与生产数据量级差异巨大 | 结论是否在生产失效 |
| 只看平均响应时间 | P99 问题是否被均值掩盖 |
自测题
- 为什么优化要一次只改一处?给出两个理由。
- 平均响应时间掩盖了什么问题?为什么看 P99?
- 资源指标与目标指标的区别是什么?告警应该挂在哪一类?
- 压测的"拐点"意味着什么?它和限流阈值的关系是什么?
- 为什么优化记录里必须写副作用和回滚方案?举一个你经历过的反例。