这篇是一个场景杂谈,但都和秒杀有点关系: Redis 消息队列,Redis 限流器、秒杀

消息队列

消息队列本质上就是一个具有FIFO性质数据结构而已,所以用Redis 作消息队列是可以的,只要和业务契合,为什么不能考虑呢

Redis FIFO 对象对比

redis 有三种对象可以作消息队列 分别是 list\pub-sub模式\stream 各自特点见下:

  • List
    • 支持持久化
      • 不需要消息确认会包(ACK),不需要群发(消息组)可用
  • PUB/SUB 模式
    • 支持群发(消息组)
      • 不需要ack,不需要持久化可用
  • stream
    • 功能最全,支持消息组、ACK、持久化,一个轻量消息队列可以用,但可靠性不如完整的消息队列中间件.

限流器

限流器主要四种(按实现复杂度排序):

OS : 其实生产常用的就后两种(漏桶、令牌桶)

计数器

单位时间内统计请求次数,超过则拒绝.

  • 实现简单
  • 无法防止流量突刺(0s -- > 1s --- 0.9s - 1000w 请求 -> 1s -同上次1000w请求-> 2s )
  • 无法作流量整形 (削峰)

滑动窗口

有请求进来,触发窗口定时器,定时器时间内统计请求数, 超过拒绝,直到定时器结束,下次有链接继续触发

  • 实现简单,可以防止流量突刺
  • 无法做流量整形(削峰)

漏桶

固定流速,每次定量处理。

  • 强流量整形
  • 不能应对突发流量

令牌桶

定时生产令牌,令牌满了停止生产; 一个请求拿一个令牌,桶中令牌没有就拒绝,直到有令牌继续处理。

  • 一定范围的流量整形
  • 能应对一定范围的突发流量
  • 实现稍复杂

秒杀场景

什么是秒杀

  • 00:00 一到,xxx开售,xxx开枪.某个时间点内请求/订单暴增。

需要注意的问题

海量请求,服务不能崩

  • 崩了比啥都没了,所以需要提前做调研,确定软件技术方案、确定硬件选型,确定用户行为。

不能超卖

  • 超卖可能会的损失在商家,赔本赚吆喝;超卖多了,可能会亏钱亏倒闭;砍单会导致商家信誉受损,平台也跟着有一定的信誉受损.

避免少卖

  • 少买情况比上面乐观些,但尽量避免,让商家利益最大化

防黄牛(保证触发用户,而不是黄牛)

  • 黄牛多了可能就影响一般用户抢购热情,损坏商家声誉等...因为秒杀活动其实就是为了吸引用户增、回馈用户的,货都到黄牛手中,那等于这次活动白做。所以打击黄牛(脚本)是必要的。而且来自脚本的高频请求本身对服务器资源也是一种消耗。
怎么打击黄牛

下面有几个常见策略:

  • 一个ID只能买一件货,一个地址只能买一件货...
  • 对于请求频繁的IP 封禁/限流
  • 验证码...

为了性能具体逻辑需要在Redis-Lua脚本中实现,可大致分为四部:

1.查询库存

2.查询当前用户购买个数(秒杀记录)

3.扣减库存

4.记录用户购买数

怎么高并发

秒杀主要思路是削峰、限流、异步、补偿

  • 异步可以引入消息队列实现,这样就能实现抢和购业务解耦还可以很方便的在消息队列出入口或者在消息队列本身配置上限频,降低后端数据压力。
  • 抢业务可以用Redis + Lua 实现高并发库存加减,因为简单的库存扣减在redis 中是很快的,而直接在后端数据库中操作可能会有些力不从心。这里我们可以将库存均摊配额到每个redis节点中,并将所有预计参与秒杀的用户ID均摊到每个redis数据节点中,这样就可以实现负载均摊。
  • 当Redis 库存扣减成功后,就会过消息队列通知后端数据库业务,生成订单写入后端数据库。
  • 关于流量预估配额,Redis 保守8WQPS是有的,所以我们可以留20%的空置资源作为突发buffer,若突发请求实在过大还可以临时调度多台实例接入nginx做业务分流

拒绝超卖

前面介绍了概念,这里说的是怎么解决超卖

抢购有两个核心步骤:

  • 判断库存是否满足操作
  • 扣减库存信息,扣减成功表示抢购成功

这两部是要保证事务操作是原子性的,所以需要使用Redis + Lua 方案解决.

这时候就可以分三种情况处理:

1.卖完,正常返回信息给用户

2.访问Redis 错误,重试

3.网络延时,或业务超时,这种情况不重试,可以让用户刷新或者耐心等待请购结果比如请购已提交请等待....(正在为您请购...),不要再重新扣库存,省得多扣超卖

避免少卖

前面介绍了概念,这里说的是怎么解决少卖

少卖怎么发生呢?就比如上面避免超卖提到的,

  • 网络延时或者业务超时(redis 作业队列超时,业务定时器过期返回失败)。这时候可能redis 数据已经操作成功了得,但是没有生成订单也就是没有同步到数据库中,这样就会出现少卖.
  • 或者扣减成功了但是投递消息队列失败。

第一种让业务跑完即可

第二种这可能再第一种得基础上发生,写磁盘文件里,后面慢慢式

第三种写磁盘前失败了,这中情况可以考虑MySQL WAL路线,但实现起来很复杂。

---- 少卖是还算能接受得场景,发生概率极小。最多人工介入处理就是了。重点关注超卖。