无标题文档
来源:无标题文档
通过网盘分享的文件:字节一面:库存超卖怎么办?.mp4
链接: https://pan.baidu.com/s/1I1w_8x3Gror42MOk_PNLFA?pwd=gmgz 提取码: gmgz
口播文案:
兄弟们!面试被问 “怎么解决库存超卖”,你是不是张口就说 “加个锁”,然后面试官直接摇头?!(身体往前凑,语气带点共鸣)最近后台私信快炸了,全是问这个的 —— 我跟你们说,超卖可不是随便的面试题,那是电商搞秒杀、做促销的 “生死线”!
去年有个粉丝跟我吐槽,他们公司 618 搞活动,就因为没防住超卖,多给用户发了 2000 单,光赔偿就亏了十几万!(语气加重,强调后果)今天我把超卖问题拆成 7 层方案,从新手能懂的基础招,到大厂在用的高阶玩法,全给你讲透 —— 下次面试再被问,你就按这个逻辑答,面试官保准点头!
第一层,最基础但特容易被忽略的 —— 给数据库里的库存字段设成无符号的,就是 unsigned int 那种!(抬手比划 “0”)啥意思?比如库存剩 0 了,再想扣成负数,数据库直接报错,这 SQL 就执行不了。
但你可别觉得这招就万事大吉啊!(语气急一点,像提醒朋友)面试官肯定会追问:“这能防超卖吗?” 你得这么答:“能防最终库存变负,但属于‘事后补救’—— 高并发的时候,100 个请求同时查库存是 1,全通过判断了,最后扣的时候才报错,99 个请求全失败,用户点了下单结果告诉你没库存,谁能乐意啊?”(摊手,模仿用户吐槽)这么说才是加分回答!
第二层,用悲观锁控制并发。核心就是先把数据行锁住,再干活 —— 比如执行 SQL 的时候加个 “for update”,我给你们看个截图啊(代码截图),就是 “select stock from goods where id=1 for update”,这时候其他请求想改这条数据,都得等着排队。
优点是简单,刚入门的小白也能上手;但面试官必问缺点,你得说:“并发能力是真不行!秒杀的时候几百个请求全堵在那儿,要么超时,要么事务堆一堆,中小厂业务量小还能用,大厂高并发场景绝对扛不住。”(摇头,强调 “扛不住”)
第三层,高并发常用的乐观锁。思路就是先不锁数据,等要改的时候再检查 —— 比如给库存表加个 version 字段,查的时候把版本号一起拿出来,更新的时候再判断版本对不对(代码截图),就像 “update goods set stock=stock-1, version=version+1 where id=1 and version=5”。
这儿有个大坑得注意啊!(手指镜头,加重语气)很多人只说加版本号,面试官一补刀:“高并发下重试太多怎么办?” 你得补一句:“给重试加个上限啊,比如最多重试 3 次,还失败就跟用户说‘当前人多,稍等再试’,别让它一直重试,搞成死循环了!”
第四层,用 Redis 队列削峰填谷。比如库存 1000 件,就往 Redis 的 list 里塞 1000 个 “1” 进去,用户下单的时候,先从队列里 “取 1”,取到了再走下单流程,没取到就是没库存了。
我给你们说个真事儿!(语气兴奋点)去年有个粉丝做生鲜秒杀,就用这方案,硬是扛住了 5 万的并发!但缺点也得说清楚:“只能单个商品买一件,没法多件一起买;而且 Redis 和 MySQL 的库存得同步好,不然 Redis 说没库存了,MySQL 里还有,这不就出乱子了嘛。”(皱眉,模仿 “出乱子” 的焦虑)
第五层,用 Lua 脚本来解决 Redis 原子性的问题。因为单独用 Redis 扣库存,查库存和扣库存是两步操作,高并发的时候中间会有空档 —— 用 Lua 脚本把这两步写成一个原子操作,Redis 执行的时候不会被别的命令打断,就没这问题了。
面试官肯定会问:“Lua 脚本为啥能保证原子性?”(模仿面试官提问语气)你就这么说:“Redis 执行脚本的时候,会把其他命令都挡住,相当于单线程在跑,肯定没并发问题。但你得注意,Redis 要是挂了怎么办?得做好持久化,不然数据一丢,那麻烦就大了!”(拍一下手,强调 “麻烦大”)
第六层,大厂面试官特别爱考的 “一锁二判三更新”。流程特别固定(伸出三根手指,逐个数):第一步先加个分布式锁,比如用 Redisson;第二步查库存,判断够不够;第三步扣减库存,最后再释放锁。
这方案能完全防超卖,但缺点也明显:“是串行处理的,并发高的时候会变慢”—— 你得补一句:“适合那种对一致性要求特别高的场景,比如奢侈品秒杀,宁愿慢一点,也绝对不能超卖,不然赔不起啊!”(点头,一脸 “懂行” 的表情)
第七层,最高阶的方案 ——“分布式锁 + 分段缓存”(手指屏幕上的分片示意图,镜头给图)。比如库存 10000 件,分成 10 个分片,每片 1000 件,存在 Redis 的 10 个 key 里,就像 goods:stock:1、goods:stock:2 这样。用户下单的时候,用用户的 userId 取个模,找到对应的分片,再用 Lua 脚本扣分片库存,这一片库存不够了,就接着找下一片。
这方案字节、拼多多都在用!(竖大拇指)优点是并发能力直接翻 10 倍,热点也分散开了;但难点你得说:“跨跨分片扣减的时候,得注意总库存的统计,不然最后剩 100 件分散在某几个分片里,用户点进去一看,匹配到的分片刚好没货,还以为全平台都卖完了,其实别的分片还有库存,这不就亏了嘛!。”
最后总结一句:千万别死记硬背方案,关键是得会 “看场景选方案”—— 中小厂用乐观锁加 Redis 队列,完全够用了;大厂搞高并发,就用 Lua 脚本加分段缓存;要是特别看重一致性,比如卖高价奢侈品,就用 “一锁二判三更新”。(手掌向下压,强调重点)面试官看重的不是你会多少方案,而是你懂在什么时候用什么方案!
今天这 7 层方案,赶紧收藏好,下次面试再被问超卖,你就按这个逻辑答!(手指屏幕下方 “收藏” 按钮)大家还有什么问题?可以评论区跟我聊聊。