中间件-05-中间件性能测试的压测模型指标口径和误判点
做中间件性能测试时,最常见的起手动作是:
- 起一台压测机,把并发和连接数拉高
- 看
QPS、TPS、平均响应时间 - 把结果和官方 benchmark 或历史峰值做对比
- 得出“还能扛”“扛不住”“需要扩容”的结论
这种做法有一个明显问题:
压测动作很热闹,但结论经常不可信。
中间件性能测试最容易被做浅的地方,不在压测工具本身,而在下面这些更根本的问题:
- 压测模型和真实业务模型不一致
- 指标口径来自不同层,时间窗和统计方式对不上
- 只看中间件侧吞吐,不看调用侧错误率和重试
- 把客户端瓶颈误判成服务端瓶颈
- 把瞬时峰值误判成长稳能力
- 把“最终恢复”误判成“过程可接受”
这篇文章只聚焦一个问题:
中间件性能测试到底应该怎样设计压测模型、统一指标口径、组织执行链路,并避开最常见的误判点,才能让结论对扩容、限流、治理和架构决策真正有价值。
一、先把中间件性能测试的目标收清,不要一开始就盯住一个吞吐数字
中间件性能测试最常见的误区,是把目标简化成“最多能跑多少”。
这个问题本身并不完整。对测试、平台和架构决策真正有价值的,通常是下面 4 个问题:
1. 当前模型下,系统的稳定吞吐边界在哪里
这里要回答的不是单次冲高值,而是:
- 在给定读写比、消息大小、连接数、请求分布下
- 中间件连续运行一段时间后
- 还能稳定保持多少吞吐和多大时延
如果只看峰值,不看长稳,很多问题都会被掩盖。
2. 当压力继续增加时,系统会先坏在哪里
更关键的问题通常不是“会不会坏”,而是:
- 先坏在连接数
- 先坏在线程池
- 先坏在磁盘 IO
- 先坏在复制、刷盘、分片均衡
- 还是先坏在客户端超时和重试风暴
如果没有识别第一个明显退化点,后续优化方向很容易走偏。
3. 中间件能力边界和业务可接受边界是不是同一个边界
很多中间件在压力升高后仍然能返回结果,但业务已经不可接受,例如:
- Redis 响应还能回来,但超时重试明显升高
- Kafka 还能生产,但消费 Lag 已经持续累积
- Elasticsearch 查询还能返回,但
P99已经把页面拖到不可用 - MySQL 还能执行,但复制延迟已经让业务读到旧值
所以中间件可用边界和业务可用边界必须分开看。
4. 扩容、限流、参数调整到底能改善哪一类问题
性能测试不能只给一个“建议扩容”。更有价值的结论通常应该能回答:
- 是不是 CPU 已经接近上限
- 是不是连接模型需要改
- 是不是消息或请求模型不合理
- 是不是刷盘、复制、分片策略需要改
- 是不是应该拆流量、限峰值、改重试策略
二、中间件压测模型不能只按工具能力设计,要按真实业务链路分层
压测模型如果从工具出发,通常很快就会滑向“某个命令能打多高”。更适合现场的方法,是先按业务链路拆层。
1. 基线模型
这一层只回答最基础的问题:
- 单连接或少量连接下,响应是否稳定
- 服务端有没有明显错误
- 指标口径和日志证据能不能收上来
这一层不追求高压,而是先确认环境是干净的、口径是可对齐的。
2. 业务常态模型
这是最容易被跳过、但最该先做的模型。需要固定:
- 请求类型占比
- 读写比例
- 消息大小或 key/value 大小
- 查询条件复杂度
- 正常连接数和消费并发
- 正常峰谷波动
如果业务日常是 80% 读、20% 写,就不应该只做纯写压测后直接得出容量结论。
3. 峰值冲击模型
这一层关注短时间突发压力,例如:
- 批量发布后缓存同时失效
- 大促前瞬时热点涌入
- 消费者重平衡后短时追赶
- 定时任务或巡检任务整点触发
峰值冲击模型重点观察:
- 错误率是否陡增
- 排队和积压是否出现
- 峰值过去后能否快速回落
4. 长稳运行模型
很多中间件真正的问题只会在长稳阶段暴露,例如:
- 连接泄漏
- 消费积压缓慢增长
- 内存碎片化
- merge、flush、compaction 累积
- 复制延迟越来越长
如果没有长稳模型,很多“测出来没问题”的结论都站不住。
5. 故障与恢复模型
这一层不只是可用性测试,也直接影响性能边界判断。典型动作包括:
- 节点摘除
- 网络抖动或限速
- 主从切换
- 分片迁移
- 磁盘压力上升
很多系统在正常模型下还能抗住,但一进入恢复期,吞吐和时延会明显失真。
三、不同中间件的性能模型重点不同,不能用一套模板套到底
中间件性能测试很容易被误做成统一模板,但不同组件的压测重点完全不同。
1. Redis 更关注请求类型、热点和连接模型
Redis 压测里真正影响结论的,通常是:
GET、SET、MGET、Lua、事务比例- value 大小
- 热 key 分布
- pipeline 使用方式
- 单连接、多连接、连接池模型
- 持久化、复制和哨兵切换是否开启
如果只测简单 GET/SET,很多实际瓶颈根本不会出现。
2. Kafka 更关注生产、消费和积压收敛
Kafka 压测必须至少拆开:
- 生产吞吐
- 消费吞吐
- 生产和消费同时进行的混合流量
- 分区数、副本数、消息大小
- ack 模式、批量大小、压缩方式
只测生产不测消费,通常无法解释真实链路为什么会慢。
3. Elasticsearch 更关注索引写入、查询和后台合并
Elasticsearch 的压测模型至少要区分:
- 写入模型
- 查询模型
- 写查混合模型
- 分片、副本和 refresh 策略
- merge、segment、磁盘和 JVM 行为
如果只做简单搜索压测,最后得到的只是一个很浅的查询结论。
4. MySQL 更关注事务、索引命中和复制边界
MySQL 的性能测试重点往往不是纯 TPS,而是:
- 事务大小
- 索引是否命中
- 锁等待
- 连接池使用方式
- 主从复制和读写分离
如果只用简单单表 SQL 压,最终结论对业务没有太大帮助。
四、指标口径必须分层,不然压测结果很容易互相打架
很多性能争议其实不是系统表现不一致,而是不同角色看的指标口径不一致。
更实用的方式,是把指标拆成下面 5 层。
1. 调用侧口径
这是最接近业务感知的一层,至少应该包含:
- 成功率
- 错误率
- 超时率
P50、P90、P99- 吞吐量
- 重试率
这一层回答的是“调用方到底感受到了什么”。
2. 协议或客户端层口径
这一层更容易暴露隐性问题,例如:
- 建连耗时
- 活跃连接数
- 连接复用率
- 发送队列积压
- 客户端异常类型分布
如果调用侧超时升高,但服务端吞吐看起来正常,这一层通常最值得先看。
3. 中间件实例层口径
这一层是最常被盯住的一层,典型指标包括:
QPS、TPS- 请求耗时
- 队列长度
- 主从同步延迟
- Lag、积压
- 命中率
- flush、merge、compaction、rebalance 状态
这一层回答的是“组件自身正在发生什么”。
4. 资源层口径
这一层决定瓶颈是不是已经打到系统资源:
- CPU 使用率和 run queue
- 内存使用率、GC、swap
- 磁盘吞吐、IOPS、await、util
- 网络带宽、重传、丢包
- 文件句柄、端口、线程数
如果只看中间件层,不看资源层,瓶颈归因往往会很模糊。
5. 恢复与收敛口径
忽略这一层,但它对性能测试很关键:
- 峰值过去后多久恢复
- 积压多久清空
- 主从切换后多久重新稳定
- 迁移、恢复、rebalance 期间性能损失多大
如果只看峰值阶段,不看恢复阶段,就很难判断系统是不是“真实可运营”。
五、指标统计方式要统一,不然同一个结果会被解释成两种结论
同一个压测结果之所以容易争议,很多时候是因为统计方式不统一。
至少要先固定下面这些规则:
1. 时间窗统一
不能拿客户端 1 分钟窗口均值,去对服务端 5 秒窗口峰值。更合适的做法是:
- 固定压测阶段开始时间和结束时间
- 每个阶段明确预热、正式统计、回落观察三个窗口
- 统计图和记录表都按同一时间窗对齐
2. 吞吐定义统一
要先说清楚吞吐到底是什么:
- 请求数每秒
- 消息数每秒
- 字节吞吐每秒
- 成功请求吞吐还是总请求吞吐
不同定义混在一起,结论一定会偏。
3. 时延口径统一
要区分:
- 单次请求响应时间
- 端到端业务耗时
- 排队时间
- 服务处理时间
如果业务端说慢,服务端说不慢,很多时候只是统计边界不同。
4. 错误定义统一
错误不能只算显式失败,还应该看:
- 超时
- 重试后成功
- 降级返回
- 业务侧数据不一致
如果只看 HTTP 或协议层显式错误码,会低估问题影响。
六、中间件性能测试的最小执行骨架
如果要把中间件性能测试收成一套可复用动作,至少应该按下面的顺序执行。
1. 执行前确认
先固定下面这些内容:
- 中间件版本、拓扑、配置差异
- 压测目标和业务场景
- 请求模型、消息大小、事务大小、读写比
- 客户端参数、连接池、重试、超时
- 观察指标和记录人
这一阶段必须避免“先压起来再说”。
2. 基线冒烟
先做:
- 低并发基线
- 连通性、认证、拓扑确认
- 核心指标和日志确认
- 证据留存链路确认
如果这一阶段都没收干净,后面高压阶段的结果通常没有排查价值。
3. 分阶段升压
更适合现场的节奏通常是:
- 低压基线
- 业务常态
- 峰值冲击
- 长稳运行
- 故障与恢复
每个阶段都要明确:
- 压力参数
- 观察时间
- 继续条件
- 暂停条件
- 止损条件
4. 同步采证
至少同时采下面几类证据:
- 压测工具结果
- 客户端错误日志
- 中间件监控
- 系统资源监控
- 配置快照和拓扑状态
如果只留了一份压测结果文件,后续很难解释“为什么慢”。
5. 结果归因
不要只写“最大吞吐”和“平均耗时”,更应该输出:
- 第一个明显退化点
- 最先异常的指标
- 退化是否可恢复
- 问题更像模型问题、参数问题、资源问题还是架构问题
6. 最小记录表
建议至少做一张类似下面的记录表:
| 阶段 | 压测模型 | 成功率 | P99 | 吞吐 | 关键异常 | 初步判断 |
|---|---|---|---|---|---|---|
| 基线 | 低并发读写 | 100% | 4ms | 8k req/s | 无 | 环境正常 |
| 常态 | 8:2 读写混合 | 99.98% | 11ms | 35k req/s | 轻微重试 | 可接受 |
| 峰值 | 突发热点写入 | 98.7% | 140ms | 42k req/s | 队列积压 | 已进入退化 |
| 长稳 | 30 分钟混合流量 | 98.2% | 220ms | 31k req/s | Lag 持续增长 | 不适合长期运行 |
七、最常见的误判点,很多不是技术问题,而是测试方法问题
1. 把 benchmark 结果当成业务容量
官方 benchmark 或简单工具测试,通常只适合做组件能力摸底,不适合直接等价成业务容量。
原因通常是:
- 请求模型太简单
- 没有鉴权、代理、网络复杂度
- 没有真实读写混合
- 没有真实失败和重试
2. 把峰值吞吐当成长稳能力
一次 1 分钟冲高跑到很高的 QPS,不代表 30 分钟后还能保持同样状态。长稳阶段更容易暴露:
- 内存累积
- 积压增长
- 磁盘写放大
- 后台任务拖慢前台请求
3. 把服务端指标正常当成业务正常
很多时候服务端 QPS 仍然很好看,但客户端已经开始出现:
- 超时
- 重试
- 写后读不一致
- 消费延迟扩大
所以调用侧口径必须放在最前面。
4. 把资源瓶颈误判成中间件瓶颈
常见情形包括:
- 压测机自身网络或 CPU 打满
- 客户端连接数不足
- 文件句柄不够
- 宿主机磁盘抖动
如果不先排这些,最后很容易把锅背到组件本身。
5. 把恢复成功误判成过程可接受
某次故障后最终恢复,并不代表过程可接受。更值得关注的是:
- 失败窗口有多长
- 业务损失多大
- 恢复后积压多久清空
- 是否留下二次风险
6. 把单项压测结论误用到整体链路
例如:
- 只测 Redis 缓存命中路径,却把结论用于缓存击穿场景
- 只测 Kafka 生产吞吐,却把结论用于生产消费混合链路
- 只测 Elasticsearch 搜索,却把结论用于写查混合业务
单项结论只能覆盖对应模型,不能跨场景放大使用。
八、真实案例:Kafka 压测看起来还能扛,但业务链路已经在持续变慢
1. 场景
某次测试平台发布前,需要验证消息链路在高峰任务分发场景下是否还能稳定运行。链路结构比较典型:
- 平台批量写入 Kafka
- 多个消费组并发消费
- 下游服务再把结果写回数据库和缓存
现场最关心的问题是:
- 高峰时能不能稳住任务派发
- 任务积压会不会持续增长
- 是否需要提前扩容 Kafka 分区和消费者实例
2. 执行
执行顺序按 4 个阶段推进:
- 先做低压基线,确认生产、消费和监控都正常
- 再按业务常态流量持续压 10 分钟
- 接着模拟发布时的瞬时突发,把生产速率拉到常态的 2 倍
- 最后继续观察 20 分钟,看积压能不能回落
同时记录了:
- 生产吞吐
- 消费吞吐
- 消费组 Lag
- Broker 磁盘和网络
- 客户端错误率和重试
3. 现象
表面上看,结果并不差:
- 生产吞吐稳定上升
- Broker CPU 没有打满
- 没有明显报错
但业务侧开始出现两个信号:
- 平台任务状态更新明显变慢
- 消费组 Lag 在峰值结束后没有回落,反而继续增长
如果只看 Broker 吞吐,很容易得出“Kafka 还能扛”的结论。
4. 排查
现场按下面顺序收敛:
- 先对齐时间窗,确认业务变慢和 Lag 增长发生在同一阶段
- 再看生产和消费吞吐,发现生产端峰值结束后已经回落,但消费吞吐没有同步回升
- 再看消费者实例,发现消费线程数虽然够,但下游数据库写入已经开始排队
- 继续看客户端日志,发现消费端重试和批量提交等待明显增加
- 最后回到中间件层确认,Broker 本身不是第一个退化点,真正先退化的是消费后处理链路
5. 修复
后续没有直接把问题归结为“Kafka 性能不够”,而是做了 4 个动作:
- 把性能模型拆成“纯 Kafka 链路”和“带下游处理的完整链路”
- 在正式报告里把吞吐口径拆成生产吞吐、消费吞吐和业务完成吞吐
- 给消费组补了独立的积压阈值和恢复时间指标
- 对下游数据库写回链路做了限流和批量策略调整
调整后再次执行同样模型,峰值结束后的 Lag 可以在可接受时间内回落,报告也不再把“Broker 吞吐正常”误写成“整条链路性能正常”。
九、中间件性能测试最终要输出什么,结论才算有价值
一篇或一次性能测试报告,如果最后只剩下“最大吞吐”和“平均耗时”,通常还不够。
更值得输出的内容至少应该包括:
- 这次测试覆盖了哪些压测模型,没覆盖哪些模型
- 每个阶段的指标口径是什么
- 第一个明显退化点在哪里
- 最先出现异常的是调用侧、客户端侧、中间件侧还是资源侧
- 哪些结论适用于当前场景,哪些不能外推
- 接下来更适合做扩容、限流、参数治理还是链路改造
中间件性能测试真正有价值的地方,从来不是跑出一个更大的数字,而是把“系统在什么条件下会开始失真、为什么失真、下一步该怎么改”讲清楚。
只有这样,压测结论才不是一份热闹的性能截图,而是一份能指导架构和治理决策的工程证据。