性能剖析
优化前测量——用剖析器找真实瓶颈而非猜测。
为什么
无测量优化是赌博。开发者'知道' DB 慢加缓存——但真瓶颈是缓存帮不了的 N+1 ORM 查询,现又加脏数据 bug。剖析器(CPU、火焰图、APM trace)精确显时间去哪:60% serializeUser、30% DB、10% render。修 serializeUser(3 行改)得 2 倍速;无剖析你花一周做缓存。规则:剖析、找顶项、只修那、重剖析。重复。这比'优化一切看似慢的'快 10 倍。
何时使用
用户操作慢于目标时(如 API > 200ms、页 > 2s)。剖析+修约 30-60 分钟。已达标操作可跳过。
如何做
1. 定义目标并测当前 - "结账 API 须 < 200ms p95";测:850ms p95 - 用生产 trace 非本地合成——本地无真实数据量 2. 捕慢路径剖析 - Node:--cpu-prof 标志,用 clinic.js 或 0x 检视 - 浏览器:Chrome DevTools Performance 标签,录交互 - DB:慢查询 EXPLAIN ANALYZE 3. 读火焰图 / trace 找最大条 - 忽 10% 下——修 2% 瓶颈不动 p95 - 记函数名和行 4. 仅修那一瓶颈——一改一 PR - 常见赢:N+1 → join、同步 JSON.parse → 流、缺索引、纯函数 memoize - 重测:p95 降?否则修无效——回退重剖析 5. 达标即停——勿镀金 - "够快"是真条;进一步优化递减回报增复杂度
示例
# 慢 API 的 Node CPU 剖析 node --cpu-prof --cpu-prof-name=checkout.cpuprofile server.js # 然后载 checkout.cpuprofile 入 chrome://inspect → CPU profiler # 0x 火焰图(一命令,开浏览器) npx 0x server.js # 打慢端点,Ctrl+C → 0x 生 flamegraph.html # Postgres——找慢查询及其计划 EXPLAIN ANALYZE SELECT * FROM orders o JOIN items i ON i.order_id = o.id WHERE o.user_id = 42; # Seq Scan on orders (cost=0..8500 rows=10000) ← 缺索引! CREATE INDEX idx_orders_user_id ON orders(user_id); # 重跑 EXPLAIN ANALYZE → Index Scan,8ms
常见问题
- ⚠本地 10 行剖析,'修'prod 规模(1M 行)不存在的瓶颈。
- ⚠一 PR 优化 5 处,见 30% 赢不知哪个驱动——后回归。
- ⚠剖析前加缓存,藏真瓶颈(N+1)并引脏数据 bug。
- ⚠过目标镀金——p95 180ms(目标 200ms)花一周到 120ms 用户感知不到。
最佳实践
- ✓剖析生产(或真实数据量 prod-like staging)——本地剖析撒谎因数据形状错。
- ✓每 PR 一优化 ,描述含前后 p95,赢可归因可审。
- ✓设 SLO 预算(如 p95 < 200ms),耗 50% 预算告警——用户注意前抓回归。
- ✓每修后重剖析;瓶颈移动,第二大条现是应修的。