# 13. 性能

如何处理 latency、throughput、profiling、caching 与 performance budget，而不陷入过早优化。

- **Author:** Fedor Chishchin
- **Level:** advanced
- **Reading time:** 约 25 分钟阅读
- **Published:** 2026-05-23
- **Source:** https://fedorthinks.com/zh/learn/software-architect-13-performance

---

## 1. 核心思想

要优化的是 bottleneck，而不是看起来不顺眼的地方。

### Latency 与 throughput 是两件事

> 出租车能把一个人快速送到。地铁则能运送上千人。用同一个"快"字去比较它们毫无意义。

Latency 是一次操作的响应时间。Throughput 是系统在一段时间内处理的操作数量。你可以降低单个请求的 latency，却让系统整体的 throughput 变差——比如不加资源控制地激进 parallelism。

架构师必须把 performance requirement 说得具体：p95 response time、p99 tail latency、requests per second、batch duration、cold start、memory budget。"应该快一点"不是一条需求。

### 缓存意味着对 staleness 负责

> 橱窗里贴出的菜单只要和收银台的价格一致就有用。一旦不一致，它就变成争吵的来源。

Caching 能加速读取，但同时引入 invalidation、TTL、consistency model 与 stale data 的风险。在加缓存之前要先问：到底什么操作昂贵，它多久变化一次，是否可以展示过期值，source of truth 变化时如何使缓存失效。

不理解数据模型就加缓存很危险：你可能只是更快地返回了错误答案。

## 2. 例子与反例

好的优化以测量开始，以验证结果结束。

### 例子：通过 profiling 解决慢 endpoint

> 医生先下诊断，而不是因为病人说"哪里痛"就直接做手术。

`/dashboard` endpoint 的 p95 是 2.8 秒。Tracing 显示 80% 的时间消耗在 N+1 数据库查询，以及缺失 `workspace_id, created_at` 索引上。团队加上 eager loading、索引和一个 performance test。

结果：p95 降到 380 ms。修复是精准的、可测量的，且没有引入额外的架构复杂度。

### 反例：没有 profile 就改写成 Go

> 如果路被封了车堵在路上，换发动机毫无意义。

团队决定把服务改写成另一种语言，理由是"Python 慢"。后来发现 90% 的时间花在外部 API 和设计糟糕的 SQL 查询上。

语言可以是 bottleneck，但需要证明。否则优化就变成一次昂贵的 refactor，对业务没有任何影响。

#### 自检

- 这里真正重要的 performance 指标是哪一个？- 根据 profiler/tracing，bottleneck 在哪里？- 可以接受的 budget 是多少？- 加上缓存后什么会坏掉？

下一模块：[可扩展性](./software-architect-14-scalability)。
