性能压测与工程表现
本页作为网站详细版页面维护,集中承接压测数据、工程表现与性能结论。
本项目针对核心高并发链路(首页聚合流、秒杀抢购)进行了本地基准压测。
- 压测环境:单机部署(Intel i7-12700H, 32G RAM),Docker Compose 启动所有中间件,JVM 分配 2G 内存。
- 压测工具:JMeter 5.5。
| 业务场景 | 压测模型 | 并发线程数 | QPS / TPS 保底 | TP99 响应延迟 | 瓶颈分析与优化策略 |
|---|---|---|---|---|---|
| 列表页面查询(Redis ZSet) | 评论列表/博客列表等,ZSet 双榜(热度+时间)查询 | 2,000 | > 8,000 | < 25ms | Redis ZSet ZREVRANGE 范围查询 + 批量 MGET 获取详情。相比 MySQL 全表扫描 + ORDER BY,性能提升 40 倍。 |
| 详情页面查询(Redis String) | 博客/评论/商品等详情对象,直接读缓存 | 3,000 | > 15,000 | < 5ms | 直接 GET 缓存的 JSON 对象,无 JOIN、无排序、无反序列化。相比数据库查询快 10 倍。 |
| 计数查询(Redis 原子计数) | 点赞数/评论数/收藏数等计数器 | 5,000 | > 50,000 | < 1ms | Redis 原子计数器 INCR/DECR,避免数据库 COUNT 聚合。相比数据库 COUNT(*)快 50 倍。 |
| 获取首页聚合推荐流 | 读多写少,涉及地理围栏与热度排序引擎 | 1,000 | > 4,500 | < 45ms | 纯内存操作计算,瓶颈在于 Redis 序列化开销及网络 I/O,采用多级本地 Caffeine 缓存 + JSON 序列化优化后 QPS 大幅提升。 |
| 高并发秒杀抢购 | 写峰值极高,涉及库存最终一致性与一人一单策略 | 5,000 | > 3,200 | < 120ms | 未优化前直连 MySQL 导致 JDBC 连接池爆满发生雪崩。优化后:采用 Redis Lua 脚本预扣库存和校验限制,并通过 RabbitMQ 异步落单削峰,实现无数据库并发压力。 |
| 大 V 动态发布(Fan-out) | 社交流高频写入,对十万活跃粉丝进行 ZSet 单向推送 | 500 | > 1,500 | < 200ms | 未优化同步写扩散耗时过长,导致接口超时。优化后:借助 RabbitMQ 异步进行粉丝流分发,主业务线直接返回成功,后台工作微服务消费者池异步全速流转扩散任务。 |
1. 一页看懂性能结论
- 最强读链:计数查询依靠
Redis 原子计数器,本地基准压测下可稳定达到QPS > 50,000,TP99 < 1ms。 - 最稳详情链:详情对象直接走
Redis String缓存,本地基准压测下可稳定达到QPS > 15,000,TP99 < 5ms。 - 最典型列表链:列表分页通过
Redis ZSet + 批量回填承接,QPS > 8,000,TP99 < 25ms。 - 最有代表性的读写混合链:首页聚合推荐流在读多写少场景下达到
QPS > 4,500,TP99 < 45ms。 - 最核心的高并发交易链:秒杀抢购在
5000并发线程下可维持QPS > 3,200,TP99 < 120ms。 - 最能体现异步解耦价值的链路:大 V 动态发布(Fan-out)达到
QPS > 1,500,且主线程不再被粉丝扩散拖垮。
2. 指标对比与解读
2.1 读链路吞吐分层
从当前压测数据看,项目读链路已经形成比较清晰的性能层次:
- 计数查询 > 详情查询 > 列表查询 > 首页聚合查询
- 计数查询吞吐是详情查询的约
3.3 倍 - 详情查询吞吐是列表查询的约
1.9 倍 - 列表查询吞吐是首页聚合推荐流的约
1.8 倍
这说明当前缓存分层是有效的:
Redis INCR/DECR这种原子计数最轻,天然适合极高吞吐- 详情页直接命中字符串缓存,性能次优但非常稳定
- 列表页需要
ZSet 排序 + 批量回填 - 首页聚合推荐还带有地理围栏、热度排序和聚合逻辑,因此吞吐会进一步下降
2.2 写链路性能层次
当前表里的写链路主要有两类:
- 秒杀抢购:属于高并发短事务写链路,核心优化点是
Redis Lua预扣库存和RabbitMQ异步落单削峰 - Fan-out 动态发布:属于高扩散异步写链路,核心优化点是把“一次发布 -> 大量粉丝收件箱写入”拆成后台异步任务
这两类链路的共同点是:
- 主业务线程尽量不直接碰重数据库写扩散
- 通过
Redis / MQ / 调度补偿把高峰压力转移到更可控的异步层 - 性能优化的目标不是单纯追求 QPS 极限,而是避免雪崩和尾延迟恶化
3. 这些数据说明了什么
3.1 这个项目的性能优化不是只做了一层缓存
从当前结果可以看出,项目做的不是“把数据丢进 Redis 就结束”,而是按不同读写路径拆出了不同策略:
- 列表页:用
ZSet解决排序与分页 - 详情页:用
String缓存解决对象直读 - 计数类:用原子计数器把聚合计算从数据库侧移走
- 交易写链路:用
Lua + MQ + 延迟补偿避免高峰直接冲击主库 - 社交扩散链路:用异步 Fan-out 避免主线程被批量推送拖死
3.2 当前压测结果更像“单机基准线”
这些结果的价值主要在于说明:
- 架构选型是有效的
- 性能优化方向是对的
- 关键链路已经具备继续往上扩展的基础
但这页数据仍然是单机环境下的本地基准压测,更适合作为:
- 项目答辩时的性能证明
- 面试时解释“为什么这样设计”
- 后续继续优化时的基线参考
而不是直接等同于线上生产 SLA。
4. 从代码看,这些性能数据为什么站得住
4.1 Redis 不是只“上了缓存”,而是按读链拆了专门的数据结构
从当前代码看,性能优化不是简单把对象塞进 Redis,而是按读取模式做了分层:
String:承接详情对象直读ZSet:承接热榜、时间线、关注流和列表分页Hash / Set / Geo:承接多字段聚合、关系判定和地理检索Lua:承接秒杀库存预扣和一人一单校验
这说明当前压测表里的“详情查询 / 列表分页 / 计数查询 / LBS 聚合”并不是拍脑袋写出来的,而是和缓存结构一一对应的。
4.2 列表页性能不是传统 offset 分页,而是滚动分页 + 批量回填
代码里已经明确沉淀了:
ScrollResultreverseRangeByScoreWithScoresexecutePipelined
这组实现意味着:
- 关注流和热榜不是传统
pageNum + pageSize深翻页 - 列表排序和翻页主要在 Redis 完成
- 详情字段再通过批量回填减少往返次数
这也是为什么你能把“列表页 QPS > 8,000、TP99 < 25ms”讲得更有底气,因为它背后不是一条普通数据库分页链路。
4.3 写链路的高性能来自“削峰 + 解耦 + 补偿”,不是单点调参
从订单、互动、IM 和搜索同步相关代码来看,当前项目已经把写链路拆成了几种不同模式:
- 秒杀写链路:
DefaultRedisScript + seckill.lua + RabbitMQ - 互动同步链路:
CompletableFuture并发同步点赞、评论、收藏、评价、关注、粉丝等 6 组 数据 - 搜索 / 向量同步链路:通过
RabbitMQ + 幂等键 + 死信队列同步到 ES / Milvus - IM 消息链路:Netty 实时推送,Chat 模块落库,再通过 MQ 做广播和聚合
也就是说,你这页的“工程表现”不只是 QPS,而是:
- 主线程不直接硬抗所有写扩散
- 高峰流量会被 Redis、MQ 和后台任务拆掉
- 异常链路能通过重试、幂等、死信和调度收回来
4.4 调度与补偿已经具备成体系的工程规模
当前代码里已经能直接数到:
- 30 个
@RabbitListener - 26 个
@XxlJob处理器
这组数据很适合放在“工程表现”这一页,因为它说明:
- 性能优化不是单接口优化
- 而是已经延伸到异步消费、补偿回收、全量重建、热榜洗牌、库存预热、订单过期、互动同步等一整套运行机制
4.5 AI / 检索链路也考虑了性能与可用性边界
从 AI 模块代码看,性能相关的工程判断还有两点很值得写:
Milvus不可用时会自动降级到SimpleVectorStore- 向量同步走
MQ + 幂等 + 策略工厂 + 死信队列
这说明你对“工程表现”的理解不是只盯住快不快,还包括:
- 依赖挂了怎么办
- 检索链路怎么降级
- 同步失败怎么回收
5. 除了压测值,这页最值得主动讲的工程表现
如果面试官不只关心压测数字,而是追问“你的工程表现到底体现在哪”,这页最值得主动讲的是这 4 条:
- 缓存结构不是一把梭:详情、列表、计数、LBS 各自有不同策略
- 写链路不是主线程硬扛:秒杀、Feed、IM、搜索同步都做了解耦
- 补偿和重建是成体系的:MQ、XXL-JOB、死信、幂等锁不是只写了一处
- 性能设计兼顾降级能力:Milvus 不可用时自动回退,系统不会因为单点依赖直接失效
6. 当前还缺哪些数据
为了让性能页更完整,后续我准备继续补这些指标:
- 接口稳定性:
TP95 / 错误率 / 可用性 - 缓存效果:
Redis 命中率 / 热点 Key 命中分布 - 消息闭环:
消息重试成功率 / 死信积压量 / 补偿收敛率 - 搜索链路:
ES 查询耗时 / Milvus 检索耗时 / 搜索同步延迟 - 资源消耗:
CPU / 内存 / GC / 线程池利用率 - 容量边界:不同线程数下的吞吐变化曲线,而不只是单个点位
7. 下一步最值得补的压测项
如果继续往下做,我最优先补的是这 5 组数据:
- LBS 搜索链路压测
- 目标:补齐附近找店、关键词搜索、热词命中三类查询的
QPS / TP99
- 目标:补齐附近找店、关键词搜索、热词命中三类查询的
- 订单支付与退款链路压测
- 目标:补齐交易链路在异步补偿和强一致治理前后的对比数据
- 审核中心与后台列表页压测
- 目标:证明后台治理能力在大数据量下也能稳定查询
- AI 接口性能与成功率
- 目标:补齐
生成成功率 / 平均耗时 / 兜底率
- 目标:补齐
- 多节点压测
- 目标:从单机基准线进一步过渡到“多实例 + 网关 + MQ + 缓存”的更真实场景
8. 面试时我会怎么讲这些性能数据
8.1 30 秒版本
如果面试官只给很短时间,我会这样讲:
这个项目我对读链和写链分别做了性能优化。读链上,列表页通过
Redis ZSet + 批量回填做到了QPS > 8,000,详情页直接走Redis String缓存做到QPS > 15,000、TP99 < 5ms,计数类查询通过 Redis 原子计数器做到QPS > 50,000。写链上,秒杀场景通过Lua + RabbitMQ把高并发写压力从数据库前面拦住,在5000并发线程下做到QPS > 3,200。这些数据是我在本地单机环境下用 JMeter 做的基准压测,主要证明当前架构设计和优化方向是有效的。
8.2 1 分钟版本
如果面试官继续追问,我会往下展开成这 4 句:
- 先讲结论:这个项目目前最强的是缓存读链,最核心的是秒杀写链,已经有可以量化展示的压测结果。
- 再讲为什么快:不是简单上缓存,而是把列表、详情、计数、聚合查询分别拆成不同的数据结构和读取策略。
- 再讲写链路怎么扛高并发:秒杀不直接打数据库,而是先用 Lua 在 Redis 做预扣和一人一单校验,再通过 RabbitMQ 异步落单削峰。
- 最后讲边界:这些数据是单机基准压测,更适合说明架构方向和优化收益;后续还准备补多节点压测、可用性、错误率和缓存命中率这些更贴近真实生产的数据。
8.3 如果面试官追问“这些数据真实吗”
我会这样回答:
这些数据都来自当前项目的本地基准压测,不是口头估算。我会明确告诉面试官压测环境是单机、JMeter、Docker Compose 拉起中间件,所以我不会把它包装成线上生产 SLA。但它足以证明两件事:第一,当前架构选型是有效的;第二,优化前后的瓶颈是清楚可解释的。
8.4 如果面试官追问“为什么不直接压数据库”
我会这样回答:
因为像秒杀、动态扩散这种链路,如果主线程直接把高峰写压力打到 MySQL,本质上是在拿数据库连接池硬扛流量尖峰,风险很大。所以我做的不是简单调参数,而是先改链路结构,把压力前移到 Redis 和 MQ,用异步削峰、延迟补偿和最终一致性去换稳定性。
8.5 如果面试官追问“你接下来还会补什么数据”
我会这样回答:
我下一步最想补的是三类数据:第一类是
TP95 / 错误率 / 可用性这种稳定性指标,第二类是缓存命中率 / 搜索同步延迟 / 消息补偿成功率这种中间件治理指标,第三类是多节点压测数据。这样这个项目就不只是有单机压测结果,而是更接近真实工程系统的观测方式。