关于 performance
面向排查软件速度或资源问题的开发者,强调先分析性能数据再修改。内容涵盖缓存、数据库查询、内存管理和测量工作流。
主要功能
- 01先分析后优化的工作流
- 02性能预算与基准测试
- 03缓存层级与失效策略
- 04数据库查询优化
- 05内存与资源管理
适用场景
- 01定位 CPU、内存或 I/O 瓶颈
- 02减少重复数据库查询
- 03选择缓存失效策略
- 04验证代码改动的效果
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ krzysztofsurdy/ code-virtuoso --skill performance安装后在 Claude Code 中输入/performance确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/caching-strategies.mddatabase-optimization.mdprofiling-patterns.md详细介绍
性能优化
性能优化遵循一条原则:先测量,再做任何更改。对瓶颈的直觉往往不可靠。每次优化都应从性能分析开始,提出假设,实施有针对性的修复,最后再次测量。
核心原则
| 原则 | 含义 |
|---|---|
| 先测量 | 没有性能分析数据时,绝不进行优化。对瓶颈的直觉并不可靠 |
| 优化关键路径 | 关注运行频率最高或会阻塞用户可见延迟的代码 |
| 设定预算 | 开始前先明确可接受的延迟、吞吐量和资源使用量 |
| 避免过早优化 | 首先编写可读、正确的代码。只有测量结果表明存在实际问题时才进行优化 |
| 了解取舍 | 每项优化都有代价(以内存换速度、以复杂度换吞吐量、以新鲜度换延迟) |
性能分析与基准测试
性能分析能显示时间和资源的去向。没有它,就只能靠猜。
性能分析类型
| 类型 | 揭示内容 | 适用场景 |
|---|---|---|
| CPU 性能分析 | 热点函数、调用频率、执行时间分布 | 请求处理缓慢、CPU 使用率高 |
| 内存性能分析 | 分配速率、堆大小、对象保留情况、内存泄漏 | 内存使用量不断增长、OOM 错误、GC 压力 |
| I/O 性能分析 | 磁盘读写、网络调用、阻塞等待 | 文件操作缓慢、外部服务延迟 |
| 数据库性能分析 | 查询执行时间、每个请求的查询次数、慢查询 | DB 负载高、N+1 查询模式、缺少索引 |
性能分析工作流
- 建立基线:在进行任何更改前,记录正常条件下的指标
- 识别:找出耗时或资源消耗最高的热点
- 提出假设:针对性能缓慢的原因提出具体假设
- 修复:实施单项、有针对性的更改
- 验证:再次测量以确认改进,并检查是否出现回归
性能预算
设定超出后需要采取行动的限制:
- 响应时间:为每个端点设定 P50、P95、P99 延迟目标
- 吞吐量:在预期负载下的最低每秒请求数
- 资源使用量:每项服务的 CPU、内存和连接数限制
- 页面体积:前端资源传输大小上限
参见 性能分析模式参考,了解性能分析工作流、瓶颈特征和负载测试策略。
缓存策略
缓存通过将结果存储在更接近使用位置的地方,避免重复计算和数据获取。
缓存层
| 层级 | 位置 | 延迟 | 使用场景 |
|---|---|---|---|
| L1 - 进程内 | 应用内存(对象缓存、记忆化) | 纳秒级 | 每个请求中多次访问的热点数据 |
| L2 - 分布式 | Redis、Memcached、共享缓存 | 亚毫秒至数毫秒 | 在多个应用实例之间共享的数据 |
| HTTP 缓存 | 浏览器、反向代理(Varnish、Nginx) | 客户端缓存无需网络往返 | 静态资源、可缓存的 API 响应 |
| CDN | 全球各地的边缘服务 | 借助地理位置优势实现低延迟 | 静态文件、预渲染页面、媒体 |
| 数据库缓存 | 查询结果缓存、缓冲池 | 不定 | 重复的相同查询 |
缓存失效方式
| 策略 | 工作方式 | 适用场景 |
|---|---|---|
| 基于 TTL | 缓存条目在固定时长后过期 | 可容忍有限数据陈旧的场景 |
| 基于事件 | 源数据更改时清除缓存 | 写入后数据必须保持最新的场景 |
| 直写 | 同时更新缓存和后端存储 | 需要强一致性的读密集型工作负载 |
| 回写 | 立即更新缓存;异步更新后端存储 | 可接受最终一致性的高写入吞吐量场景 |
防止缓存击穿
热门缓存键过期时,许多并发请求可能会同时尝试重新生成它,从而压垮后端。以下三种方法可以防止这种情况:
- 加锁:只有一个请求负责重新生成;其他请求等待或提供过期数据
- 概率性提前重新计算:请求随机在过期前刷新缓存,将重新生成的操作分散到一段时间内
- 请求合并:将重复的在途请求合并为一次后端调用
有关多语言示例中的实现模式,请参阅 缓存策略参考。
数据库优化
数据库查询是 Web 应用中最常见的性能瓶颈。
索引策略
- 为 WHERE、JOIN 和 ORDER BY 子句中使用的列创建索引
- 使用符合最常见查询模式的复合索引(最左前缀规则)
- 覆盖索引包含查询所需的所有列,因此无需查找表
- 监控未使用的索引,因为它们会拖慢写入,却无法帮助读取
防止 N+1 查询
当代码获取 N 条记录的列表,然后针对每条记录再运行一次查询以加载关联数据时,就会出现 N+1 问题。原本只需 1 次查询,却运行了 N+1 次。
检测信号:
- 查询次数随结果集大小线性增长
- 存在许多几乎相同、仅有一个参数不同的查询
- 性能分析器显示单次页面加载执行了数十次或数百次查询
预防策略:
- 预加载(使用 JOIN 或提前执行单独的批量查询)
- 批量加载(收集 ID,在一次查询中获取所有关联记录)
- DataLoader 模式(在一次请求中进行批量处理和去重)
连接池
建立数据库连接的开销很大(TCP 握手、身份验证、TLS 协商)。连接池会保留一组可重复使用的连接:
- 根据预期并发量设置连接池大小:过小会导致排队,过大会压垮数据库
- 工作完成后立即将连接归还连接池,因为连接泄漏会耗尽连接池
- 设置空闲超时,以回收未使用的连接
- 当应用级连接池不足以满足需求时,使用外部连接池(例如 PostgreSQL 的 PgBouncer)
有关查询模式、执行计划分析和多语言示例,请参阅 数据库优化参考。
内存和资源管理
内存优化模式
| 模式 | 说明 |
|---|---|
| 对象池 | 重复使用开销大的对象,而不是分配后再丢弃 |
| 流式处理 | 将大型数据集作为数据流处理,而不是全部加载到内存中 |
| 延迟初始化 | 推迟创建开销大的对象,直到需要时再创建 |
| 弱引用 | 持有不会阻止垃圾回收的引用 |
| 缓冲区复用 | 只分配一次缓冲区,并在多个操作中重复使用 |
延迟加载
延迟加载会将工作推迟到需要结果时再执行。它可以减少启动时间和内存使用量,但会增加复杂性,也可能在之后造成意外延迟。
适合使用延迟加载的场景:
- 仅在访问时加载关联的数据库记录
- 在首次使用时初始化开销大的服务连接
- 仅在 UI 组件或资源变得可见时加载它们
不适合使用延迟加载的场景:
- 无论如何都会执行延迟的工作时(只会增加开销)
- 将延迟从可预测的启动阶段转移到不可预测的用户交互时
- 导致 N+1 查询模式时(参见上文的数据库优化)
批量操作
在可能的情况下,用批量操作替代单个操作:
- 使用批量插入,而不是一次插入一行
- 使用批量 API 调用,而不是将外部服务调用 N 次
- 使用批量文件操作,而不是逐个处理文件
快速参考:常见瓶颈模式
| 症状 | 可能原因 | 首要调查步骤 |
|---|---|---|
| 响应时间慢,CPU 使用率低 | I/O 等待(数据库、网络、磁盘) | 对 I/O 进行性能分析并检查查询日志 |
| CPU 使用率高,响应时间正常 | 算法效率低或计算量过大 | 进行 CPU 性能分析,找出热点函数 |
| 内存随时间持续增长 | 内存泄漏(引用未释放、缓存无界增长) | 比较不同时间的堆转储 |
| 负载下间歇性变慢 | 资源争用(锁、连接池耗尽) | 检查连接池大小和锁等待时间 |
| 本地运行快,生产环境运行慢 | 网络延迟、缺少缓存、数据量不同 | 比较不同环境中的性能分析数据 |
参考文件
| 参考资料 | 内容 |
|---|---|
| 缓存策略 | 缓存层、失效模式、防止缓存击穿,包含多种语言的示例 |
| 数据库优化 | 查询优化、防止 N+1 问题、连接池、批量操作,包含多种语言的示例 |
| 性能分析模式 | 性能分析工作流、瓶颈特征、性能预算、负载测试策略 |
与其他技能的集成
| 情况 | 推荐技能 |
|---|---|
| 性能问题由架构不佳导致 | clean-architecture |
| 需要重构运行缓慢的代码路径 | refactoring |
| 优化 API 响应时间 | api-design |
| 数据库架构和查询设计 | database-design |
评论
支持 Markdown