当前位置: 首页 > news >正文

面试遇到的问题3

现在我有这样的一个场景:商品A秒杀场景,库存10单。我的设计:首先把 10存入redis,然后下单的时候首先使用 redis.increment(-1)方法扣减库存,大于0代表可以下单,否则提示库存卖完了。然后定时任务去看订单表中10分钟之内还没有付款的,使用lua脚本加库存。这样的一个设计,redis单体部署。一般能抗住多少的并发。有什么问题?如何优化

你的设计方案分析

你的秒杀方案大致流程如下:

  1. 初始化库存:预存库存 10 到 Redis(如 stock:skuA = 10)。
  2. 扣减库存:用户下单时,使用 redis.increment(key, -1) 扣减库存,返回 >=0 表示成功,<0 表示失败。
  3. 订单支付超时回滚:定时任务扫描 10分钟未支付订单,用 Lua 脚本恢复库存。
优点

简单高效:Redis 单机 QPS 可达 10W+,INCR 是原子操作,避免超卖。
避免复杂锁:直接利用 Redis 的原子性,无需额外分布式锁。


潜在问题及优化建议

1. 并发能力估算(Redis 单机)
  • INCR 操作:Redis 单机 QPS 约 5W~10W(取决于机器配置和网络)。
  • 实际并发上限
    • 如果仅执行 INCR,理论可抗 5W+ QPS
    • 但实际业务还需处理订单创建、支付等逻辑(这些操作通常比 Redis 慢得多),最终瓶颈可能在数据库
2. 关键问题
(1)超卖风险
  • 问题:若用户 INCR 扣减成功,但后续订单创建失败(如数据库超时),会导致库存少卖。
  • 解决
    • 两步提交:先扣减 Redis 库存,创建订单成功后,再异步更新数据库库存。若订单失败,则回滚 Redis(需记录操作日志)。
    • 预扣库存:使用 DECR + INCR 组合(扣减前检查 GET stock >=1),但需注意原子性问题(建议用 Lua 脚本)。
(2)库存回滚的原子性
  • 问题:定时任务回滚库存时,若多个任务并发执行,可能导致库存回滚多次(重复 INCR)。
  • 解决
    • Lua 脚本保证原子性:回滚时检查订单状态,避免重复回滚。
    • 分布式锁:回滚前对订单加锁(如 REDLOCK),但会增加复杂度。
(3)Redis 单点故障
  • 问题:单机 Redis 宕机时,整个秒杀不可用。
  • 解决
    • 主从集群 + 哨兵(至少 1主1从),但主从切换可能导致少量数据不一致。
    • Redis 集群(Codis/Twemproxy),但 INCR 的 key 必须落在同一节点(可用 hash tag 保证)。
(4)恶意请求
  • 问题:攻击者频繁请求 INCR,导致 Redis 压力过大。
  • 解决
    • 限流:网关层(如 Nginx)对用户/IP 限流。
    • 验证码:下单前先验证。

优化后的方案

1. 初始化库存: SET stock:skuA 10
2. 用户下单:a. Lua 脚本原子化操作:- 检查库存: if (redis.call("GET", KEYS[1]) >= 1)- 扣减库存: redis.call("DECR", KEYS[1])- 返回成功/失败b. 若成功,异步创建订单(消息队列削峰)
3. 支付超时回滚:a. 定时任务扫描未支付订单b. Lua 脚本检查订单状态后恢复库存
性能优化
  • Redis Pipeline:批量处理请求(适合高并发场景)。
  • 库存分段:将 stock:skuA 拆分为多个 key(如 stock:skuA:1~stock:skuA:5),分散热点。
  • 本地缓存:客户端缓存库存余量(如 剩余>0 才请求 Redis),减少无效请求。

并发能力总结

场景QPS 上限瓶颈点
仅 Redis INCR5W~10WRedis CPU/网络
完整下单流程(含DB)1K~5K数据库写入/订单处理逻辑

建议压测指标

  • Redis 单机:INCR 压测可达 8W QPS(4C8G 配置)。
  • 完整链路:建议从 1K QPS 开始逐步加压,观察数据库和 Redis 负载。

如果是redis集群的话,也没法解决呀,因为10这个数值也只会存到一台redis里面,所以最终的结果还是一台redis承受了一切,只不过主从部署能够容灾而已,而且在高并发下,再加上reids主从之间存在数据的延迟同步,也就是说,主节点中数量刚变成7的时候就挂了,从节点还没同步到这个7,还是之前的9,那么还是会出现超卖

你的观察非常准确!Redis 集群模式下,库存 Key 只会落到一个分片(主节点),高并发下仍然是单分片扛压力,且主从同步延迟可能导致超卖数据不一致

你的问题核心

  1. 热点 Key 问题:库存 stock:skuA 只会存在一个分片,无法分散压力。
  2. 主从同步延迟:主节点扣减到 7 后崩溃,从节点仍为 9,切换后导致超卖。
  3. 集群容灾但无法提升并发:集群仅解决数据分片和故障转移,无法解决单分片瓶颈。

解决方案

1. 避免单分片热点:库存分片

将库存拆分为多个 Key,分散到不同分片,最终汇总检查。
示例

# 初始化库存(分5片,总和=10)
SET stock:skuA:1 2
SET stock:skuA:2 2
SET stock:skuA:3 2
SET stock:skuA:4 2
SET stock:skuA:5 2

扣减逻辑(Lua 脚本):

-- 尝试从任意分片扣减1
for i = 1, 5 dolocal key = "stock:skuA:" .. iif redis.call("GET", key) > "0" thenredis.call("DECR", key)return 1  -- 扣减成功end
end
return 0  -- 库存不足

优点
✅ 压力分散到多个分片,提升并发能力。
缺点
❌ 需维护分片逻辑,复杂性增加。


2. 最终一致性:异步补偿

允许短暂超卖,但通过异步任务修正数据。
流程

  1. 快速扣减:Redis 扣减后立即返回成功。
  2. 异步校验:消息队列消费订单,检查剩余库存:
    • 若超额,取消订单并恢复库存。
    • 若正常,完成下单。

优点
✅ 极限高并发(如 100W QPS)。
缺点
❌ 用户体验可能受损(部分用户下单后被告知无效)。


3. 强一致性:分布式锁 + 数据库

RedLock(Redis 分布式锁)ZooKeeper 控制并发,最终库存落地到数据库。
流程

  1. 获取分布式锁。
  2. 查询/更新数据库库存(如 SELECT ... FOR UPDATE)。
  3. 释放锁。

优点
✅ 绝对避免超卖。
缺点
❌ 性能下降(数据库成为瓶颈)。


4. 混合方案:本地缓存 + Redis 限流

适用场景:超高并发但允许少量误差。
步骤

  1. 本地缓存库存:每个服务实例缓存剩余库存(如 10),本地扣减。
  2. Redis 批量同步:每 N 毫秒汇总本地扣减量,批量更新 Redis。
  3. 最终校验:Redis 异步同步到数据库。

优点
✅ 抗瞬时超高并发(如 100W QPS)。
缺点
❌ 可能超卖(需业务容忍)。


推荐方案

场景推荐方案QPS 预估一致性
常规秒杀(1W~10W QPS)库存分片 + Lua 脚本5W~10W最终一致
超高频秒杀(100W+ QPS)本地缓存 + 异步补偿100W+弱一致
强一致性需求RedLock + 数据库1K~5K强一致

最终建议

  1. 优先尝试库存分片,配合 Lua 脚本保证原子性。
  2. 若并发极高,改用本地缓存 + 异步修正(如电商容忍少量超卖)。
  3. 强一致性场景,用分布式锁 + 数据库,但需接受性能下降。

你的设计核心问题在于 单分片热点主从延迟,通过分片或异步补偿可有效缓解。

http://www.dtcms.com/a/303797.html

相关文章:

  • java String类
  • 打印机错误0x0000011B,0x00000709,0x00000BCD,修复工具下载。
  • ElasticSearch 的3种数据迁移方案
  • Nginx安装流程
  • Vue.js 指令系统完全指南:深入理解 v- 指令
  • 递归推理树(RR-Tree)系统:构建认知推理的骨架结构
  • 【C++】1. C++基础知识
  • 传媒行业视频制作:物理服务器租用是隐藏的效率引擎
  • 【Golang】Go语言Map数据类型
  • Qt 并行计算框架与应用
  • 【go】语言的匿名变量如何定义与使用
  • TDengine 中 TDgpt 异常检测的机器学习算法
  • spring data mongodb 入门使用手册
  • [Linux入门] Linux 部署本地 APT 仓库及 NFS 共享服务全攻略
  • Java -- String类-StringBuffer类-StringBuilder类
  • uniapp开发微信小程序(新旧版本对比:授权手机号登录、授权头像和昵称)
  • K-近邻算法(KNN算法)的K值的选取--交叉验证+网格搜索
  • 搭建专属AI聊天网站:NextChat + 蓝耘MaaS平台完整部署指南
  • 2025年7月技术问答第4期
  • 树形结构递归查询与嵌套结构转换:Flask + PostgreSQL 完整实现
  • 力扣面试150题--位1的个数
  • 【第四章自定义编辑器窗口_扩展默认的编辑器窗口_扩展Hierarchy窗口(7/11)】
  • QT项目 -仿QQ音乐的音乐播放器(第三节)
  • 深入理解 Qt 元对象系统 (Meta-Object System)
  • WindowsAPI|每天了解几个winAPI接口之网络配置相关文档Iphlpapi.h详细分析六
  • [明道云]-基础教学2-工作表字段 vs 控件:选哪种?
  • HTTP 与 HTTPS 的区别
  • mapbox进阶,mapbox-gl-draw绘图插件扩展,编辑模式支持点、线、面的捕捉
  • Objective-c 初阶——异常处理(try-catch)
  • 盛最多水的容器-双指针