性能评审清单

性能评审关注系统的响应时间、吞吐量、资源消耗。性能问题往往在上线后才暴露,修复成本高。在设计阶段就把性能考虑进去,能避免大量后期优化工作。

要点

  • 性能评审关注响应时间、吞吐量、资源消耗
  • 重点检查:P50/P95/P99 延迟、缓存策略、数据库优化、并发处理
  • 不要过早优化,先定位瓶颈再优化
  • Cloudflare Workers 有 CPU 时间限制,要注意计算密集型任务

1. 适用时机

  • 新功能上线(评估对整体性能的影响)
  • 现有功能重构(评估性能变化)
  • 数据量增长预期大(评估当前架构能否支撑)
  • 出现性能问题后(定位瓶颈,制定优化方案)
  • 引入新的技术组件(评估性能特征)

2. 评审前准备

  • 性能目标:P50、P95、P99 响应时间目标是多少?
  • 负载估算:预期 QPS、并发用户数、数据量
  • 性能基线:当前系统的性能数据是多少?
  • 压测报告:有压测数据吗?瓶颈在哪里?
  • 资源使用:CPU、内存、磁盘、网络的当前使用率

3. 核心检查项

3.1 响应时间

  • 关键接口的响应时间目标明确(P50/P95/P99)
  • 响应时间可测量(有监控、有追踪)
  • 慢查询识别和优化(数据库、外部调用)
  • 同步 vs 异步选择合理(长操作异步化)
  • 首屏/首字节时间优化(流式响应、预加载)

Hono 示例:性能监控

import { Hono } from 'hono'
 
const app = new Hono()
 
// 性能监控中间件
app.use('*', async (c, next) => {
  const start = performance.now()
 
  await next()
 
  const duration = performance.now() - start
 
  // 记录性能指标
  c.header('X-Response-Time', `${duration.toFixed(2)}ms`)
 
  // 慢请求告警
  if (duration > 1000) {
    console.warn(`Slow request: ${c.req.method} ${c.req.path} took ${duration.toFixed(2)}ms`)
  }
 
  // 发送指标到监控系统
  await c.env.KV.put(
    `metrics:${c.req.path}:${Date.now()}`,
    JSON.stringify({
      method: c.req.method,
      path: c.req.path,
      duration,
      status: c.res.status
    }),
    { expirationTtl: 3600 }
  )
})
 
// 批量查询优化
app.get('/api/users/with-orders', async (c) => {
  // 坏的代码:N+1 查询
  // const users = await db.users.findMany()
  // for (const user of users) {
  //   user.orders = await db.orders.findByUserId(user.id)  // N 次查询
  // }
 
  // 好的代码:批量查询
  const users = await c.env.DB.prepare('SELECT * FROM users LIMIT 100').all()
 
  const userIds = users.results.map(u => u.id)
  const orders = await c.env.DB.prepare(
    `SELECT * FROM orders WHERE user_id IN (${userIds.map(() => '?').join(',')})`
  )
    .bind(...userIds)
    .all()
 
  // 在内存中关联
  const ordersByUser = new Map()
  for (const order of orders.results) {
    if (!ordersByUser.has(order.user_id)) {
      ordersByUser.set(order.user_id, [])
    }
    ordersByUser.get(order.user_id).push(order)
  }
 
  const result = users.results.map(user => ({
    ...user,
    orders: ordersByUser.get(user.id) || []
  }))
 
  return c.json(result)
})

3.2 吞吐量

  • 系统容量评估(最大 QPS、最大并发)
  • 压测覆盖(峰值负载、持续负载、压力测试)
  • 瓶颈识别(CPU、内存、IO、网络、数据库)
  • 水平扩展能力(加机器能线性提升吗?)
  • 限流策略(防止过载)

3.3 数据库性能

  • 查询优化(EXPLAIN 分析、索引覆盖)
  • 连接池配置合理
  • 批量操作代替循环单条操作
  • 避免 N+1 查询
  • 大数据量分页(游标分页而不是 OFFSET)
  • 读写分离(读多写少场景)
  • 缓存热点数据

Hono 示例:分页优化

import { Hono } from 'hono'
 
const app = new Hono()
 
// 坏的分页:OFFSET 性能差
app.get('/api/orders', async (c) => {
  const page = parseInt(c.req.query('page') || '1')
  const limit = 20
  const offset = (page - 1) * limit
 
  // OFFSET 越大越慢
  const orders = await c.env.DB.prepare(
    'SELECT * FROM orders ORDER BY created_at DESC LIMIT ? OFFSET ?'
  )
    .bind(limit, offset)
    .all()
 
  return c.json(orders.results)
})
 
// 好的分页:游标分页
app.get('/api/orders', async (c) => {
  const cursor = c.req.query('cursor')
  const limit = 20
 
  let query = 'SELECT * FROM orders'
  let params: any[] = []
 
  if (cursor) {
    query += ' WHERE created_at < ?'
    params.push(new Date(cursor).getTime())
  }
 
  query += ' ORDER BY created_at DESC LIMIT ?'
  params.push(limit + 1) // 多查一条,判断是否有下一页
 
  const orders = await c.env.DB.prepare(query)
    .bind(...params)
    .all()
 
  const hasMore = orders.results.length > limit
  const data = hasMore ? orders.results.slice(0, limit) : orders.results
 
  const nextCursor = hasMore
    ? new Date(data[data.length - 1].created_at).toISOString()
    : null
 
  return c.json({
    data,
    pagination: {
      hasMore,
      nextCursor
    }
  })
})
 
// 批量插入优化
app.post('/api/orders/bulk', async (c) => {
  const { orders } = await c.req.json()
 
  // 坏的代码:循环单条插入
  // for (const order of orders) {
  //   await db.insert(order)  // N 次插入
  // }
 
  // 好的代码:批量插入
  const values = orders.map((o: any) => `('${o.id}', '${o.userId}', ${o.amount})`)
  const sql = `INSERT INTO orders (id, user_id, amount) VALUES ${values.join(',')}`
 
  await c.env.DB.prepare(sql).run()
 
  return c.json({ success: true, count: orders.length })
})

3.4 缓存策略

  • 缓存命中率目标(> 80% 是健康值)
  • 缓存层级明确(内存 → CDN → KV → 数据库)
  • 缓存键设计合理(避免冲突、避免过长)
  • 缓存失效策略(TTL、主动失效、版本号)
  • 缓存穿透、击穿、雪崩防护
  • 缓存一致性(与数据库的同步策略)

Hono 示例:多级缓存

import { Hono } from 'hono'
 
const app = new Hono()
 
// 内存缓存(Worker 实例级别)
const memoryCache = new Map<string, { data: any; expireAt: number }>()
 
// 多级缓存查询
async function getCachedData(
  env: Env,
  key: string,
  fetcher: () => Promise<any>,
  ttl = 60
): Promise<any> {
  // Level 1: 内存缓存
  const memCached = memoryCache.get(key)
  if (memCached && memCached.expireAt > Date.now()) {
    return memCached.data
  }
 
  // Level 2: KV 缓存
  const kvCached = await env.KV.get(key, 'json')
  if (kvCached) {
    // 回填内存缓存
    memoryCache.set(key, {
      data: kvCached,
      expireAt: Date.now() + ttl * 1000
    })
    return kvCached
  }
 
  // Level 3: 数据库
  const data = await fetcher()
 
  // 写入缓存
  await env.KV.put(key, JSON.stringify(data), { expirationTtl: ttl })
  memoryCache.set(key, {
    data,
    expireAt: Date.now() + ttl * 1000
  })
 
  return data
}
 
// 使用示例
app.get('/api/users/:id', async (c) => {
  const userId = c.req.param('id')
 
  const user = await getCachedData(
    c.env,
    `user:${userId}`,
    async () => {
      return c.env.DB.prepare('SELECT * FROM users WHERE id = ?')
        .bind(userId)
        .first()
    },
    300 // 5 分钟缓存
  )
 
  if (!user) {
    return c.json({ error: 'User not found' }, 404)
  }
 
  return c.json(user)
})
 
// 缓存失效:数据更新时主动失效
app.put('/api/users/:id', async (c) => {
  const userId = c.req.param('id')
  const body = await c.req.json()
 
  await c.env.DB.prepare('UPDATE users SET name = ? WHERE id = ?')
    .bind(body.name, userId)
    .run()
 
  // 主动失效缓存
  await c.env.KV.delete(`user:${userId}`)
  memoryCache.delete(`user:${userId}`)
 
  return c.json({ success: true })
})
 
// 防止缓存穿透:空值也缓存
app.get('/api/products/:id', async (c) => {
  const productId = c.req.param('id')
  const cacheKey = `product:${productId}`
 
  const cached = await c.env.KV.get(cacheKey, 'json')
  if (cached !== null) {
    if (cached === 'NULL') {
      return c.json({ error: 'Product not found' }, 404)
    }
    return c.json(cached)
  }
 
  const product = await c.env.DB.prepare('SELECT * FROM products WHERE id = ?')
    .bind(productId)
    .first()
 
  if (!product) {
    // 缓存空值,防止穿透
    await c.env.KV.put(cacheKey, 'NULL', { expirationTtl: 60 })
    return c.json({ error: 'Product not found' }, 404)
  }
 
  await c.env.KV.put(cacheKey, JSON.stringify(product), { expirationTtl: 3600 })
  return c.json(product)
})
 
// 防止缓存雪崩:TTL 加随机抖动
async function setCacheWithJitter(env: Env, key: string, data: any, baseTtl: number) {
  const jitter = Math.random() * 60 // 0-60 秒随机抖动
  const ttl = baseTtl + jitter
  await env.KV.put(key, JSON.stringify(data), { expirationTtl: ttl })
}

3.5 资源使用

  • 内存使用合理(不一次性加载大文件)
  • CPU 使用合理(不跑 CPU 密集型任务在 Web 进程)
  • 磁盘 IO 合理(日志、临时文件清理)
  • 网络带宽合理(压缩、CDN、分片)
  • 连接数合理(连接池、长连接复用)

Cloudflare Workers CPU 限制

// Cloudflare Workers 有 CPU 时间限制
// 免费版:10ms CPU 时间
// 付费版:50ms CPU 时间
 
// 避免 CPU 密集型任务
app.post('/api/process', async (c) => {
  const data = await c.req.json()
 
  // 坏的做法:在 Worker 中处理大文件
  // const result = heavyComputation(data)  // 可能超时
 
  // 好的做法:异步处理
  const taskId = crypto.randomUUID()
 
  // 发送到队列
  await c.env.QUEUE.send({ taskId, data })
 
  // 立即返回任务 ID
  return c.json({ taskId, status: 'processing' }, 202)
})
 
// 客户端轮询结果
app.get('/api/tasks/:id', async (c) => {
  const taskId = c.req.param('id')
 
  const task = await c.env.KV.get(`task:${taskId}`, 'json')
 
  if (!task) {
    return c.json({ error: 'Task not found' }, 404)
  }
 
  return c.json(task)
})

3.6 并发与异步

  • 并发处理合理(线程池、协程池配置)
  • 异步任务处理(队列、后台任务)
  • 锁粒度合理(避免大范围锁、死锁)
  • 竞态条件处理(乐观锁、CAS)
  • 超时设置合理(不无限等待)

3.7 前端性能(如涉及)

  • 资源加载优化(懒加载、预加载、代码分割)
  • 渲染性能(虚拟列表、防抖节流)
  • 包体积优化(tree-shaking、压缩)
  • 缓存策略(静态资源长缓存、HTML 短缓存)
  • CDN 使用(静态资源、API 响应)

3.8 监控与告警

  • 性能指标监控(响应时间、QPS、错误率)
  • 资源使用监控(CPU、内存、磁盘、网络)
  • 慢查询监控(数据库、外部调用)
  • 性能退化告警(响应时间突然变长)
  • 容量预警(资源使用率超过阈值)

3.9 优化空间评估

  • 当前性能是否满足需求?不满足差多少?
  • 优化优先级(先优化瓶颈最大的环节)
  • 优化成本收益(投入多少人力,提升多少性能)
  • 长期优化路径(架构调整、技术升级)

4. 评审会上容易漏的点

  1. P99 延迟:问「最慢的 1% 请求需要多久?为什么?」
  2. 数据增长:问「数据量翻 10 倍后,这个查询还能这么快吗?」
  3. 缓存失效:问「缓存失效的瞬间会发生什么?」
  4. 并发场景:问「100 个用户同时做这个操作,系统会怎样?」
  5. 资源上限:问「内存/磁盘/网络用完了会怎样?」

5. 通过标准

  • 性能目标明确且可达
  • 压测数据支撑(有真实数据,不是拍脑袋)
  • 瓶颈识别清楚,有优化方案
  • 缓存策略合理
  • 监控告警覆盖
  • 资源使用在安全范围内

6. 反模式

  • 过早优化:还没定位瓶颈就盲目优化
  • 只看平均:只看平均响应时间,不看 P95/P99
  • 忽视数据库:应用层优化再好,数据库慢查询也白搭
  • 不压测:凭感觉判断性能,没有数据支撑
  • 缓存滥用:什么都缓存,导致一致性问题

7. 维护建议

性能评审清单需要随系统演进更新:

  • 数据规模增长后,调整查询性能和缓存相关要求
  • 引入新技术组件时,补充对应性能检查项
  • 出现性能事故后,把根因对应的检查项加进来
  • 每半年复审一次,删除过时的检查项