文档目录

0.2 吞吐、延迟、成本的三方权衡

上一节:0.1 三类问题必须分开处理 | 下一节:0.3 延迟是分布,不是数字


一句话结论

「让性能变好」是一句没有意义的话。 任何优化都是在「吞吐量、延迟、资源成本」三者之间做取舍——你必须说清楚:在什么成本约束下,把哪个指标改善到什么程度。


一、用一个快递公司的故事理解三者关系

假设你开了一家快递站,每天要送 1000 个包裹:

策略 吞吐量 单个包裹的延迟 成本
一件一送:每来一个包裹就派一辆车 低(一天最多几十趟) 极低(来了就走) 极高(要几十辆车、几十个司机)
攒够 100 件送一趟 高(10 趟送完) 高(第一个包裹要等到第 100 个才出发) 低(一辆车就够)

看出来了吗?「吞吐高」和「延迟低」在这里是直接冲突的。

再看第三个维度:那辆车的载重、司机的数量、仓库的大小——这就是资源成本。想同时提高吞吐、降低延迟,唯一办法是加钱(更多车辆、更多司机)。

性能优化就是这么回事:

性能优化的本质,是在给定的成本预算下,重新分配「吞吐—延迟」这一对矛盾。

所以当有人说「我要性能更好」时,你必须追问:

「你能接受多大成本?你更在意「每秒处理更多」还是「每个请求更快」?」


二、三个指标的通俗定义与精确含义

2.1 吞吐量(Throughput)

  • 通俗:单位时间能干完多少活。
  • 精确:每秒完成的请求数(req/s、QPS、RPS)。注意分母是「成功完成」的请求数——返回 500 的错误请求不该算进吞吐。

2.2 延迟(Latency / Response Time)

  • 通俗:一个请求从发出到收到结果,等了多久。
  • 精确:必须说明是哪个点测的——客户端观测、网关观测、服务端 handler 内部,三者数值不同(第 1 章会展开)。

2.3 资源成本(Cost / Resource)

  • 通俗:为了达到上面两个数字,花了多少钱。
  • 精确:CPU 核数、内存、连接数、副本数、以及这些折算成的账单。

记住:延迟和吞吐是「结果」,成本是「代价」。只谈前两个而不谈代价的性能报告,是不完整的。


三、一个具体的数字例子:批量 vs 单条

假设你在写一个批量导入功能,要往数据库插 1000 行。

方案 A:逐条插入

每条 INSERT 往返一次数据库,耗时 5 ms
总耗时 = 1000 × 5 ms = 5000 ms(5 秒)
第 1 行的延迟 = 5 ms
吞吐 = 1000 / 5s = 200 行/秒

方案 B:批量插入(每 1000 行一次)

一次批量 INSERT 耗时 200 ms
总耗时 = 200 ms
第 1 行的延迟 = 200 ms(它必须等到整批拼好才能发出去)
吞吐 = 1000 / 0.2s = 5000 行/秒

对比:

总耗时 吞吐 第 1 行的延迟
方案 A 5000 ms 200 行/秒 5 ms
方案 B 200 ms 5000 行/秒 200 ms
变化 快 25 倍 高 25 倍 慢 40 倍

注意最后一行:吞吐提升 25 倍的代价,是「单个请求的延迟变差了 40 倍」。

这在导入、导出、批处理场景是划算的(用户本来就要等整体完成)。但如果这是一个用户点一下要立刻看到结果的接口,方案 B 就是灾难。

这就是为什么「批量处理」这个优化手段不能无脑用——要先看清这个接口是「吞吐敏感」还是「延迟敏感」。


四、常见优化手段的「代价清单」

每一项优化都在消耗某样东西。下表请记住,第 7 章会展开:

优化手段 提升什么 牺牲什么
批处理 / 请求合并 吞吐 单请求延迟(要等攒批)
加缓存 平均延迟、吞吐 一致性、内存成本、失效逻辑复杂度
加并发 / 加连接池 吞吐(到某点为止) 下游压力、上下文切换、饱和风险
加大批量并行度 吞吐 尾延迟(最慢的那个决定整体)
降低超时时间 快速失败、保护自己 成功率(本来能成功的变超时)
重试 成功率 放大故障、下游压力、尾延迟恶化
减少功能/降级 全部 业务价值

最后一行很重要:「不做这个功能」往往是性价比最高的优化,但它不是技术决策,而是产品决策。


五、怎么把「取舍」变成可执行的目标

模糊的目标 → 可执行的目标,需要补三个要素:

模糊说法 缺什么 可执行的说法
「接口要快」 指标 + 阈值 「P99 < 100 ms」
「P99 < 100 ms」 负载前提 「在 500 QPS、100 万行数据下」
「500 QPS 下 P99 < 100 ms」 成本约束 + 测量点 「单实例 4C8G、无缓存、从客户端观测」

这三步做完,你才有一个可以被判定通过/失败的目标。第 1 章会把这件事做成标准表格(SLO 表 + 延迟预算表)。


六、本节小结

  1. 性能永远是三方的:吞吐量、延迟、资源成本,不可能同时最优。
  2. 优化的本质是「在成本约束下重新分配吞吐与延迟」。
  3. 批处理类优化会显著抬高单请求延迟——用之前先判断这个接口是吞吐敏感还是延迟敏感。
  4. 每种优化都有代价,把它写进报告,别只写收益。
  5. 「在 X 成本下,把 Y 指标改善到 Z」——这才是完整的目标陈述。

七、自测

  1. 产品经理说:「这个列表接口返回 1000 条太慢了,改成一次返回 10 条但支持翻页。」这个改动提升了什么、牺牲了什么?
  2. 你的服务在 500 QPS 时 P99 = 80 ms,团队想把 P99 降到 40 ms。除了改代码,还有哪两种「非代码」的方式?它们各有什么代价?
  3. 为什么「添加缓存」这条优化,有时会让 P99 变得更差?(提示:想想缓存未命中的那些请求)
  1. 提升:单次请求的响应体积变小 → 延迟降低、带宽降低、序列化成本降低,用户体验更快。牺牲:总请求数上升(翻页会发多次请求)、服务端总查询次数上升、若用户翻到很后面则总延迟反而更长(深分页问题)。本质上是用「更多次轻请求」换「单次重请求」,属于吞吐—延迟的重新分配。
  2. ① 加缓存——代价是一致性风险与内存成本;② 加机器/加副本——代价是钱,且如果瓶颈是共享数据库,加副本可能无效甚至更糟。还有第三种:降低功能复杂度(比如不做实时聚合),代价是业务价值。
  3. 因为缓存只加速「命中」的请求。如果命中率只有 60%,剩下 40% 未命中的请求不仅要查数据库,还要承受缓存层自己带来的开销(序列化、网络、锁)。结果:60% 的请求变快,40% 的请求略微变慢 → P50 变好,P99 可能变差。这正说明「只看平均值会做出错误决策」。