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

用Redis秒杀怎么瞬间搞定,性能瓶颈和技巧分享

说到用Redis搞定秒杀,核心思想就一句话:尽量把请求拦在数据库之外,用内存操作搞定大部分事情。 数据库(比如MySQL)是秒杀系统最大的瓶颈,因为它要写磁盘,并发一高就撑不住了,而Redis所有数据都在内存里,速度极快,天生适合这种高并发读写的场景。

性能瓶颈在哪里?

别看Redis快,用不好照样崩,瓶颈主要在这几个地方:

  1. 网络IO和命令操作:一瞬间几万甚至几十万个请求涌进来,Redis要处理这么多网络连接和执行命令,CPU和网络带宽可能先顶不住,这就像一条高速公路,车一下子全堵在收费站口,收费站再快也会瘫痪。(参考阿里云开发者社区关于秒杀场景的分析)
  2. 库存超卖:这是最经典的问题,比如只剩1件商品,但多个请求同时来查,都看到“还有1件”,然后都执行了扣减,结果就卖出了10件,这就是超卖,问题的根源在于“查询”和“扣减”这两个操作不是原子的,在并发下被打断了。
  3. Redis本身成为单点瓶颈:即使你的业务逻辑没问题,但所有请求都打向一个Redis实例,这个实例的物理性能是有上限的,它可能成为整个系统的单点瓶颈。

核心技巧:如何用Redis“瞬间”搞定?

用Redis秒杀怎么瞬间搞定,性能瓶颈和技巧分享

针对上面的瓶颈,有以下几个非常实用的技巧:

库存预热,数据提前放到Redis里

秒杀开始前,就把要卖的商品id和库存数量提前设置到Redis中,比如用简单的String类型:set seckill_good_1001_stock 100,这样秒杀开始时,所有请求都直接访问Redis,完全不用查数据库,减轻了数据库的压力。

使用原子操作,杜绝超卖

用Redis秒杀怎么瞬间搞定,性能瓶颈和技巧分享

这是最关键的一步,绝对不能先get库存,判断大于0再decr(减1),因为这两个命令不是一起执行的,必须用Redis的原子命令。

  • 最佳选择:使用DECR命令。 直接 DECR seckill_good_1001_stock,这个命令的作用是将键的值减1,并返回减后的值,它本身是原子性的,多个客户端同时执行也不会出错。

    • 判断逻辑是: 执行DECR后,看返回值。
    • 如果返回值大于等于0,说明扣减成功,用户抢购成功。
    • 如果返回值小于0(1),说明库存已经扣到零了,这次抢购失败。
    • 这里有个小技巧: 发现返回值小于0后,可以再执行一次 INCR命令把库存加回来,避免库存被扣成负数,虽然用户抢购失败,但保持库存为0,后续请求会更快失败。
  • 备选方案:使用Lua脚本。 如果逻辑更复杂,比如你还需要判断用户是否重复购买,可以把多个命令写在一个Lua脚本里,因为Lua脚本在Redis中是串行执行的,也能保证原子性,但通常简单的扣库存,DECR就足够了,性能更高。

动静分离,限流降级

用Redis秒杀怎么瞬间搞定,性能瓶颈和技巧分享

不是所有请求都需要走到扣库存那一步。

  • 动静分离: 像商品图片、详情介绍这些不会变的信息,可以提前放到CDN或者专门的静态服务器上,不要让它占用秒杀时的网络带宽和Redis资源。
  • 限流和队列削峰: 这是保护Redis的法宝,瞬间的请求太多了,可以用一些办法把峰值“削平”。
    • 前端限流: 在用户点击“抢购”按钮后,立刻让按钮变灰,禁止用户重复提交,这能拦掉很多无效请求。
    • 网关层限流: 在请求到达业务服务器之前,用Nginx等网关设置限流规则,比如每秒只放行1万个请求进入后端,多余的直接返回“抢购人数过多,请稍后重试”,这就像在高速公路入口实行管制,只放行一部分车流。
    • 消息队列: 更常见的做法是,业务服务器接收到请求后,先进行一些基础验证(比如用户登录态),然后立刻将抢购请求发送到像RabbitMQ、Kafka这样的消息队列里,并立即给用户返回“正在排队中”的提示,后端再从一个匀速的队列里慢慢消费这些请求,去Redis里扣库存,这样就把一瞬间的洪峰压力,变成了一个匀速的流水线作业,Redis的压力就变得可控了。(参考众多互联网大厂的秒杀方案总结)

Redis集群和高可用

单机Redis能力有限,要做好高可用和扩容。

  • 使用集群模式: 将不同的商品库存数据分布到Redis集群的不同节点上,这样压力也被分散了,比如商品1001的库存放在A节点,商品1002的库存放在B节点。
  • 读写分离: 如果读请求(比如检查库存状态)也很多,可以配置主从复制,让从节点来分担读压力。

一个简化的流程总结

  1. 预热: SET stock_1001 100
  2. 用户请求到达: 网关和前端先拦掉一部分。
  3. 扣减库存: 业务服务器执行 DECR stock_1001
  4. 判断结果:
    • 返回值 >= 0:成功,然后业务服务器可以异步地向数据库写入订单信息,并通知用户付款。
    • 返回值 < 0:失败,直接返回用户“已售罄”。
  5. 后续: 用户付款后,再完成整个订单流程。

用Redis做秒杀,它的核心职责就是准确、快速地扣减库存,只要把这一个点做扎实了,再把流量控制好,秒杀系统的核心难题就解决了大半。