跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书AI学习GitHub 精选镜像AI工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
Javajava算法

设计一个支持万人同时抢购商品的秒杀系统

秒杀系统设计核心在于高并发下的库存一致性与系统稳定性。通过分层架构实现流量削峰,利用 Redis 原子操作与 Lua 脚本防止超卖,结合消息队列异步处理订单以解耦业务。缓存预热、多级限流及熔断降级机制保障服务可用性,配合监控告警与数据对账确保最终一致性。整体方案兼顾性能与安全,适用于大规模抢购场景。

未来可期发布于 2026/3/25更新于 2026/10/24100 浏览
设计一个支持万人同时抢购商品的秒杀系统

一、系统架构设计

1. 分层架构
客户端层 → 接入层 → 业务服务层 → 数据层
限流      缓存      队列      数据库
2. 具体组件
  • 客户端:静态资源 CDN、倒计时校准、防重复提交。
  • 接入层:Nginx+Lua/OpenResty,做第一层限流和缓存。
  • 业务层:秒杀服务集群(无状态)、消息队列(Kafka/RocketMQ)、缓存集群(Redis Cluster)。
  • 数据层:主从数据库(读写分离)、分库分表(按商品/时间)。

二、核心问题解决方案

1. 超卖问题
Redis 原子操作

利用 Lua 脚本保证库存扣减的原子性,避免并发竞争。

-- 使用 Redis 的 DECR 原子操作扣减库存
local stock = tonumber(redis.call('GET', KEYS[1]))
if stock and stock > 0 then
    redis.call('DECR', KEYS[1])
    return 1
end
return 0
数据库乐观锁

作为兜底方案,防止极端情况下的数据不一致。

UPDATE products SET stock = stock - 1, version = version + 1 
WHERE id = ? AND stock > 0 AND version = ?
预扣库存策略

先预扣 Redis 库存,再异步同步到 DB,提升响应速度。

public   {
        + productId;
       redisTemplate.opsForValue().decrement(key, count);
     (remaining >= ) {
        sendStockDeductMessage(productId, count); 
         ;
    }  {
        redisTemplate.opsForValue().increment(key, count); 
         ;
    }
}
boolean
preDeductStock
(String productId, int count)
String
key
=
"seckill:stock:"
Long
remaining
=
if
0
// 发送 MQ 消息异步扣减数据库
return
true
else
// 库存不足,回滚
return
false
2. 高并发请求处理
流量削峰

通过消息队列缓冲请求,将瞬时流量转化为平滑消费。

@Component
public class SeckillService {
    @Autowired
    private RocketMQTemplate mqTemplate;

    public SeckillResult seckill(SeckillRequest request) {
        if (!validate(request)) {
            return SeckillResult.fail("校验失败");
        }
        String requestId = generateRequestId(request);
        // 请求入队,立即返回
        mqTemplate.sendOneWay("seckill-topic", MessageBuilder.withPayload(request).build());
        return SeckillResult.processing(requestId);
    }
}
分层过滤

所有请求经过多级过滤,只有合法且有余量的请求才能进入核心逻辑。

所有请求 → 合法性校验 → 库存校验 → 频率控制 → 实际下单
100 万     → 50 万       → 10 万       → 5 万       → 1 万
3. 系统性能优化
缓存策略

采用多级缓存配置,减少数据库压力。

一级:JVM 本地缓存 (Caffeine) - 热点商品
二级:Redis 集群 - 库存信息
三级:数据库 - 最终一致性
读多写少优化

活动开始前进行缓存预热,确保热点数据在内存中。

@Service
public class CacheWarmUpService {
    @PostConstruct
    public void warmUpSeckillProducts() {
        List<Product> hotProducts = loadHotProducts();
        for (Product product : hotProducts) {
            redisTemplate.opsForValue().set(
                "stock:" + product.getId(), product.getStock()
            );
            redisTemplate.opsForValue().set(
                "product:" + product.getId(), JSON.toJSONString(product)
            );
            bloomFilter.add(product.getId());
        }
    }
}
4. 详细实现方案
秒杀流程

这里展示的是核心逻辑伪代码,重点在于风险拦截与快速失败。

class SeckillSystem:
    def process_seckill(self, user_id, product_id):
        # 1. 恶意请求拦截
        if not self.check_risk(user_id):
            return {"code": 403, "msg": "访问过于频繁"}
        # 2. 布隆过滤器快速判断
        if not bloom_filter.contains(product_id):
            return {"code": 404, "msg": "商品不存在"}
        # 3. 内存标记(已售罄的商品直接返回)
        if sold_out_flags.get(product_id):
            return {"code": 400, "msg": "已售罄"}
        # 4. Redis 原子扣减库存
        if not self.deduct_stock_in_redis(product_id):
            sold_out_flags[product_id] = True
            return {"code": 400, "msg": "库存不足"}
        # 5. 生成订单 ID(雪花算法)
        order_id = snowflake.generate()
        # 6. 订单信息入队
        mq.send({
            "order_id": order_id,
            "user_id": user_id,
            "product_id": product_id,
            "time": time.time()
        })
        # 7. 返回排队中
        return {
            "code": 200,
            "msg": "排队中",
            "order_id": order_id,
            "queue_position": get_queue_position(order_id)
        }
库存同步方案

后台服务消费消息,异步更新数据库并处理异常。

@Component
@Slf4j
public class StockSyncService {
    @Transactional
    public void syncStockToDB(String productId, int count) {
        try {
            boolean success = productDAO.deductStock(productId, count);
            if (success) {
                redisTemplate.opsForValue().set(
                    "stock_final:" + productId, getDBStock(productId)
                );
                soldOutCache.remove(productId);
            }
        } catch (Exception e) {
            log.error("库存同步失败", e);
            alertService.sendAlert(e);
        }
    }

    @Scheduled(cron = "0 */5 * * * ?")
    public void stockReconciliation() {
        List<Product> products = productDAO.getAllSeckillProducts();
        for (Product product : products) {
            Integer redisStock = getRedisStock(product.getId());
            Integer dbStock = product.getStock();
            if (!Objects.equals(redisStock, dbStock)) {
                log.warn("库存不一致:productId={}, redis={}, db={}", 
                         product.getId(), redisStock, dbStock);
                fixStockInconsistency(product.getId(), dbStock);
            }
        }
    }
}

三、高可用保障

1. 限流降级策略

多维度限流规则,保护系统不被压垮。

用户维度:每个用户 10 次/分钟
IP 维度:每个 IP 1000 次/分钟
商品维度:每个商品 10000 次/分钟
总 QPS: 系统最大承受 50000 QPS
2. 熔断降级

当依赖服务不可用时,快速失败,避免雪崩。

@RestController
@Slf4j
public class SeckillController {
    @GetMapping("/seckill/{productId}")
    @HystrixCommand(fallbackMethod = "seckillFallback", 
                     commandProperties = {
                         @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "1000"),
                         @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "20")
                     })
    public Response seckill(@PathVariable String productId, @RequestParam String userId) {
        return seckillService.process(userId, productId);
    }

    public Response seckillFallback(String productId, String userId) {
        return Response.error("系统繁忙,请稍后重试");
    }
}

四、监控与告警

1. 关键监控指标
  • 系统层面:QPS、RT、错误率、CPU/内存使用率。
  • 应用层面:库存扣减成功率、消息堆积量。
  • 业务层面:抢购成功率、用户排队时长。
2. 监控实现

使用 Micrometer 等工具记录关键指标。

@Component
public class SeckillMonitor {
    private final MeterRegistry meterRegistry;

    public void recordSeckill(String productId, boolean success, long cost) {
        meterRegistry.counter("seckill.requests.total").increment();
        if (success) {
            meterRegistry.counter("seckill.success.total").increment();
        } else {
            meterRegistry.counter("seckill.fail.total").increment();
        }
        meterRegistry.timer("seckill.process.time")
            .record(cost, TimeUnit.MILLISECONDS);
        meterRegistry.gauge("seckill.stock." + productId, getCurrentStock(productId));
    }
}

五、部署与扩展

1. 弹性扩展策略
  • 水平扩展:无状态服务可快速扩容。
  • 自动伸缩:基于 CPU 使用率或 QPS 自动扩缩容。
  • 异地多活:重要业务支持多机房部署。
2. 压测方案

模拟真实场景验证系统稳定性。

场景 1: 库存预热,10 万用户同时抢 1 万商品
场景 2: 持续高压,5 万 QPS 持续 5 分钟
场景 3: 峰值冲击,瞬间 20 万 QPS
目标:成功率 >99.9%,平均 RT <100ms,错误率 <0.1%

六、安全考虑

  • 防刷机制:验证码(峰值时降级)、设备指纹、行为分析。
  • 数据安全:关键数据加密、操作日志记录、防篡改校验。

总结要点

  1. 架构核心:分层过滤 + 异步处理 + 最终一致。
  2. 库存核心:Redis 原子操作 + 消息队列 + 数据库乐观锁。
  3. 性能核心:缓存预热 + 流量削峰 + 读写分离。
  4. 稳定核心:熔断降级 + 限流隔离 + 快速失败。

面试回答

如果面试官问到秒杀系统设计,我会从架构、核心问题和兜底策略三个维度来回答。

首先是架构设计上要动静分离、分层削峰。把系统分为静态资源分离(CDN)、网关层限流(Nginx/Sentinel)、业务逻辑后置(消息队列)。秒杀的核心是'下单扣库存',这个逻辑绝不放在前台实时处理。用户点击'抢购'后,前端直接返回'排队中',请求进入消息队列。海量并发被平滑成顺序处理的流量,后端服务按照自己的能力慢慢消费,实现削峰填谷。服务独立部署,避免影响商城其他正常功能。

其次是针对超卖、库存扣减和高并发请求这三个核心问题。 解决超卖和库存扣减,我的方案是预扣库存。活动开始前,把商品库存加载到 Redis。Redis 单线程内存操作,保证原子性。使用 DECR 或 LUA 脚本扣减库存,彻底杜绝超卖。后台服务从队列消费,成功扣减 Redis 库存后,生成订单 ID,再异步去更新数据库。应对高并发请求,除了网关层限流,秒杀服务本身也要做限流,比如用信号量或令牌桶控制处理线程数。服务做成无状态的,方便横向扩容。对于爆款商品的热点 Key,可以做 Key 散列分散压力。

最后是关键的细节和兜底策略。 防刷与验证方面,前端加入计算型验证码,下单前校验用户资格。异步下单与结果轮询,用户提交后服务端返回'排队 ID',前端轮询查询最终结果。数据一致性对账,定时核对 Redis、数据库库存和订单状态。降级与熔断,如果 Redis 或数据库访问慢,要有熔断机制,防止服务被拖垮。

总结一下,设计思路是:前端限流拦截,请求队列削峰;Redis 原子扣减防超卖;服务无状态化应对高并发;再通过异步、对账等手段保证最终一致性和用户体验。

目录

  1. 1. 分层架构
  2. 2. 具体组件
  3. 1. 超卖问题
  4. Redis 原子操作
  5. 数据库乐观锁
  6. 预扣库存策略
  7. 2. 高并发请求处理
  8. 流量削峰
  9. 分层过滤
  10. 3. 系统性能优化
  11. 缓存策略
  12. 读多写少优化
  13. 4. 详细实现方案
  14. 秒杀流程
  15. 库存同步方案
  16. 1. 限流降级策略
  17. 2. 熔断降级
  18. 1. 关键监控指标
  19. 2. 监控实现
  20. 1. 弹性扩展策略
  21. 2. 压测方案

更多推荐文章

查看全部
  • 2025年12月GESP C++五级真题:相等序列
  • Android 程序员发展前景与 Framework 底层技术解析
  • 数据结构:顺序表与链表经典算法实战
  • MiniMax 海螺 AI 视频:图片与文本生成高质量视频
  • 统一 OpenAI API 格式调用 20+ 主流大模型方案
  • MySQL 索引机制实战:从 ORM 到 EXPLAIN 分析
  • 基于 Docker 部署 DeskClaw 人机协同办公平台
  • 基于 AI 智能体的费曼学习法知识助手实战
  • Whisper-WebUI 语音转文字工具部署与使用教程
  • OpenClaw 全平台卸载指南:Windows、macOS、Linux 与包管理器
  • Dify 工作流发布为 MCP Server 实战指南
  • 基于 Qwen1.5-1.8B-GPTQ-Int4 与 Chainlit 的思维导图自动生成
  • Redis IO 多路复用模型详解
  • ToDesk、顺网云与海马云运行 DeepSeek 大模型性能对比
  • 一周烧光 14 亿 Token!OpenClaw 架构设计的 10 条血泪教训
  • VSCode Copilot 登录失败的常见原因与排查方案
  • VSCode 集成 OpenAI 兼容模型配置指南
  • 基于管道通信的 Linux 进程池实现详解
  • 动态规划:打家劫舍类问题
  • AI 在医疗领域的十大应用场景解析

相关免费在线工具

  • Keycode 信息

    查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online

  • Escape 与 Native 编解码

    JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online

  • JavaScript / HTML 格式化

    使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online

  • JavaScript 压缩与混淆

    Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online