面试题之如何设计一个秒杀系统?

系统痛点:

痛点分析:
  • 瞬时并发量大:大量并发请求瞬间涌入,网站流量激增,可能压垮系统。
  • 库存少:访问请求远大于库存,只有少量用户可以秒杀成功
  • 架构设计原则:
    • 系统隔离:
      • 独立部署秒杀服务,避免影响主线业务流程
      • 独立数据库/缓存集群等
      • 独立域名/CDN等
    • 分层削峰:
      • 前端层:静态化、请求拦截
      • 服务层:队列缓冲、异步处理
      • 数据层:预减库存、最终一致
业务流程:
用户 → CDN → 负载均衡 → 网关(限流) → 秒杀服务 → 消息队列 → 订单服务
                                   ↘ Redis集群 ↘ 数据库集群

实施与解决

系统设计图:

分层设计:

以上为秒杀系统的设计图:根据访问层、负债层、服务层、业务层支撑层、数据层阐述设计

访问层

当用户访问系统商品时,需要将商品从动态网页转为静态网页。减少动态请求到服务端的压力,服务端只需要处理秒杀即可

前端上做一些逻辑处理:

  • 静态资源分离:将活动页静态化,部署到CDN上
  • 活动前禁用按钮:减少不必要的资源请求
  • 按钮防重复点击:当用户点击提交后,立即灰显禁用,限制用户多次提交页面
  • 随机延迟请求:客户端随机添加微笑言辞
  • 验证码/答题:通过滑动验证码、图形验证码等拦截机器人及防止用户控制多个设备进行提交
  • 排队体验:增加等待中、抢购中的提示,防止 用户多次刷新

服务层

        前端校验通过后,通过多台NG的转发,到后端处理秒杀功能,单台ng可以处理2-3万的数据量,一旦NG集群了,就需要在NG上方进行部署网络及硬件级别相关的F5/LVS等。通过NG校验及转发后,就会到服务端的网关集成器,比如ribbon或者loadbalancer进行客户端的负债均衡的转发,通过上面四级的负债均衡大概能处理每秒10+万的qps请求并发量。

  • 网关限流操作:
    • 限流熔断:NG/Lua实现IP/用户限流
    • 请求过滤:恶意请求识别与拦截
    • URL动态化:活动开始前不暴露真实的接口
    • DNS轮询:让DNS服务商针对网站提供的域名绑定多个IP地址
  • 支撑层
    • 异步处理:请求先入队列,异步处理订单
    • 缓存预热:提前加载热点数据到redis
    • 分层校验:
      • 第一层:用户资格校验(是否登录、黑名单等)
      • 第二层:库存校验(redis预扣减库存)
      • 第三层:最终校验(数据库确认)
      • 接口防重校验:(防止绕过前端的恶意用户刷单,setNX保证同一个时间、同一个商品、同一用户只能下单成功一次)
    • 库存方案:
      • 库存分段:将库存拆分成多个段(如1000库存拆成10个100)
      • 预扣库存:redis预减库存(LUA脚本进行数据操作),异步(秒杀时客户端将请求放到MQ消息中发送给MQ服务端,消息者通过消费队列来进行下单操作并持久化到数据库)同步到DB
      • 库存回滚:支付超时后自动释放库存
  • 数据一致性:
    • 最终一致性:通过消息队列异步处理
    • 事务消息:通过rocketMQ事务宝恒下单与扣减库存
    • 对账系统:定期核对redis与数据库的库存告警监控
    • 日志监控:防止系统出现问题后处理
限流相关:
  • 核心要点:
    • NG需要配置限流,防止恶意绕过我们前端的一些D DOS攻击;
    • 网关sentinel对不同的服务节点限流和熔断机制;
    • 通过MQ的来削峰,通过MQ来减轻下游的服务压力
  • 降级方案
<think>我们正在设计一个支持百万级并发的秒杀系统架构。结合引用[1][2][3][4]中的核心思想,我们将从架构设计、技术选型、关键问题解决等方面展开。以下是一个分层的架构设计方案: ### 一、核心挑战 1. **瞬时高并发**:百万QPS集中在秒级时间窗口 2. **资源竞争**:有限库存的超卖问题 3. **系统容灾**:避免单点故障导致雪崩 4. **数据一致性**:库存扣减与订单创建的强一致性 ### 二、分层架构设计 ```mermaid graph TD A[客户端] --> B[CDN+边缘计算] B --> C[接入层] C --> D[业务层] D --> E[数据层] D --> F[异步队列] F --> G[订单服务] F --> H[库存服务] ``` #### 1. **客户端优化** - **静态资源分离**:将商品图片/JS/CSS推送到CDN - **请求合并**:客户端倒计时结束后合并10次请求为1次 - **验证码干扰**:在秒杀开始前加入图形验证码,防刷[^2] #### 2. **接入层设计(Nginx集群)** ```nginx # 限流配置(每秒1000请求/IP) limit_req_zone $binary_remote_addr zone=secKill:10m rate=1000r/s; location /seckill { limit_req zone=secKill burst=5 nodelay; proxy_pass http://seckill_backend; } ``` - **Lua脚本预检**:使用OpenResty在Nginx层校验用户资格 ```lua local token = ngx.var.arg_token if not token then ngx.exit(403) end -- 校验令牌有效性 local ok = redis:call("EXISTS", "seckill:token:"..token) if ok == 0 then ngx.exit(403) end ``` #### 3. **业务层设计(微服务集群)** **关键服务拆分**: - **风控服务**:实时黑名单(Redis BloomFilter) - **库存服务**:Redis集群+Lua原子扣减 - **订单服务**:MQ异步创建订单 **库存扣减Lua脚本**: ```lua -- KEYS[1]:商品库存key ARGV[1]:扣减数量 local stock = tonumber(redis.call('GET', KEYS[1])) if stock >= tonumber(ARGV[1]) then return redis.call('DECRBY', KEYS[1], ARGV[1]) end return -1 ``` #### 4. **数据层设计** - **缓存架构**: ```mermaid graph LR A[客户端] --> B[Redis集群] B --> C[主从同步] C --> D[持久化] ``` - 热点库存采用**分片存储**:`stock_{sku_id%1024}` - **数据库设计**: - 订单表按用户ID分库分表(1024库×1024表) - 库存表单独部署(避免与订单库竞争) #### 5. **异步订单流程** ```mermaid sequenceDiagram 用户->>+库存服务: 请求扣减库存 库存服务->>Redis: 执行Lua扣减 Redis-->>库存服务: 返回成功 库存服务->>MQ: 发送订单消息 MQ->>订单服务: 消费消息 订单服务->>DB: 创建订单 ``` ### 三、关键技术方案 #### 1. 防超卖方案 - **Redis原子操作**:Lua脚本保证库存扣减原子性 - **预扣库存机制**: ```php // 预扣库存(防止超卖) $result = Redis::eval($LUA_SCRIPT, 1, $stockKey, $quantity); if ($result >= 0) { // 发送MQ消息 $mq->send(json_encode(['sku'=>$sku, 'user'=>$userId])); } else { throw new Exception('库存不足'); } ``` #### 2. 热点数据处理 - **动态热点探测**:Flink实时监控商品访问频次 ```java // Flink热点发现伪代码 DataStream<ItemAccess> stream = env.addSource(kafkaSource); stream.keyBy("itemId") .window(TumblingProcessingTimeWindows.of(Time.seconds(1))) .trigger(new ContinuousTrigger(100)) // 每100条触发计算 .aggregate(new HotItemAggregator()); ``` - **本地缓存**:在应用层缓存热点商品信息(Guava Cache) #### 3. 限流降级策略 | 层级 | 工具 | 策略 | |-------------|---------------------|-------------------------------| | 接入层 | Nginx+Lua | IP限流/令牌桶 | | 服务层 | Sentinel | QPS限流/熔断降级 | | 资源隔离 | Hystrix | 线程池隔离秒杀服务 | #### 4. 数据一致性保障 - **最终一致性方案**: 1. 扣减Redis库存 2. MQ发送订单消息 3. 消费成功后写订单库 4. 定时任务核对库存与订单 ```sql /* 库存回补逻辑 */ UPDATE stock s SET s.quantity = s.quantity + (SELECT COUNT(*) FROM orders o WHERE o.status='timeout' AND o.item_id=s.item_id) ``` - **分布式事务**:对账任务修复异常数据(每日执行) ### 四、容灾设计 1. **多机房部署**: - 同城双活架构(库存服务跨机房同步) 2. **故障转移**: ```mermaid graph LR A[健康检查] -->|异常| B[切流量] B --> C[备用集群] ``` 3. **压力测试**: ```bash # 使用wrk模拟百万并发 wrk -t100 -c100000 -d30s http://seckill.example.com ``` ### 五、性能指标参考 | 环节 | 要求 | 实现方案 | |-----------------|------------------|------------------------------| | 请求响应 | <100ms | Nginx+Lua快速拦截 | | 库存操作 | <10ms | Redis内存操作 | | 订单创建 | <500ms | MQ异步处理 | | 系统吞吐量 | >100万QPS | 水平扩展+无状态服务 | > 📌 **关键提示**: > - 99%的流量应在接入层被拦截[^1] > - 库存数据必须走内存操作 > - 订单创建必须异步化 ### 六、架构演进路线 ```mermaid graph LR A[单机] --> B[服务拆分] B --> C[缓存集群] C --> D[数据库分库分表] D --> E[读写分离] E --> F[异地多活] ``` ### 相关问题 1. 如何动态识别秒杀系统中的热点商品? 2. 在库存扣减过程中,如何避免Redis单点故障? 3. 如果消息队列积压导致订单延迟创建,该如何处理? 4. 如何设计一个库存对账系统来保证最终一致性? [^1]: 千万级高并发秒杀系统设计套路 [^2]: 高频面试题深度解析:如何设计一个百万级并发的秒杀系统? [^3]: 电商平台每秒100万高并发请求的秒杀系统架构设计和实现 [^4]: 高并发秒杀系统架构设计与实现(RuoYi-Cloud-SecKill)</think>### 高并发秒杀系统架构设计(百万级并发) 设计百万级并发秒杀系统需解决三大核心问题:**瞬时流量冲击**、**资源竞争**和**数据一致性**。以下是分层架构设计方案: --- ### 一、架构分层设计 ```mermaid graph TD A[客户端层] --> B[接入层] B --> C[业务层] C --> D[数据层] D --> E[异步服务] ``` #### 1. **客户端层优化** - **静态资源CDN分发**:将商品图片/JS/CSS推送到全球CDN节点 - **请求合并**:客户端倒计时结束后合并10次请求为1次 - **验证码干扰**:在秒杀开始前加入图形验证码,分散请求峰值[^2] ```javascript // 前端请求合并示例 setInterval(() => { if (countdown <= 0) { batchRequest(items); // 合并多个商品请求 } }, 100); ``` #### 2. **接入层设计(千万QPS承载)** - **OpenResty(Nginx+Lua)**: ```lua -- Nginx层库存预扣减 local redis = require "resty.redis" local red = redis:new() local stock_key = "sk:"..ngx.var.sku_id local remain = red:get(stock_key) if tonumber(remain) <= 0 then ngx.exit(503) -- 直接返回库存不足 end ``` - **分布式限流**: - IP限流:`limit_req_zone $binary_remote_addr` - 用户ID限流:Redis `INCR` + `EXPIRE` - **流量削峰**:令牌桶算法控制进入后端流量 #### 3. **业务层设计(无状态服务)** - **服务拆分**: - 风控服务:实时黑名单(Redis BloomFilter) - 库存服务:Redis集群+Lua原子操作 - 订单服务:MQ异步创建订单 - **热点探测**: ```java // Flink实时热点发现(引用[3]) DataStream<ItemAccess> stream = env.addSource(kafkaSource); stream.keyBy("itemId") .window(TumblingProcessingTimeWindows.of(Time.seconds(1))) .aggregate(new HotItemAggregator()); ``` #### 4. **数据层设计** - **缓存架构**: - L1:本地缓存(Caffeine)存储热点商品 - L2:Redis集群(Proxy分片) ```mermaid graph LR A[客户端] --> B[LocalCache] B --> C[Redis Cluster] C --> D[DB] ``` - **库存扣减方案**: ```lua -- Redis原子操作(解决超卖) local stock = tonumber(redis.call('GET', KEYS[1])) if stock >= tonumber(ARGV[1]) then return redis.call('DECRBY', KEYS[1], ARGV[1]) end return -1 ``` - **数据库优化**: - 分库分表:订单表按用户ID哈希分1024库 - 读写分离:写主库 + 读从库 #### 5. **异步服务层** - **订单创建流程**: ```mermaid sequenceDiagram 用户->>库存服务: 请求扣减库存 库存服务->>Redis: 执行Lua扣减 Redis-->>库存服务: 返回成功 库存服务->>RabbitMQ: 发送订单消息 RabbitMQ->>订单服务: 创建订单 订单服务->>DB: 持久化订单 ``` - **库存回补机制**: ```sql /* 定时任务扫描超时订单 */ UPDATE items SET stock = stock + 1 WHERE id IN ( SELECT item_id FROM orders WHERE status = 'timeout' AND create_time < NOW() - 10m ) ``` --- ### 二、核心问题解决方案 | 问题 | 解决方案 | 实现方式 | |---------------------|------------------------------|------------------------------| | 超卖问题 | Redis原子操作 | Lua脚本+单线程模型 | | 热点商品 | 本地缓存+动态分片 | Caffeine+Redis Cluster | | 订单创建瓶颈 | MQ异步解耦 | RabbitMQ/Kafka分区消费 | | 数据一致性 | 定时对账任务 | XXL-JOB分布式调度[^4] | | 服务雪崩 | 熔断降级机制 | Sentinel/Hystrix | --- ### 三、容灾与监控 1. **多机房容灾**: - 同城双活部署架构 - 库存数据跨机房同步 2. **全链路压测**: ```bash # 模拟百万并发 wrk -t1000 -c100000 -d30s http://seckill.example.com ``` 3. **监控体系**: - 日志:ELK收集Nginx/服务日志 - 指标:Prometheus + Grafana监控QPS/库存量 - 追踪:SkyWalking全链路追踪 --- ### 四、性能优化关键点 1. **读优化**: - 商品详情静态化:CDN缓存HTML片段 - 查询分离:ES存储商品搜索数据 2. **写优化**: - 批量提交:合并DB写入请求 - 缓冲队列:Kafka积压百万级消息 3. **网络优化**: - 长连接复用:HTTP/2保持连接 - 数据压缩:Protobuf序列化 --- ### 五、技术栈参考 | 层级 | 技术选型 | |------------|-----------------------------------| | 接入层 | OpenResty + Lua + Kong | | 业务层 | Spring Cloud + Sentinel | | 缓存 | Redis Cluster + Caffeine | | 消息队列 | Kafka/RocketMQ | | 数据库 | MySQL(分库分表)+ TiDB | | 监控 | Prometheus + SkyWalking + ELK | > 💡 **关键原则**: > - 数据量:尽量少(前端过滤无效请求) > - 路径短:减少IO(Nginx层拦截80%流量) > - 无状态:便于水平扩展 > - 最终一致:接受短时不一致(通过对账修复) --- ### 相关问题 1. 如何动态识别秒杀系统中的热点商品? 2. Redis集群故障时如何保证库存数据安全? 3. 消息队列积压导致订单延迟如何处理? 4. 分库分表后如何实现跨分片的订单查询? [^1]: 千万级高并发秒杀系统设计套路 [^2]: 高频面试题深度解析:如何设计一个百万级并发的秒杀系统? [^3]: 电商平台每秒100万高并发请求的秒杀系统架构设计和实现 [^4]: 高并发秒杀系统架构设计与实现(RuoYi-Cloud-SecKill)
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zbqice007

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值