性能优化规范
性能基线、压测、瓶颈分析与优化
[Requirement] 每个核心服务必须建立性能基线,作为优化和回归的参照。基线指标包括:
| 指标 | 说明 | 采集方式 |
|---|---|---|
| QPS | 每秒请求数 | 压测 / 生产监控 |
| 响应时间 | P50 / P95 / P99 | 压测 / APM |
| 错误率 | 失败请求占比 | 压测 / 监控 |
| 资源使用率 | CPU / 内存 / 网络 | 监控 |
| 吞吐量 | 单位时间处理量 | 压测 |
[Standard] 在 staging 环境(与 prod 同构)进行压测,建立性能基线。基线数据包含:
- 不同并发下的 QPS、响应时间、错误率
- 资源使用率随负载变化的曲线
- 服务的最大承载能力(拐点)
[Standard] 性能基线必须文档化,纳入版本管理,随架构变更更新。
# user-service 性能基线
最后更新:2024-01-15
环境:staging(同 prod 配置)
| 并发 | QPS | P50(ms) | P99(ms) | 错误率 | CPU | 内存 |
|------|------|---------|---------|--------|------|-------|
| 10 | 500 | 20 | 80 | 0% | 30% | 40% |
| 50 | 2000 | 25 | 120 | 0% | 60% | 45% |
| 100 | 3500 | 35 | 200 | 0.1% | 85% | 50% |
| 200 | 4000 | 80 | 500 | 2% | 95% | 60% |
最大承载 QPS:3500(并发 100 时,错误率 < 0.1%)[Standard] 每次重大变更(架构调整、核心逻辑改动)后,重新压测并与基线对比:
- 响应时间劣化 > 20% → 需分析原因
- 吞吐量下降 > 15% → 需分析原因
- 错误率上升 > 0.5% → 阻塞发布
| 类型 | 目的 | 场景 |
|---|---|---|
| 基准测试 | 建立基线 | 单接口、固定并发 |
| 负载测试 | 验证预期负载下的表现 | 日常峰值流量 |
| 压力测试 | 找到系统极限 | 逐步加压直到崩溃 |
| 稳定性测试 | 验证长时间运行稳定性 | 持续 24 小时以上 |
| 容量测试 | 确定最大容量 | 找出吞吐量拐点 |
[Standard] 压测遵循以下流程:
明确目标 → 准备环境 → 准备数据 → 编写脚本 → 执行压测 → 分析结果 → 输出报告[Standard] 根据场景选择压测工具:
| 工具 | 适用场景 | 特点 |
|---|---|---|
| wrk / wrk2 | HTTP 接口 | 轻量、高性能 |
| k6 | HTTP/WS/GRPC | 脚本化、可扩展 |
| JMeter | 复杂场景 | 图形化、协议丰富 |
| Locust | 自定义场景 | Python 脚本、分布式 |
| Go-stress | Go 服务 | 原生、低开销 |
# 基础压测:100 并发,持续 30 秒
wrk -t4 -c100 -d30s --latency http://api.example.com/users
# 带请求体的 POST 压测
wrk -t4 -c100 -d30s -s post.lua http://api.example.com/orders-- post.lua
wrk.method = "POST"
wrk.body = '{"user_id": 123, "product_id": 456}'
wrk.headers["Content-Type"] = "application/json"[Requirement] 压测必须在 staging 环境(与 prod 同构)进行,禁止在 prod 环境直接压测(除全链路压测外)。
[Standard] 压测前必须:
- 通知相关团队,避免影响测试
- 确认压测环境数据量与 prod 同量级
- 确认监控已开启,可采集性能指标
- 确认无其他任务占用资源
[Standard] 压测数据量必须与 prod 同量级,避免数据量差异导致性能误判。
| 数据表 | prod 数据量 | 压测数据量 |
|---|---|---|
| users | 1000 万 | ≥ 1000 万 |
| orders | 5000 万 | ≥ 5000 万 |
| products | 10 万 | ≥ 10 万 |
- ❌ 在 dev 环境压测,数据量和机器配置与 prod 差异巨大。
- ❌ 压测脚本只测一个接口,不模拟真实业务场景。
- ❌ 压测前不通知,影响其他团队的测试工作。
[Standard] 性能瓶颈分析按以下顺序逐层排查:
客户端 → 网络 → 负载均衡 → 应用服务 → 缓存 → 数据库 → 外部依赖| 层级 | 瓶颈表现 | 排查方法 |
|---|---|---|
| 应用层 | CPU 高、GC 频繁、线程阻塞 | Profiler(pprof / async-profiler / Arthas) |
| 数据库层 | 慢 SQL、连接池耗尽、锁等待 | 慢查询日志、执行计划、锁监控 |
| 缓存层 | 缓存击穿、命中率低 | 缓存命中率监控、热点 Key 分析 |
| 网络层 | 延迟高、丢包 | ping / mtr / tcpdump |
| 外部依赖 | 第三方接口慢 | 调用耗时监控、熔断状态 |
Java Arthas 示例:
# 查看最耗时的方法
trace com.example.UserService getUserById
# 查看方法调用栈
stack com.example.UserService getUserById
# 监控方法执行耗时
monitor -c 5 com.example.UserService getUserByIdGo pprof 示例:
# 采集 CPU profile
go tool pprof http://localhost:6060/debug/pprof/profile?seconds=30
# 采集内存 profile
go tool pprof http://localhost:6060/debug/pprof/heap
# 火焰图
go-torch -u http://localhost:6060 -t 30Python py-spy 示例:
# 实时查看函数调用
py-spy top --pid 12345
# 生成火焰图
py-spy record -o profile.svg --pid 12345[Standard] 慢查询排查步骤:
# 1. 开启慢查询日志,设置阈值 100ms
SET GLOBAL slow_query_log = ON;
SET GLOBAL long_query_time = 0.1;
# 2. 查看慢查询
SELECT * FROM mysql.slow_log ORDER BY query_time DESC LIMIT 10;
# 3. 分析执行计划
EXPLAIN SELECT * FROM orders WHERE user_id = 123;[Caution] 禁止在生产环境对大表执行无索引的查询或全表扫描。详细规则见 慢SQL治理规范。
[Standard] 瓶颈定位遵循”先定位再优化”原则:
- 先通过监控和日志缩小瓶颈范围
- 再用 Profiler 精确定位瓶颈代码
- 最后针对性优化,避免盲目优化
[Caution] 禁止在未定位瓶颈前盲目优化,可能优化了非瓶颈,浪费精力且无效果。
| 优化方向 | 具体措施 | 预期收益 |
|---|---|---|
| 算法优化 | 降低时间复杂度(O(n²) → O(n log n)) | 高 |
| 并发优化 | 异步处理、线程池调优、协程 | 中-高 |
| 缓存优化 | 本地缓存、分布式缓存、缓存预热 | 高 |
| 批处理 | 批量查询、批量写入 | 中 |
| 连接池 | 数据库/HTTP 连接池调优 | 中 |
| 对象复用 | 避免频繁创建对象、对象池 | 中 |
| 序列化优化 | 切换高效序列化(Protobuf / JSON 优化) | 中 |
| 优化方向 | 具体措施 | 预期收益 |
|---|---|---|
| 索引优化 | 合理添加索引、覆盖索引、联合索引 | 高 |
| SQL 优化 | 避免 SELECT *、减少 JOIN、分页优化 | 高 |
| 分库分表 | 读写分离、水平/垂直拆分 | 高 |
| 连接池 | 合理配置连接池大小 | 中 |
| 读写分离 | 读操作走从库 | 中 |
| 数据归档 | 冷数据归档,减少热数据量 | 中 |
[Standard] 缓存策略选择:
| 场景 | 策略 | 说明 |
|---|---|---|
| 读多写少 | Cache-Aside | 先读缓存,未命中读 DB 再写缓存 |
| 写多读少 | Write-Through | 写时同步更新缓存 |
| 高并发写 | Write-Behind | 异步写 DB,削峰填谷 |
[Caution] 缓存三大问题必须有对应方案:
| 问题 | 解决方案 |
|---|---|
| 缓存穿透 | 布隆过滤器 / 缓存空值 |
| 缓存击穿 | 互斥锁 / 永不过期 + 异步更新 |
| 缓存雪崩 | 过期时间加随机值 / 多级缓存 |
| 优化方向 | 具体措施 |
|---|---|
| 服务拆分 | 单体拆微服务,独立扩容 |
| 读写分离 | 读操作走只读副本 |
| 消息队列 | 异步解耦、削峰填谷 |
| CDN | 静态资源边缘加速 |
| 负载均衡 | 多实例水平扩展 |
| 就近访问 | 多机房部署、边缘计算 |
[Requirement] 任何优化措施上线前,必须在 staging 环境压测验证,确认性能提升且无副作用。
[Standard] 优化验证对比模板:
| 指标 | 优化前 | 优化后 | 提升 |
|---|---|---|---|
| QPS | 2000 | 5000 | +150% |
| P99 延迟 | 500ms | 120ms | -76% |
| CPU 使用率 | 85% | 45% | -47% |
| 错误率 | 1% | 0% | -100% |
- ❌ 过早优化:在未确认瓶颈前就优化代码。
- ❌ 过度优化:为了 1% 的性能提升牺牲可读性和可维护性。
- ❌ 优化后不压测验证,直接上线。
[Requirement] 生产环境必须实时监控以下性能指标,设置告警阈值:
| 指标 | 告警阈值 | 级别 |
|---|---|---|
| P99 响应时间 | > 2s 持续 5 分钟 | P1 |
| P95 响应时间 | > 1s 持续 5 分钟 | P2 |
| QPS 突降 | 较基线下降 > 30% | P1 |
| 错误率 | > 1% | P0 |
| CPU 使用率 | > 85% 持续 5 分钟 | P1 |
| 内存使用率 | > 90% 持续 5 分钟 | P1 |
| GC 停顿 | > 500ms 持续 5 分钟 | P2 |
[Standard] 超过阈值的请求(默认 1s)必须记录详细日志,包含:
- 请求参数(脱敏后)
- 各阶段耗时(路由、业务、DB、缓存、外部调用)
- trace_id(可跳转链路追踪)
{
"trace_id": "abc123",
"slow_request": true,
"cost_ms": 1500,
"breakdown": {
"route": 5,
"business": 200,
"db_query": 800,
"redis": 50,
"external_api": 445
},
"params": { "user_id": 123 }
}[Standard] 监控服务容量水位,提前预警:
| 指标 | 预警阈值 | 动作 |
|---|---|---|
| 服务负载 | 达到最大容量 70% | 预警,准备扩容 |
| 服务负载 | 达到最大容量 85% | 自动扩容 |
| 数据库连接 | 达到连接池 80% | 预警 |
| 磁盘使用率 | > 80% | 预警,清理或扩容 |
[Standard] 核心业务流程必须有全链路监控,可追踪请求从入口到所有依赖的完整耗时。
客户端 → LB → Nginx → 网关 → 业务服务 → DB └→ Redis └→ MQ └→ 外部 API[Suggestion] 使用 APM 工具(SkyWalking / Datadog / New Relic)实现全链路性能监控。
- ❌ 只监控错误率,不监控响应时间,慢请求无人感知。
- ❌ 没有容量水位监控,流量突增时服务被打垮才发现。
- ❌ 慢请求只记录总耗时,不记录各阶段耗时,无法定位瓶颈。