当前位置:首页 > 问答 > 正文

Redis秒杀其实没那么复杂,少做多快才是关键,流程也能简单点

(引用来源:沈剑架构师关于秒杀系统的经典文章及演讲核心思想)

“Redis秒杀其实没那么复杂,少做多快才是关键,流程也能简单点”这个说法,其实点出了很多人在设计高并发系统时的一个误区:总想着用非常复杂、面面俱到的方案去应对一个极端场景,结果往往是把系统搞得无比臃肿,反而更容易出问题,秒杀的核心问题就一个:在极短时间内,有海量请求涌来,要抢购数量极少的商品,解决这个问题的钥匙,不在于你的系统功能有多全,而在于你的核心路径有多快、多坚固。

怎么理解“少做”和“快”呢?

Redis秒杀其实没那么复杂,少做多快才是关键,流程也能简单点

第一,“少做”是指减少不必要的操作。 在秒杀这个场景里,用户最核心的动作就是“扣减库存”,系统最应该保障的就是这个动作的原子性和高性能,其他很多动作,都可以往后放,或者用更轻量的方式去做。

用户点击秒杀按钮前的各种校验,你不能等到真正扣库存的时候才去校验用户是否登录、是否有资格参与,那肯定来不及,但你可以把这些校验提前,常见的做法是,在用户进入秒杀页面时,就用一个简单的Token(可以理解为一个一次性的门票)机制,系统快速校验一下用户的基本资格,然后发一个Token给前端,用户真正发起秒杀请求时,只需要带上这个Token和商品ID,服务端收到请求,先验证Token是否有效,无效的直接拒绝,这个验证过程非常快,因为它不涉及查询数据库,可能只是在Redis里查一个键是否存在,这就过滤掉了大量无效请求(比如没登录的、没抢到门票的),让后续流程更轻松。

Redis秒杀其实没那么复杂,少做多快才是关键,流程也能简单点

再比如,秒杀成功后,生成订单这个操作,如果秒杀请求一过来,你就要同时完成扣库存、写订单表、更新用户信息等一系列数据库操作,那数据库压力会非常大,很容易就崩溃了,更聪明的“少做”是:异步化,当Redis成功扣减库存后,立刻给用户返回“秒杀成功”,然后把“生成完整订单”这个稍微慢一点的任务,放到一个消息队列里,由后端的服务慢慢去消费、处理,这样,最关键的秒杀路径就变得极短:校验Token -> Redis原子扣减库存 -> 返回结果,用户瞬间就能知道结果,体验非常好,而系统的压力也降下来了。

第二,“快”的核心是依赖更快的组件,并且让操作尽可能简单。 这就是Redis大显身手的地方。

Redis秒杀其实没那么复杂,少做多快才是关键,流程也能简单点

为什么是Redis?因为它快啊!它的数据都在内存里,操作速度是微秒级别的,远比读写硬盘的数据库要快几个数量级,秒杀就是一场“速度战争”,用最快的武器是理所当然的。

但光用Redis还不够,关键是怎么用,这里就要用到Redis的原子操作,比如DECR命令(递减)或者Lua脚本,库存数量可以预先加载到Redis中,当秒杀请求过来时,不需要像在数据库里那样,先查询一下库存是否大于0,然后再做更新,那样的话,在查询和更新的间隙,很可能库存已经被别人抢走了,就会导致超卖,Redis的DECR命令是原子的,意思就是这个“检查并扣减”的动作是不可分割的,一瞬间完成,它返回一个值,如果返回值大于等于0,说明扣减成功,抢到了;如果小于0,说明库存不足,没抢到,就这么简单一个操作,就完美解决了库存扣减和防超卖这两个最核心的并发难题。

你看,整个流程可以简化成这样:

  1. 前置校验(网关/前端):用户资格初步检查,发放Token。
  2. 核心秒杀(极简服务)
    • 接收请求,验证Token。
    • 调用Redis原子操作(如DECR)扣减库存。
    • 根据扣减结果,立即返回“成功”或“失败”。
  3. 后续处理(异步任务)
    • 对于“成功”的请求,将信息发送到消息队列。
    • 订单服务从队列取出消息,异步生成完整订单,通知用户。

这个流程里,第二步是绝对不能卡的,所以它做的事情非常少,而且用的都是速度最快的组件(Redis),第一步和第三步即使稍微慢一点,也不会影响用户秒杀那一刻的体验。

之所以说Redis秒杀没那么复杂,就是因为它抓住了主要矛盾,我们不需要一个能处理所有业务逻辑的“万能”秒杀系统,而是要打造一个在核心路径上“专”而“快”的尖刀系统,通过“少做”——把非核心操作前置或异步化,和“快做”——利用Redis等高速存储的原子操作,我们完全可以用一个相对简单、清晰的架构,稳稳地扛住秒杀带来的瞬时海量流量,把事情做简单,往往比做复杂需要更多的思考和智慧。