简单设计秒杀系统

1 秒杀架构图

在这里插入图片描述

2 秒杀场景

  1. 登陆12306进行火车票抢座
  2. 1599元购入飞天茅台
  3. 周董演唱会的门票
  4. 双十一秒杀活动

3 秒杀的难点

  • 瞬时高并发流量的挑战
  • 超卖
  • 恶意攻击
  • 用户体验
  • 木桶短板理论,整个系统的瓶颈往往都在 DB

4 如何设计

在这里插入图片描述

上图是一个典型的互联网业务,用户完成一个写操作,一般会通过接入层和逻辑层,这里的服务都是无状态,可以通过平行拓展去解决高并发的问题;到了 db 层,必须要落到介质中,可以是磁盘/ssd/内存,如果出现 key 的冲突,会有一些并发控制技术,例如 cas/加锁/串行排队等。

4.1 直筒型

直筒型业务,指的是用户请求 1:1 的洞穿到 db 层,如下图所示。在比较简单的业务中,才会采用这个模型。随着业务规模复杂度上来,一定会有 db 和逻辑层分离、逻辑层和接入层分离。
在这里插入图片描述

**

4.2 漏斗型

漏斗型业务,指的是,用户的请求,从客户端到 db 层,层层递减,递减的程度视业务而定。例如当 10w 人去抢 1 个物品时,db 层的请求在个位数量级,这就是比较理想的模型。如下图所示
在这里插入图片描述

这个模型,是高并发的基础,翻译一下就是下面这些:

  • 及早发现,及早拒绝
  • Fast Fail
  • 前端保护后端,避免打到数据库

5 具体措施

5.1 前端

资源静态化

前端把能提前放入cdn服务器的东西都放进去,反正把所有能提升效率的步骤都做一下,减少真正秒杀时候服务器的压力。

秒杀链接加盐

实现思路:首先获取秒杀下单url可变参数,根据skuId进行md5加密。校验是否已经到秒杀时间,防止秒杀还没开始就要有人通过脚本刷单。

//用于加密和解密的密钥
private final String cipher="hosjoy-spike-md5-cipher&73@(**$d--=,./;~·2··%##4";

//返回url可变部分
public String  getPathVirableMd5(long skuId) {
    if(LocalDateTime.now().before(spikeStartTime) ){
        throw new ClientException("还没到秒杀时间")
    }
    String base=skuId+"/"+cipher;
    return DigestUtils.md5DigestAsHex(base.getBytes());
}

下单请求

@RequestMapping(value = "/submit/{skuId}/{md5}")
public void  submit(@PathVariable("skuId") Long skuId, @PathVariable("md5") String md5){
    return spikeService.submitSpikeOrder(skuId, md5);  
}

public void submitSpikeOrder(long skuId,  String md5){
      if(md5==null||!md5.equals(getPathVirableMd5(skuId))){
         //md5校验错误
         throw new RuntimeException("非法请求");
      }
      //校验通过,执行下单
      this. doSubmitSpikeOrder(skuId);
    }
}

限流

前端按钮灰色化:如果参与过秒杀活动会发现,没到秒杀时间时秒杀按钮是灰色状态的,只有时间到了才是可点击状态。并且秒杀开始咯也不是一直可以点的,可能只允许1秒内点10次那种的。

5.2 Nginx

Nginx: 高性能的web服务器,并发也随便顶几万不是梦,但是我们的Tomcat只能顶几百的并发呀,那简单呀负载均衡嘛,一台服务几百,那就多搞点,在秒杀的时候多租点流量机
在这里插入图片描述

恶意请求拦截也需要用到它,一般单个用户请求次数太夸张,不像人为的请求在网关那一层就得拦截掉了,不然请求多了他抢不抢得到是一回事,服务器压力上去了,可能占用网络带宽或者把服务器打崩、缓存击穿等等。

5.3 风控

我可以明确的告诉大家,前面的所有措施还是拦不住很多羊毛党,因为他们是专业的团队,他们可以注册很多账号来薅你的羊毛,而且不用机器请求,就用群控,操作几乎跟真实用户一模一样。
那怎么办,是不是无解了?
这个时候就需要风控同学的介入了,在请求到达后端之前,风控可以根据账号行为分析出这个账号机器人的概率大不大。

5.4 后端

服务单一职责

设计个能抗住高并发的系统,我觉得还是得单一职责
什么意思呢,我们给秒杀开个服务,我们把秒杀的代码业务逻辑放一起。
单一职责的好处就是就算秒杀没抗住,秒杀库崩了,服务挂了,也不会影响到其他的服务。(高可用)

Redis集群

如果说单机的Redis顶不住,那就做一个redis集群,秒杀本来就是读多写少,那你们是不是瞬间想起来我之前跟你们提到过的,Redis集群主从同步读写分离,我们还搞点哨兵,开启持久化直接无敌高可用!
在这里插入图片描述

库存预热

秒杀的本质,就是对库存的抢夺,每个秒杀的用户来你都去数据库查询库存校验库存,然后扣减库存,数据库顶不住啊。所以要开始秒杀前你提前把商品的库存加载到Redis中去,让整个流程都在Redis里面去做,然后等秒杀介绍了,再异步的去修改库存就好了。
但是用了Redis就有一个问题了,我们上面说了我们采用主从,就是我们会去读取库存然后再判断然后有库存才去减库存,正常情况没问题,但是高并发的情况问题就很大了。就比如现在库存只剩下1个了,我们高并发嘛,4个服务器一起查询了发现都是还有1个,那大家都觉得是自己抢到了,就都去扣库存,那结果就变成了-3,是的只有一个是真的抢到了,别的都是超卖的。咋办?

超卖问题

redis的 watch + multi
我们可以使用使用redis的 watch + multi 指令,去监听秒杀商品库存,如果库存数发生改变,则后续无法进行修改库存操作。
缺点:
(1)由于watch采用乐观锁机制,没有对其它线程修改操作作限制,因此事务有可能频繁失败;需要用while循环去重复尝试;
(2)增加服务器压力
分布式锁
利用分布式锁,保证同一时刻只有一个线程进行读库存—修改库存操作。
缺点:同一个商品多用户同时下单的时候,会基于分布式锁串行化处理,导致没法同时处理同一个商品的大量下单的请求,并发处理能力较弱。
redis 队列
将库存缓存到redis队列,队列里面放sku_id,例如库存为5个,就放5个id。通过rightPop操作取出商品,预扣减库存,如果pop出来的元素为空,说明售罄 。这里利用了redis单线程操作特性,队列取id即扣减库存,相当于原子操作,高并发场景下不需要开事务,也不用加锁同步,性能、数据一致性均好于以上两种方案。
乐观锁
利用CAS原理,在操作数据库更新库存的时候,更新条件带上之前查询到的库存数量,如果更新结果数为0,说明过程中其它线程修改了库存。

select inventory from sku; update sku set inventory =#{inventory} where id=?  and inventory= ?
限流&降级&熔断&隔离

这个为啥要做呢,不怕一万就怕万一,万一你真的顶不住了,限流,顶不住就挡一部分出去但是不能说不行,降级,降级了还是被打挂了,熔断,至少不要影响别的系统,隔离,你本身就独立的,但是你会调用其他的系统嘛,你快不行了你别打挂别的系统那啊。

消息队列(削峰填谷)

你卖的东西少,你直接100个请求改库我觉得没问题,但是万一秒杀一万个,10万个呢?服务器挂了,秒杀就是这种瞬间流量很高,但是平时又没有流量的场景,那消息队列完全契合这样的场景了呀,削峰填谷。

数据库

数据库用MySQL只要连接池设置合理一般问题是不大的,不过最好单独给秒杀建立一个数据库,为秒杀服务,表的设计也是竟可能的简单点。

少卖问题

少卖问题原因分析
(1)redis预扣减库存成功,但是执行真正的下单逻辑失败了,且库存没有回滚;
(2)用户订单提交成功了,但是超时没有支付,且超时后活动已结束或者超时后没有回滚库存;
(3)用户排队成功了,但是排队下单请求消息发送到MQ失败了,或者MQ消息丢了,或者消费者弄丢了数据。
解决方案
(1)异步下单失败后,要即时回滚redis中的sku库存;
(2)缩短支付时间,或者修改秒杀流程:先支付再确认订单;超时未支付后即时回滚redis中的sku库存;
(3)解决MQ消息丢失问题(下文会提到)。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值