Skip to content

面试速答(先看这里)

**一句话结论:**这是一个典型的秒杀问题。

60秒标准回答:

这是一个典型的秒杀问题。几千万个并发抢100个库存,如何保证不超卖、不少卖,如何保证服务不被打垮、如何保证前面的人能抢到,这都是秒杀系统需要考虑的重点问题

超卖:一共100件,卖出去101件

少卖:一共100件,只卖出去99件,另外的那一件无法购买了

**答题顺序:**结论 → 原理/机制 → 关键流程 → 场景与取舍 → 易错点

回答主线:

  • **要点1:**这里重点说一下如何保证前面的人能抢到。
  • **要点2:**这个其实就是我们多次提到过的, 要用Redis来做扣减,而不是直接用加分布式锁和限流来实现秒杀的主要原因。
  • **要点3:**因为如果使用限流,那么没办法保证被限制住的流量是先来的还是后来的,因为有可能非常极限的他们差个零点几毫秒,但是限流其实是按照秒级做统计和限制的,所以一秒内的请求一起过来的时候,谁能被放过,谁能被阻塞,就靠运气了。
  • **要点4:**分布式锁也是一样的,需要靠抢锁来获取库存扣减的资格,但是并不能保证先来的一定能抢到锁,因为有可能当前有人在持有这个锁,而后来的线程可能来的时候刚好没锁,就很快就拿到了。
  • **要点5:**而如果借助Redis的lua脚本或者事务进行库存扣减,那么Redis可以保证命令执行的先后顺序,按照发送顺序进行执行,先来的请求就能先进行库存扣减了。

**记忆锚点:**Redis → 是直接用加分布式锁 → 先来的一定能抢到锁 → 几千万个并发 → 而后来的线程 → 脚本或者事务

关键取舍:

  • 因为如果使用限流,那么没办法保证被限制住的流量是先来的还是后来的,因为有可能非常极限的他们差个零点几毫秒,但是限流其实是按照秒级做统计和限制的,所以一秒内的请求一起过来的时候,谁能被放过,谁能被阻塞,就靠运气了。
  • 如果做的再好一点,在redis中维护一个优惠券是否还有库存的表示,先判断是否还有,然后再扣减,或者这个标识也可以维护到本地缓存,可以进一步提升性能,减少应用被打垮的概率。

易错提醒:

  • 分布式锁也是一样的,需要靠抢锁来获取库存扣减的资格,但是并不能保证先来的一定能抢到锁,因为有可能当前有人在持有这个锁,而后来的线程可能来的时候刚好没锁,就很快就拿到了。
  • (操作数据库) 当库存扣减为0之后,后续的请求再进来,会在Redis中构建失败,因为库存已经没有了,无法扣减了,这样就可以把后续的所有流量都抗在数据库之前了。

加分表达:

  • 而如果借助Redis的lua脚本或者事务进行库存扣减,那么Redis可以保证命令执行的先后顺序,按照发送顺序进行执行,先来的请求就能先进行库存扣减了。

追问准备:

  • 围绕「Redis」:底层原理是什么?使用时有哪些边界和常见坑?
  • 围绕「是直接用加分布式锁」:底层原理是什么?使用时有哪些边界和常见坑?
  • 围绕「先来的一定能抢到锁」:底层原理是什么?使用时有哪些边界和常见坑?
  • 如果线上出现异常,你会如何定位、验证并规避?

典型回答 ​

这是一个典型的秒杀问题。几千万个并发抢100个库存,如何保证不超卖、不少卖,如何保证服务不被打垮、如何保证前面的人能抢到,这都是秒杀系统需要考虑的重点问题。

> 超卖:一共100件,卖出去101件 > > 少卖:一共100件,只卖出去99件,另外的那一件无法购买了

关于优惠券的库存的超卖和少卖的问题,可以看一下其他的文档,内容都是一样的方案。

📄 ✅库存扣减如何避免超卖和少卖?

打开文档:✅库存扣减如何避免超卖和少卖?

📄 ✅账户里面只有十块钱,同时发来两笔订单一共大于十块钱,怎么保证不超花?

打开文档:✅账户里面只有十块钱,同时发来两笔订单一共大于十块钱,怎么保证不超花?

这里重点说一下如何保证前面的人能抢到。

这个其实就是我们多次提到过的,要用Redis来做扣减,而不是直接用加分布式锁和限流来实现秒杀的主要原因。

因为如果使用限流,那么没办法保证被限制住的流量是先来的还是后来的,因为有可能非常极限的他们差个零点几毫秒,但是限流其实是按照秒级做统计和限制的,所以一秒内的请求一起过来的时候,谁能被放过,谁能被阻塞,就靠运气了。

分布式锁也是一样的,需要靠抢锁来获取库存扣减的资格,但是并不能保证先来的一定能抢到锁,因为有可能当前有人在持有这个锁,而后来的线程可能来的时候刚好没锁,就很快就拿到了。

而如果借助Redis的lua脚本或者事务进行库存扣减,那么Redis可以保证命令执行的先后顺序,按照发送顺序进行执行,先来的请求就能先进行库存扣减了。扣减成功则说明有库存,那么就可以给他发放优惠券了。(操作数据库)

当库存扣减为0之后,后续的请求再进来,会在Redis中构建失败,因为库存已经没有了,无法扣减了,这样就可以把后续的所有流量都抗在数据库之前了。

如果做的再好一点,在redis中维护一个优惠券是否还有库存的表示,先判断是否还有,然后再扣减,或者这个标识也可以维护到本地缓存,可以进一步提升性能,减少应用被打垮的概率。(这个本地缓存的方案在我的项目课中有用到。感兴趣的可以了解下、)