中间件-05-中间件性能测试的压测模型指标口径和误判点

做中间件性能测试时,最常见的起手动作是:

  • 起一台压测机,把并发和连接数拉高
  • QPSTPS、平均响应时间
  • 把结果和官方 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 压测里真正影响结论的,通常是:

  • GETSETMGET、Lua、事务比例
  • value 大小
  • 热 key 分布
  • pipeline 使用方式
  • 单连接、多连接、连接池模型
  • 持久化、复制和哨兵切换是否开启

如果只测简单 GET/SET,很多实际瓶颈根本不会出现。

2. Kafka 更关注生产、消费和积压收敛

Kafka 压测必须至少拆开:

  • 生产吞吐
  • 消费吞吐
  • 生产和消费同时进行的混合流量
  • 分区数、副本数、消息大小
  • ack 模式、批量大小、压缩方式

只测生产不测消费,通常无法解释真实链路为什么会慢。

3. Elasticsearch 更关注索引写入、查询和后台合并

Elasticsearch 的压测模型至少要区分:

  • 写入模型
  • 查询模型
  • 写查混合模型
  • 分片、副本和 refresh 策略
  • merge、segment、磁盘和 JVM 行为

如果只做简单搜索压测,最后得到的只是一个很浅的查询结论。

4. MySQL 更关注事务、索引命中和复制边界

MySQL 的性能测试重点往往不是纯 TPS,而是:

  • 事务大小
  • 索引是否命中
  • 锁等待
  • 连接池使用方式
  • 主从复制和读写分离

如果只用简单单表 SQL 压,最终结论对业务没有太大帮助。

四、指标口径必须分层,不然压测结果很容易互相打架

很多性能争议其实不是系统表现不一致,而是不同角色看的指标口径不一致。

更实用的方式,是把指标拆成下面 5 层。

1. 调用侧口径

这是最接近业务感知的一层,至少应该包含:

  • 成功率
  • 错误率
  • 超时率
  • P50P90P99
  • 吞吐量
  • 重试率

这一层回答的是“调用方到底感受到了什么”。

2. 协议或客户端层口径

这一层更容易暴露隐性问题,例如:

  • 建连耗时
  • 活跃连接数
  • 连接复用率
  • 发送队列积压
  • 客户端异常类型分布

如果调用侧超时升高,但服务端吞吐看起来正常,这一层通常最值得先看。

3. 中间件实例层口径

这一层是最常被盯住的一层,典型指标包括:

  • QPSTPS
  • 请求耗时
  • 队列长度
  • 主从同步延迟
  • 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. 分阶段升压

更适合现场的节奏通常是:

  1. 低压基线
  2. 业务常态
  3. 峰值冲击
  4. 长稳运行
  5. 故障与恢复

每个阶段都要明确:

  • 压力参数
  • 观察时间
  • 继续条件
  • 暂停条件
  • 止损条件

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 个阶段推进:

  1. 先做低压基线,确认生产、消费和监控都正常
  2. 再按业务常态流量持续压 10 分钟
  3. 接着模拟发布时的瞬时突发,把生产速率拉到常态的 2 倍
  4. 最后继续观察 20 分钟,看积压能不能回落

同时记录了:

  • 生产吞吐
  • 消费吞吐
  • 消费组 Lag
  • Broker 磁盘和网络
  • 客户端错误率和重试

3. 现象

表面上看,结果并不差:

  • 生产吞吐稳定上升
  • Broker CPU 没有打满
  • 没有明显报错

但业务侧开始出现两个信号:

  • 平台任务状态更新明显变慢
  • 消费组 Lag 在峰值结束后没有回落,反而继续增长

如果只看 Broker 吞吐,很容易得出“Kafka 还能扛”的结论。

4. 排查

现场按下面顺序收敛:

  1. 先对齐时间窗,确认业务变慢和 Lag 增长发生在同一阶段
  2. 再看生产和消费吞吐,发现生产端峰值结束后已经回落,但消费吞吐没有同步回升
  3. 再看消费者实例,发现消费线程数虽然够,但下游数据库写入已经开始排队
  4. 继续看客户端日志,发现消费端重试和批量提交等待明显增加
  5. 最后回到中间件层确认,Broker 本身不是第一个退化点,真正先退化的是消费后处理链路

5. 修复

后续没有直接把问题归结为“Kafka 性能不够”,而是做了 4 个动作:

  • 把性能模型拆成“纯 Kafka 链路”和“带下游处理的完整链路”
  • 在正式报告里把吞吐口径拆成生产吞吐、消费吞吐和业务完成吞吐
  • 给消费组补了独立的积压阈值和恢复时间指标
  • 对下游数据库写回链路做了限流和批量策略调整

调整后再次执行同样模型,峰值结束后的 Lag 可以在可接受时间内回落,报告也不再把“Broker 吞吐正常”误写成“整条链路性能正常”。

九、中间件性能测试最终要输出什么,结论才算有价值

一篇或一次性能测试报告,如果最后只剩下“最大吞吐”和“平均耗时”,通常还不够。

更值得输出的内容至少应该包括:

  • 这次测试覆盖了哪些压测模型,没覆盖哪些模型
  • 每个阶段的指标口径是什么
  • 第一个明显退化点在哪里
  • 最先出现异常的是调用侧、客户端侧、中间件侧还是资源侧
  • 哪些结论适用于当前场景,哪些不能外推
  • 接下来更适合做扩容、限流、参数治理还是链路改造

中间件性能测试真正有价值的地方,从来不是跑出一个更大的数字,而是把“系统在什么条件下会开始失真、为什么失真、下一步该怎么改”讲清楚。

只有这样,压测结论才不是一份热闹的性能截图,而是一份能指导架构和治理决策的工程证据。