大家好,相信很多致力于Java后端开发的同学,对"苍穹外卖"、"黑马点评"、"云商城"这类经典项目都不会陌生。我也曾跟随黑马程序员及其他技术文档,完整实现过这些项目。在它们的亮点中,高并发处理方案无疑是重中之重。然而,这些方案之间的实际性能差异究竟有多大?为了解开心中的疑惑,也为了丰富个人简历中的实践内容,我设计并实施了一次针对"秒杀"场景的高并发压力测试。本篇文档将详细记录我的实验设计、实现与结果分析。
1. 业务背景与实验设计
1.1 核心业务流程
一个典型的秒杀下单流程可抽象为以下步骤:
1. 请求拦截:校验是否为重复请求(如短时间内的频繁点击)。
2. 资格校验:校验用户是否已购买过(一人一单)。
3. 库存校验:校验商品库存是否充足。
4. 订单创建:扣减库存,在数据库中创建订单记录。
1.2 实验方案对比
为探究不同技术栈的性能表现,我设立了以下两个实验组:
对照组(数据库直连组):
核心思想:将并发控制的重担完全交给数据库和应用层代码。
技术实现:
库存防超卖:通过MySQL的"SELECT ... FOR UPDATE"(悲观锁)实现。
一人一单 & 防重:由Java应用层代码在数据库事务中校验保证。
实验组(优化组):
核心思想:利用Redis的高性能与原子性操作,以及消息队列的异步削峰能力,将压力从数据库剥离。
技术实现:
防重请求:通过Redisson分布式锁实现。
一人一单 & 库存防超卖:通过Redis + Lua脚本的原子性操作确保。
数据持久化:通过RocketMQ普通消息异步写入数据库,实现最终一致性。
1.3 实验环境
硬件:单机测试(笔记本:ThinkPad Neo 14)。
压测工具:K6(注:初期曾使用Apifox,但其在极限压测场景下数据不够精确,故更换为专业工具K6。Apifox更适合日常接口调试与流程测试)。
数据库连接池:50
初始库存:1000
- 压测结果与数据分析
以下是两组方案在不同并发用户数下的关键性能指标对比。
2.1 对照组(数据库直连组)性能数据
并发50用户:
P95响应时间:204.01ms
平均响应时间:129.74ms
QPS:381.47/s
总请求数:3,876
成功率(got ticket):25%(1000/3876)
HTTP失败率:0.00%
并发100用户:
P95响应时间:399.5ms
平均响应时间:309.77ms
QPS:317.74/s
总请求数:3,261
成功率(got ticket):30%(1000/3261)
HTTP失败率:0.00%
关键观察:
无HTTP错误:所有请求都正常返回,接口可用性100%。
响应延迟显著:并发100时P95接近400ms,用户体验开始劣化。
QPS不升反降:并发翻倍但QPS下降17%,说明数据库已成为瓶颈。
悲观锁阻塞:大量线程等待数据库锁,CPU利用率低但连接池耗尽。
2.2 实验组(Redis+MQ优化组)性能数据
并发50用户:
P95响应时间:28.47ms
平均响应时间:8.96ms
QPS:5519.13/s
总请求数:55,226
成功率(got ticket):1%(1000/55226)
HTTP失败率:0.00%
并发100用户:
P95响应时间:74.43ms
平均响应时间:22.94ms
QPS:4333.56/s
总请求数:43,397
成功率(got ticket):2%(1000/43397)
HTTP失败率:0.00%
关键观察:
性能飞跃:QPS提升14.5倍(并发50)和13.6倍(并发100)。
延迟极低:P95控制在30-75ms,用户体验流畅。
承载能力:总请求量是对照组的14倍,系统吞吐量大幅提升。
精准控流:成功率仅1-2%,说明Redis原子性过滤有效,仅真实扣减库存的请求进入下游。
2.3 性能对比总结
| 指标 | 数据库直连组(并发100) | Redis优化组(并发100) | 提升幅度 |
|---|---|---|---|
| 吞吐量(QPS) | 317.74/s | 4333.56/s | 13.6倍 |
| 响应延迟(P95) | 399.5ms | 74.43ms | 降低81% |
| 总处理能力 | 3,261请求 | 43,397请求 | 13.3倍 |
| 平均响应时间 | 309.77ms | 22.94ms | 降低93% |
- 核心结论与方案对比
3.1 数据解读与业务意义
为什么Redis组成功率只有1-2%?
这是预期内的优秀表现,说明:Redis原子性扣减成功拦截了99%的无效请求(库存不足/重复购买)。 只有真正抢到库存的1000个请求被放行,其余直接返回"已售罄"。 对照组25-30%的"成功率"实际上是数据库层面的竞争失败重试,并非真正成功。
为什么Redis组并发100时QPS比并发50低?
并发50时:系统未达到瓶颈,单机Redis性能充分发挥(5500+ QPS)。
并发100时:可能受限于单机网络带宽或应用层线程数,但4333 QPS仍是对照组的13.6倍。
延迟对比的业务价值:
数据库组点击秒杀按钮需等待0.4秒,Redis组仅需0.03秒,几乎无感知。
3.2 技术方案深度对比
数据库直连组(悲观锁方案):
优点:实现简单,数据强一致性,无需引入额外组件。
缺点:数据库成为瓶颈,连接数受限,锁竞争激烈,性能随并发线性下降。
Redis优化组(缓存+异步方案):
优点:Redis单线程+Lua脚本保证原子性,无锁竞争。 内存操作性能比磁盘高2-3个数量级。 MQ异步解耦,削峰填谷,保护数据库。
缺点: 系统复杂度增加,需维护Redis和MQ。 最终一致性需处理消息丢失、重复消费。 需要额外的库存回滚和补偿机制。
- 实战踩坑与优化建议
4.1 压测过程中的关键发现
数据库连接池耗尽(对照组):
现象:并发100时QPS反而下降,连接等待时间增加。
根因:SELECT ... FOR UPDATE持有锁时间过长,连接无法释放。
建议:数据库方案必须配合连接池监控和熔断降级。
Redis单节点性能天花板(实验组并发100):
现象:QPS从5500降至4300,未达到线性扩展。
根因:单机Redis网络IO或CPU单线程瓶颈。
库存精准扣减验证:
验证方式:检查数据库最终库存是否为0,订单数是否为1000。
结果:两组方案均无超卖,但Redis组无重复订单,对照组需去重。
- 写在最后
本次压测用真实数据验证了"缓存+异步"架构在高并发秒杀场景下的绝对优势: 14倍吞吐量提升、81%延迟降低、零超卖风险 —— 这就是Redis+MQ方案交出的答卷。 但也要清醒认识到: 对照组并非一无是处:它的强一致性和简单性,在低并发场景(<50 QPS)下完全够用。 优化组也有代价:引入了Redis、MQ运维复杂度,以及最终一致性的业务适配成本。