目录
[一、压测:不止是测 QPS,更是 "体检"](#一、压测:不止是测 QPS,更是 “体检”)
[1. 摸清楚系统的 "安全阈值"](#1. 摸清楚系统的 “安全阈值”)
[2. 定位性能瓶颈的 "藏身处"](#2. 定位性能瓶颈的 “藏身处”)
[3. 验证系统的 "稳定性"](#3. 验证系统的 “稳定性”)
[第一级优化:代码层 ------ 用最少的成本,挖最大的潜力](#第一级优化:代码层 —— 用最少的成本,挖最大的潜力)
[第二级优化:中间件层 ------ 解决 "拖后腿" 的关键](#第二级优化:中间件层 —— 解决 “拖后腿” 的关键)
[第三级优化:服务器层 ------ 最后的 "扩容" 选项](#第三级优化:服务器层 —— 最后的 “扩容” 选项)
作为后端开发,我们都绕不开一个话题:压测。
曾几何时,我以为压测就是 "跑个 QPS 数字",遇到瓶颈就升级服务器 ------4 核换 8 核,8G 扩 16G,简单粗暴。直到一次线上促销,明明压测过的接口,却在真实流量下延迟飙升、错误率爆表,我才明白:压测的核心不是 "测上限",而是 "找病根";优化的关键,从来不是 "堆硬件",而是 "挖潜力"。
今天就跟大家聊聊,压测到底要测什么,以及如何从服务器、中间件、代码三个层面,完成系统性能的 "三级跳"。
一、压测:不止是测 QPS,更是 "体检"
很多人对压测的理解停留在 "看最高 QPS",这其实是本末倒置。压测的真正目的,是给系统做一次全面的 "体检",核心要完成三件事:
1. 摸清楚系统的 "安全阈值"
我们要找到系统在 "延迟达标 + 错误率为 0" 前提下的最大承载能力 ------ 比如商品详情接口,在 p99 延迟≤200ms、错误率 0% 时,能扛住 1000 QPS,这就是它的 "安全线"。
超过这个阈值,系统可能会出现延迟飙升、连接耗尽,甚至直接崩溃。这个阈值,是我们做容量规划的核心依据。
2. 定位性能瓶颈的 "藏身处"
压测中最有价值的时刻,不是 QPS 冲高的瞬间,而是QPS 停滞、延迟飙升的拐点。这个拐点背后,一定藏着性能瓶颈:
- 是 CPU 打满了?还是内存泄漏了?
- 是数据库慢查询拖了后腿?还是 Redis 连接池耗尽了?
- 是代码里的串行调用太耗时?还是没有做缓存导致重复查询?
这些瓶颈,才是压测要抓的 "真凶"。
3. 验证系统的 "稳定性"
一次合格的压测,绝不是跑 30 秒就结束。我们需要在目标流量的 1.5 倍下,持续压测 1 小时甚至更久 ------ 看内存使用率会不会持续上升(排查内存泄漏),看连接数会不会越积越多(排查连接泄漏),看依赖服务故障时,系统会不会优雅降级(验证容灾能力)。
毕竟,线上的流量是持续的,短时间的 "峰值扛住",不代表长时间的 "稳定运行"。
二、优化:先治本,再治标,性价比最高
找到瓶颈后,优化就有了方向。很多人会先想着 "升级服务器",但我建议遵循 "代码优化→中间件调优→服务器扩容" 的顺序 ------ 前者是 "治本",后者是 "治标",前者的收益往往远超后者。
第一级优化:代码层 ------ 用最少的成本,挖最大的潜力
代码层面的优化,是性价比最高的操作,往往几行代码的改动,就能让 QPS 翻倍。常见的优化点有这几个:
-
干掉 "循环查库",换成批量查询 这是最常见的低级错误。比如查询订单列表时,循环遍历订单 ID 查用户信息,100 个订单就查 100 次数据库。优化方案很简单:用
IN语句批量查询,一次搞定。从 "100 次 IO" 变成 "1 次 IO",延迟直接降一个数量级。 -
把串行调用改成并行 很多接口会串行调用多个依赖服务 ------ 比如查商品详情时,先查商品基本信息,再查库存,再查评价,总耗时是三个接口之和。用 Go 的
goroutine+channel改成并行调用,总耗时直接降到 "最慢的那个接口" 的时间,QPS 提升立竿见影。 -
给热点数据加缓存,减少重复计算 对于不常变化的热点数据(比如商品分类、活动规则),没必要每次请求都查数据库。加一层本地缓存(如
sync.Map)或分布式缓存(如 Redis),设置合理的过期时间,能直接把数据库的压力降到原来的 1/10。 -
设置超时时间,避免协程阻塞调用外部依赖时,一定要设置超时时间 ------ 比如数据库查询超时 50ms,HTTP 调用超时 100ms。否则,一旦依赖服务出问题,大量协程会被阻塞,最终拖垮整个系统。
第二级优化:中间件层 ------ 解决 "拖后腿" 的关键
很多时候,系统的瓶颈不在代码,而在数据库、Redis 这些中间件。毕竟,应用层的性能再高,也架不住中间件的 "拖后腿"。
-
数据库优化:索引是第一生产力数据库的慢查询,80% 是因为没有加索引或索引失效。
- 给查询字段加合适的索引,避免全表扫描;
- 减少
select *,只查需要的字段,降低数据传输量; - 量大的表做分库分表,读写分离,把读请求分流到从库。
-
Redis 优化:用好连接池,避免频繁建联 遇到
ERR max number of clients reached报错,别着急升级 Redis,先看看连接池配置。- 增大连接池大小,避免频繁创建和关闭连接;
- 用
pipeline批量执行命令,减少网络往返次数; - 避免用
keys *这类阻塞命令,防止 Redis 卡顿。
-
消息队列优化:解决 "堆积" 问题消息队列堆积,本质是 "生产速度大于消费速度"。
- 增加消费者数量,并行消费;
- 优化消费逻辑,减少消费侧的处理时间;
- 开启分区,让不同的消费者处理不同的分区数据。
第三级优化:服务器层 ------ 最后的 "扩容" 选项
当代码和中间件都优化到极限,再考虑服务器扩容。这一步是 "治标",但也是应对大规模流量的必要手段。
-
垂直扩容:升级硬件配置
- CPU 使用率持续 > 85%?把 4 核换成 8 核,开启多核优化(Go 程序记得设置
GOMAXPROCS为 CPU 核心数); - 内存不足?从 8G 扩到 16G,排查内存泄漏;
- 磁盘 IO 高?换成 SSD 硬盘,减少随机读写。
- CPU 使用率持续 > 85%?把 4 核换成 8 核,开启多核优化(Go 程序记得设置
-
水平扩容:增加实例数量单机的性能总有上限,这时候就要水平扩容 ------ 增加服务器实例,用负载均衡(如 Nginx、K8s Service)分发请求。比如单机能扛 1000 QPS,要支撑 1 万 QPS,就部署 10 台实例(预留 20% 冗余)。
-
架构扩容:拆分系统,降低复杂度当系统越来越大,就需要做垂直拆分 ------ 把一个大系统拆成用户服务、订单服务、商品服务等多个微服务,每个服务独立部署、独立扩容,降低单个服务的压力。
三、总结:压测优化的闭环思维
压测和优化,不是一次性的工作,而是一个 **"压测→找瓶颈→优化→再压测"** 的闭环。
我们要记住:
- 服务器扩容是 "治标",解决的是 "硬件上限";
- 代码和中间件优化是 "治本",解决的是 "效率上限"。
在资源有限的情况下,优先做 "治本" 的事 ------ 几行代码的改动,可能比升级服务器带来的收益更大。