🚀从0到1构建高并发秒杀系统:实战 RocketMQ 异步削峰与Redis预减库存
📖一、 简介
在电商、抢票等高并发场景中,秒杀系统面临着"高并发、库存稀缺、易超卖、系统易崩"的严峻挑战。传统的同步处理架构难以支撑海量请求并发下的性能与一致性要求。
本文从实战出发,系统性地讲解如何基于 Redis + RocketMQ + MySQL + Spring Boot 构建一个高性能、高可用、强一致性 的秒杀系统。通过 接口限流、Redis 原子扣减库存、RocketMQ 异步削峰、数据库幂等落库 等机制,彻底解决了高并发下的核心问题,如超卖、重复下单、系统崩溃等。
你将看到:
- 🧠 秒杀系统面临的本质问题与设计原则
- 🧱 架构层次的分层与职责划分
- ⚙️ Redis + Lua 脚本实现库存预扣与并发控制
- 🚀 RocketMQ 异步下单实现削峰填谷与解耦
- 💡 MySQL 乐观锁 + 幂等设计实现最终一致性
- 🛠️ 全程配套详细注释代码、架构图与数据库设计
本文不仅提供了完整可运行的思路,还具备工程级可落地性。适合架构师、后端工程师在面对实际高并发场景时作为参考与实践蓝图。
🧠 二、秒杀系统的挑战与本质
"秒杀"业务场景常出现在电商、抢票、预约系统中,具有以下挑战:
维度 | 问题 | 说明 |
---|---|---|
并发性 | 高并发访问 | 瞬时请求高达数十万甚至上百万 |
数据一致性 | 超卖/重复下单 | 库存是关键共享资源 |
系统稳定性 | 容易雪崩 | 单点性能瓶颈可能导致系统挂掉 |
响应速度 | 秒级反馈 | 用户希望秒杀是否成功即时反馈 |
核心:限流 + 削峰 + 异步 + 缓存
🔧 三、系统总体架构设计

🔨 四、核心技术选型与职责
技术组件 | 作用 |
---|---|
Redis | 缓存库存、原子扣减、用户状态标记 |
RocketMQ | 削峰填谷、异步解耦 |
MySQL | 最终订单存储、库存持久化 |
Spring Boot | 微服务框架 |
Guava RateLimiter | 接口级限流 |
📦 五、秒杀系统核心模块详细设计
1️⃣ 接口限流 + 秒杀入口
java
@RestController
@RequestMapping("/seckill")
public class SeckillController {
// 每秒只允许100个请求通过
private final RateLimiter rateLimiter = RateLimiter.create(100);
@Autowired
private SeckillService seckillService;
@PostMapping("/{itemId}")
public ResponseEntity<String> seckill(@PathVariable Long itemId) {
// 通过令牌桶控制请求速率
if (!rateLimiter.tryAcquire()) {
return ResponseEntity.status(HttpStatus.TOO_MANY_REQUESTS).body("请求过多,请稍后重试");
}
// 模拟用户获取(实际从登录信息中取)
Long userId = 1001L;
boolean success = seckillService.processSeckill(itemId, userId);
if (success) {
return ResponseEntity.ok("请求成功,正在排队中...");
} else {
return ResponseEntity.badRequest().body("库存不足或已抢完");
}
}
}
2️⃣ Redis 原子扣减库存(Lua 脚本)+ RocketMQ 消息发送
java
@Service
public class SeckillServiceImpl implements SeckillService {
private static final String STOCK_KEY_PREFIX = "seckill:stock:";
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Autowired
private RocketMQTemplate rocketMQTemplate;
@Override
public boolean processSeckill(Long itemId, Long userId) {
String stockKey = STOCK_KEY_PREFIX + itemId;
// Lua 脚本:原子检查库存并扣减
String luaScript = "if (tonumber(redis.call('get', KEYS[1])) > 0) then " +
" return redis.call('decr', KEYS[1]) " +
"else return -1 end";
// 执行脚本,防止并发引起库存超卖
DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
redisScript.setScriptText(luaScript);
redisScript.setResultType(Long.class);
Long result = redisTemplate.execute(redisScript, Collections.singletonList(stockKey));
if (result == null || result < 0) {
// 库存不足
return false;
}
// 构建订单消息并发送到 MQ
SeckillOrderMessage message = new SeckillOrderMessage(userId, itemId);
rocketMQTemplate.convertAndSend("seckill-topic", message);
return true;
}
}
3️⃣ RocketMQ 消费者:监听秒杀订单消息,落库处理
java
@Component
@RocketMQMessageListener(topic = "seckill-topic", consumerGroup = "seckill-consumer-group")
public class SeckillConsumer implements RocketMQListener<SeckillOrderMessage> {
@Autowired
private SeckillOrderService orderService;
@Override
public void onMessage(SeckillOrderMessage msg) {
try {
// 调用下单服务进行库存校验和订单落库
orderService.createOrder(msg.getUserId(), msg.getItemId());
} catch (Exception e) {
// 消息重试或记录异常用于补偿
System.err.println("消费失败: " + e.getMessage());
}
}
}
4️⃣ 下单服务:幂等校验 + 数据库存储 + 乐观锁扣减
java
@Service
public class SeckillOrderServiceImpl implements SeckillOrderService {
@Autowired
private SeckillOrderRepository orderRepository;
@Autowired
private StockRepository stockRepository;
@Transactional
public void createOrder(Long userId, Long itemId) {
// 幂等校验:防止重复下单(可用唯一索引或Redis SET)
if (orderRepository.existsByUserIdAndItemId(userId, itemId)) {
return;
}
// 扣减数据库库存,使用乐观锁 version
int updated = stockRepository.decreaseStock(itemId);
if (updated == 0) {
throw new RuntimeException("库存不足,数据库扣减失败");
}
// 写入订单记录
SeckillOrder order = new SeckillOrder(userId, itemId, LocalDateTime.now());
orderRepository.save(order);
}
}
5️⃣ 数据库表结构设计(库存 + 订单)
📌 商品库存表(带 version 乐观锁)
sql
CREATE TABLE stock (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
item_id BIGINT NOT NULL UNIQUE,
stock INT NOT NULL,
version INT NOT NULL DEFAULT 0
);
📌 库存扣减 SQL(乐观锁)
sql
UPDATE stock
SET stock = stock - 1, version = version + 1
WHERE item_id = ? AND version = ? AND stock > 0;
📌 订单表
sql
CREATE TABLE seckill_order (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
user_id BIGINT NOT NULL,
item_id BIGINT NOT NULL,
create_time DATETIME NOT NULL,
UNIQUE KEY uniq_user_item (user_id, item_id)
);
📊 六、性能优化与高可用建议
方向 | 建议 |
---|---|
限流 | 接口层限流(Guava)、网关限流(Sentinel) |
削峰 | 使用 RocketMQ 异步下单,防止数据库击穿 |
幂等 | Redis SETNX、唯一索引、分布式锁 |
日志与监控 | Prometheus + Grafana、日志追踪链路 |
高可用 | RocketMQ 主从部署、Broker 宕机自动切换 |
✅ 七、总结
通过将 Redis 与 RocketMQ 结合,构建出一个具备如下特性的高并发秒杀系统:
- 并发控制得当:接口限流 + Redis 原子性操作
- 系统抗压能力强:消息削峰,MQ异步下单
- 数据一致性高:数据库落库有幂等保障
- 用户体验更好:请求秒级响应,后台异步处理
📘 八、后续可拓展点
- 秒杀结果异步通知(短信 / WebSocket)
- Redis 秒杀状态标识(用户是否成功)
- RocketMQ 事务消息提升可靠性
- 异常消息记录 + 自动补偿机制