
一个企业级电商秒杀场景面试官真正想听的不是“用户点击抢购”而是如何解决超卖、高并发、库存一致性、削峰填谷、订单最终一致性。下面按照互联网公司常见方案讲一遍完整链路。一、业务架构用户 ↓ Nginx ↓ Gateway ↓ 秒杀服务 ↓ Redis ↓ RocketMQ/Kafka ↓ 订单服务 ↓ 库存服务 ↓ MySQL二、秒杀开始前预热阶段秒杀前几分钟通常会进行预热。1. 商品信息加载到Redis数据库商品ID:1001 库存:10000预热seckill:stock:1001 10000存入Redis。这样秒杀时不会直接查数据库。2. 本地缓存预热例如ConcurrentHashMapLong, Boolean保存1001 - true表示还有库存当库存卖完1001 - false后续请求直接拒绝。避免继续访问Redis。3. 用户资格校验例如会员等级 黑名单 限购次数提前准备。三、秒杀开始核心链路假设库存10000 用户100万人第一步请求进入Gateway用户 ↓ GatewayGateway负责限流例如100万请求 ↓ 限流 ↓ 每秒1万防止系统被打爆。常见SentinelGateway限流第二步秒杀资格判断判断是否登录 是否实名 是否限购 是否重复购买例如SETNXuser:1001:product:2001存在说明抢过了。直接返回请勿重复抢购第三步Redis预扣库存核心操作DECR seckill:stock:1001Redis10000 9999 9998 ...如果库存 0说明抢光了。立即库存不足结束。为什么不用数据库扣减如果直接update stock set countcount-1100万人同时执行数据库直接崩掉所以必须Redis承担流量。四、削峰填谷MQ扣减Redis成功后不要立即创建订单。而是发送MQ。秒杀服务 ↓ RocketMQ消息内容{ userId:1001, productId:2001 }这样100万请求 ↓ Redis ↓ MQ ↓ 慢慢消费数据库压力大幅下降。五、订单服务消费MQ消费者收到消息订单消费者 ↓ 创建订单生成订单insert into order(...)订单状态WAIT_PAY待支付。六、库存服务扣减数据库库存订单创建成功后调用库存服务。update stock set countcount-1 where product_id1001 and count0;这里是最终库存落库。Redis库存只是临时库存。数据库库存才是真实库存。七、支付环节用户支付支付宝 微信回调支付服务 ↓ 订单服务订单状态WAIT_PAY ↓ PAID八、超时未支付例如30分钟未支付发送延迟消息RocketMQ Delay消费者处理关闭订单 恢复库存恢复RedisINCR stock数据库update stock set countcount1九、防止超卖重点面试必问。方案1Redis原子扣减DECR天然原子。方案2数据库乐观锁update stock set countcount-1 where id? and count0方案3版本号versionversion1CAS思想。十、防止重复下单RedisuserId_productId例如SETNXorder:1001:2001成功第一次购买失败重复购买十一、库存一致性问题面试高频。例如Redis扣减成功 MQ发送失败怎么办解决事务消息例如Apache RocketMQ 事务消息流程Redis扣库存 ↓ 半消息 ↓ 本地事务 ↓ 提交消息保证最终一致。十二、企业级完整链路用户点击秒杀 ↓ Gateway限流 ↓ 登录校验 ↓ 资格校验 ↓ Redis判断库存 ↓ Redis预扣库存 ↓ SETNX防重复购买 ↓ 发送MQ ↓ 订单服务消费 ↓ 创建订单 ↓ 库存服务落库 ↓ 返回抢购成功 ↓ 用户支付 ↓ 修改订单状态 ↓ 发货面试版 1 分钟总结秒杀系统核心目标是解决高并发和超卖问题。秒杀开始前会将库存预热到 Redis。用户请求经过 Gateway 限流后先进行资格校验和重复下单校验然后通过 Redis 原子扣减库存实现快速响应。扣减成功后不直接操作数据库而是发送 MQ 进行削峰填谷。订单服务异步消费消息创建订单库存服务完成数据库库存扣减。支付成功后修改订单状态超时未支付通过延迟消息关闭订单并回补库存。整个过程中利用 Redis、MQ、乐观锁、幂等控制和最终一致性机制保证系统稳定运行并避免超卖。