本文目录导读:

我来为你提供一个完整的高并发秒杀系统设计案例,这是Java面试中最经典的系统设计题目之一。
系统需求分析
核心业务场景
- 限时抢购商品
- 高并发访问(可能达到10万+ QPS)
- 超卖问题必须避免
- 系统需要稳定可靠
核心功能
商品管理:上架、库存管理
2. 秒杀活动:活动配置、时间控制
3. 下单流程:秒杀请求处理
4. 订单系统:订单生成、状态管理
5. 支付系统:支付对接(可选)
架构设计
整体架构图
graph TB
A[客户端] --> B[CDN]
B --> C[负载均衡 Nginx/LVS]
C --> D[Web服务器集群]
D --> E[缓存层 Redis集群]
D --> F[消息队列 RabbitMQ/Kafka]
F --> G[订单服务]
G --> H[数据库 MySQL主从]
E --> H
详细架构设计
┌─────────────────────────────────────────────┐
│ 客户端层 │
│ Web端 / App端 / H5页面 │
└─────────────────┬───────────────────────────┘
│
┌─────────────────▼───────────────────────────┐
│ 接入层 │
│ CDN + 负载均衡 + 网关 + 限流 │
│ (Nginx + Sentinel/Guava RateLimiter) │
└─────────────────┬───────────────────────────┘
│
┌─────────────────▼───────────────────────────┐
│ 业务层 │
│ 秒杀接口 + 订单服务 + 支付服务 │
│ (SpringBoot 集群) │
└─────────────────┬───────────────────────────┘
│
┌─────────────────▼───────────────────────────┐
│ 中间件层 │
│ Redis集群 + MQ队列 + 任务调度 │
└─────────────────┬───────────────────────────┘
│
┌─────────────────▼───────────────────────────┐
│ 数据层 │
│ MySQL主从 + ES + 分库分表 │
└─────────────────────────────────────────────┘
核心技术实现
前端优化
// 1. 静态资源CDN加速
- 商品详情页静态化处理
- 使用CDN分发静态资源
- 按钮置灰,禁止重复提交
// 2. 后端接口动静分离
@Controller
public class SeckillController {
@GetMapping("/seckill/{goodsId}")
public String seckillPage(@PathVariable("goodsId") Long goodsId,
Model model) {
// 静态化页面数据
model.addAttribute("goods", goodsService.getGoodsInfo(goodsId));
model.addAttribute("startTime", seckillService.getStartTime(goodsId));
model.addAttribute("endTime", seckillService.getEndTime(goodsId));
return "seckill";
}
}
缓存设计(Redis)
@Service
public class SeckillService {
@Autowired
private StringRedisTemplate redisTemplate;
// 商品库存缓存Key
private static final String STOCK_KEY = "seckill:stock:";
// 用户购买记录Key
private static final String USER_KEY = "seckill:user:";
// 秒杀结果Key
private static final String RESULT_KEY = "seckill:result:";
/**
* 秒杀核心逻辑
*/
public SeckillResult seckill(Long userId, Long goodsId) {
// 1. 校验用户是否已购买过
if (redisTemplate.hasKey(USER_KEY + userId + ":" + goodsId)) {
return SeckillResult.error("您已参与过秒杀");
}
// 2. 预扣库存(Redis原子操作)
Long stock = redisTemplate.opsForValue()
.decrement(STOCK_KEY + goodsId);
if (stock < 0) {
// 库存不足,回补库存
redisTemplate.opsForValue().increment(STOCK_KEY + goodsId);
return SeckillResult.error("商品已售罄");
}
// 3. 记录用户购买信息
redisTemplate.opsForValue()
.set(USER_KEY + userId + ":" + goodsId, "1", 10, TimeUnit.MINUTES);
// 4. 发送消息到MQ,异步处理订单
sendOrderMessage(userId, goodsId);
return SeckillResult.success("秒杀成功");
}
/**
* 使用Lua脚本确保原子性
*/
public boolean seckillWithLua(Long userId, Long goodsId) {
String script =
"local stock = redis.call('get', KEYS[1])" +
"if tonumber(stock) <= 0 then return 0 end" +
"redis.call('decr', KEYS[1])" +
"redis.call('set', KEYS[2], 1)" +
"return 1";
DefaultRedisScript<Long> redisScript =
new DefaultRedisScript<>(script, Long.class);
Long result = redisTemplate.execute(redisScript,
Arrays.asList(STOCK_KEY + goodsId, USER_KEY + userId + ":" + goodsId));
return result == 1;
}
}
消息队列异步处理
@Slf4j
@Component
public class OrderConsumer {
@Autowired
private OrderService orderService;
/**
* 监听订单消息
*/
@RabbitListener(queues = "seckill.order.queue")
public void handleOrderMessage(SeckillMessage message) {
try {
// 创建订单
Order order = orderService.createOrder(
message.getUserId(),
message.getGoodsId()
);
// 更新库存(数据库最终一致性)
goodsService.updateStock(message.getGoodsId());
// 记录秒杀结果
redisTemplate.opsForValue().set(
RESULT_KEY + message.getUserId() + ":" + message.getGoodsId(),
JSON.toJSONString(order)
);
log.info("订单创建成功: userId={}, orderId={}",
message.getUserId(), order.getId());
} catch (Exception e) {
log.error("订单创建失败", e);
// 失败重试或补偿处理
sendCompensationMessage(message);
}
}
}
分布式锁应用
@Configuration
public class RedissonConfig {
@Bean
public RedissonClient redissonClient() {
Config config = new Config();
config.useSingleServer()
.setAddress("redis://localhost:6379")
.setDatabase(0);
return Redisson.create(config);
}
}
@Service
public class SeckillLockService {
@Autowired
private RedissonClient redissonClient;
public boolean seckillWithLock(Long userId, Long goodsId) {
String lockKey = "seckill:lock:" + goodsId;
RLock lock = redissonClient.getLock(lockKey);
try {
// 尝试获取分布式锁
if (!lock.tryLock(500, 20000, TimeUnit.MILLISECONDS)) {
throw new RuntimeException("系统繁忙,请稍后重试");
}
// 校验库存
int stock = goodsDao.getStock(goodsId);
if (stock <= 0) {
return false;
}
// 扣减库存
goodsDao.decreaseStock(goodsId);
// 创建订单
orderService.createOrder(userId, goodsId);
return true;
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return false;
} finally {
lock.unlock();
}
}
}
限流控制
@Component
public class RateLimitInterceptor implements HandlerInterceptor {
private static final Map<String, RateLimiter> RATE_LIMITERS =
new ConcurrentHashMap<>();
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
String uri = request.getRequestURI();
// 每用户每秒最多5个请求
String userId = request.getHeader("userId");
String key = uri + ":" + userId;
RateLimiter rateLimiter = RATE_LIMITERS.computeIfAbsent(
key, k -> RateLimiter.create(5)
);
if (!rateLimiter.tryAcquire()) {
response.setStatus(429);
response.getWriter().write("请求过于频繁");
return false;
}
return true;
}
}
数据库优化设计
数据库表设计
-- 秒杀商品表 CREATE TABLE `seckill_goods` ( `id` BIGINT NOT NULL AUTO_INCREMENT, `goods_name` VARCHAR(100) NOT NULL COMMENT '商品名称', `seckill_price` DECIMAL(10,2) NOT NULL COMMENT '秒杀价格', `original_price` DECIMAL(10,2) NOT NULL COMMENT '原价', `stock_count` INT NOT NULL COMMENT '库存数量', `start_time` DATETIME NOT NULL COMMENT '开始时间', `end_time` DATETIME NOT NULL COMMENT '结束时间', `status` TINYINT NOT NULL DEFAULT 0 COMMENT '状态:0未开始 1进行中 2已结束', `version` INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号', PRIMARY KEY (`id`), KEY `idx_start_time` (`start_time`), KEY `idx_end_time` (`end_time`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 秒杀订单表 CREATE TABLE `seckill_order` ( `id` BIGINT NOT NULL AUTO_INCREMENT, `user_id` BIGINT NOT NULL COMMENT '用户ID', `goods_id` BIGINT NOT NULL COMMENT '商品ID', `order_no` VARCHAR(32) NOT NULL COMMENT '订单号', `seckill_price` DECIMAL(10,2) NOT NULL COMMENT '秒杀价格', `status` TINYINT NOT NULL DEFAULT 0 COMMENT '状态:0待支付 1已支付 2已取消', `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, `pay_time` DATETIME DEFAULT NULL COMMENT '支付时间', PRIMARY KEY (`id`), UNIQUE KEY `uk_user_goods` (`user_id`, `goods_id`), KEY `idx_order_no` (`order_no`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
数据库优化策略
// 1. 分库分表设计
// 订单表按用户ID分表
@TableName("seckill_order")
@TableId(type = IdType.AUTO)
public class SeckillOrder {
@TableId(type = IdType.INPUT)
private Long id;
@TableField("user_id")
private Long userId;
// ... 其他字段
}
// 2. 读写分离配置
@Configuration
public class DataSourceConfig {
@Bean
@ConfigurationProperties("spring.datasource.master")
public DataSource masterDataSource() {
return DataSourceBuilder.create().build();
}
@Bean
@ConfigurationProperties("spring.datasource.slave")
public DataSource slaveDataSource() {
return DataSourceBuilder.create().build();
}
@Bean
public DynamicDataSource dynamicDataSource() {
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put(DataSourceType.MASTER, masterDataSource());
targetDataSources.put(DataSourceType.SLAVE, slaveDataSource());
return new DynamicDataSource(masterDataSource(), targetDataSources);
}
}
流量削峰策略
分层限流
@Configuration
public class FlowControlConfig {
@Bean
public RateLimiter rateLimiter() {
// 每秒100个请求
return RateLimiter.create(100);
}
@Component
public static class SeckillRateLimitFilter implements Filter {
@Autowired
private RateLimiter rateLimiter;
@Override
public void doFilter(ServletRequest request,
ServletResponse response,
FilterChain chain) throws IOException, ServletException {
if (!rateLimiter.tryAcquire()) {
HttpServletResponse resp = (HttpServletResponse) response;
resp.setStatus(503);
resp.setContentType("application/json");
resp.getWriter().write("{\"code\":503,\"message\":\"系统繁忙\"}");
return;
}
chain.doFilter(request, response);
}
}
}
请求排队
@Service
public class SeckillQueueService {
@Autowired
private RedissonClient redissonClient;
private static final String QUEUE_KEY = "seckill:queue";
// 排队请求
public Long enqueue(BigDecimal userId) {
RBlockingQueue<Long> queue = redissonClient.getBlockingQueue(QUEUE_KEY);
try {
queue.offer(userId, 5, TimeUnit.SECONDS);
return 1L;
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return 0L;
}
}
}
面试核心考点总结
超卖问题解决方案
- Redis原子操作:使用
decr命令预扣库存 - 分布式锁:Redisson实现分布式锁
- 数据库乐观锁:
version字段防止并发更新 - MQ串行化:消息队列串行处理订单
高并发优化策略
- CDN加速:静态资源就近访问
- 缓存预热:提前加载热点数据
- 异步处理:消息队列削峰
- 熔断降级:保护核心服务
数据一致性保障
- 最终一致性:MQ异步处理订单
- 主动回滚:失败时补偿库存
- 定期核对:Redis与数据库库存核对
扩展性问题
- 水平扩展:Web服务无状态化
- Redis集群:主从+哨兵+Cluster
- MQ集群:高可用消息队列
实战建议
面试回答思路
第一层:功能实现(怎么做)
- 前端限流、按钮置灰
- Redis预扣库存、MQ异步下单
第二层:架构设计(怎么扩展)
- 分布式架构、集群部署
- 缓存、MQ、分库分表
第三层:性能优化(怎么优化)
- 并发压测、瓶颈分析
- JVM调优、SQL优化
第四层:可用性保障(怎么保障)
- 容灾备份、限流降级
- 监控告警、日志排查
常见面试问题
如何防止库存超卖?
2. 秒杀系统为什么用MQ?
3. Redis如何保证库存原子性?
4. 如何设计高可用的架构?
5. 系统如何横向扩展?
6. 如何保证数据最终一致性?
系统指标参考
并发量:设计支持10万+ QPS
响应时间:页面加载 < 200ms
下单延迟:< 2秒
成功率:> 99.9%
数据一致性:最终一致
这个设计案例涵盖了Java面试中的核心技术点,包括高并发处理、分布式系统设计、缓存优化、消息队列等,面试时能够清晰表达这些设计和实现思路,会给面试官留下深刻印象。