具体案例分析:电商平台秒杀活动性能优化
背景
某电商平台计划开展“双11”秒杀活动,预计峰值流量为 10万QPS,目标是在1秒内完成用户抢购请求,保障核心接口 下单API 的P99响应时间≤200ms。 系统架构:
- 前端:Vue + Nginx(负载均衡)
- 后端:Spring Boot微服务集群(8节点)
- 数据库:MySQL主从集群(1主3从)
- 缓存:Redis Cluster(3主3从)
- 消息队列:Kafka(3节点)
问题现象
在预压测阶段,使用JMeter模拟5万并发用户时,出现以下问题:
- 下单接口响应时间陡增:P99从150ms升至1.2s,错误率(5xx)达到15%。
- 数据库CPU使用率飙升:主库CPU持续100%,从库复制延迟增加。
- Redis连接池耗尽:日志显示“Could not get a resource from the pool”。
性能分析过程
1. 数据收集与监控
-
系统层监控(Prometheus) :
- MySQL主库CPU 100%,磁盘IOPS峰值15k(SATA SSD)。
- Redis连接池活跃连接数达最大值(200),大量连接超时。
-
应用层追踪(SkyWalking) :
- 下单接口调用链中,80%时间消耗在“库存查询”和“订单写入”阶段。
- 库存查询SQL平均执行时间800ms。
-
数据库分析(Percona Toolkit) :
- 执行
SHOW PROCESSLIST
发现大量SELECT stock FROM sku WHERE sku_id=xxx
查询。 EXPLAIN
显示未命中索引,全表扫描(rows=500万)。
- 执行
2. 瓶颈定位
-
根因1:数据库索引缺失
sku
表的sku_id
字段未建立索引,导致全表扫描,引发CPU过载。
-
根因2:缓存失效
- 库存数据未缓存,所有请求直击数据库。
-
根因3:Redis连接池配置不合理
- 连接池最大连接数(200)不足,高并发下连接被耗尽。
优化措施
1. 数据库优化
- 添加索引:为
sku_id
字段创建唯一索引,库存查询时间从800ms降至5ms。 - 读写分离:将库存查询路由到从库,主库专注写操作。
2. 缓存策略升级
-
库存预热:活动开始前,将库存数据加载到Redis Cluster。
-
原子化操作:使用
Redis DECR
命令扣减库存,替代数据库扣减。Long remain = redisTemplate.opsForValue().decrement("stock:" + skuId); if (remain < 0) { throw new BusinessException("库存不足"); }
3. 连接池与线程池调优
-
Redis连接池扩容:最大连接数从200调整为1000,超时时间从2s改为500ms。
-
后端服务线程池优化:
# application.yml tomcat: max-threads: 500 # 原值200 min-spare-threads: 100
4. 异步削峰与限流
-
Kafka异步下单:抢购请求先写入Kafka,由消费者异步处理订单。
@KafkaListener(topics = "order-create") public void handleOrder(OrderMessage message) { orderService.createOrder(message); }
-
Sentinel限流:对下单接口设置QPS阈值(5万),超出后快速失败。
优化效果验证
指标 | 优化前 | 优化后 |
---|---|---|
下单接口P99延迟 | 1200ms | 150ms |
数据库CPU使用率 | 100% | 40% |
Redis连接池错误率 | 15% | 0.1% |
系统吞吐量(QPS) | 3万 | 12万 |
总结与启示
- 索引是数据库性能的生命线:全表扫描是常见性能杀手,需通过
EXPLAIN
定期检查SQL执行计划。 - 缓存是抗高并发的基石:热点数据预加载 + 原子操作可避免缓存穿透和超卖。
- 资源池配置需动态调整:连接池、线程池参数需结合压测结果动态优化。
- 异步解耦是削峰利器:通过消息队列将同步操作转为异步,显著提升系统吞吐量。
后续改进:
- 引入熔断降级(如Hystrix)防止雪崩效应。
- 使用分布式ID生成器(雪花算法)替代数据库自增ID,减少主键冲突。
通过此案例可以看出,性能优化是系统性工程,需结合架构设计、代码优化、运维调参等多维度手段,才能在高并发场景下保障用户体验与系统稳定。