秒杀活动总结(秒杀之一)

(1)对于秒杀活动的过程中,系统一般会先查一下库存是否足够,如果足够才允许下订单,写数据库。如果不足够的话,则直接返回该商品已经抢购完成。属于读多写少的情景。

出现的问题:如十万 的请求过来,同时通过数据库查缓存是否足够,此时数据库可能会挂掉。因为数据库的连接资源非常有限,比如:mysql,无法同时支持这么多的连接。此时建议使用redis

(2)通常情况下,我们在redis中保存商品信息,里面包含:商品id、商品名称、规格属性、库存等信息,同时数据库中也要有相关信息,毕竟缓存并不完全可靠(问题:这种情况下会存在缓存击穿,在高并发下,同一时刻会有大量的请求,都在秒杀同一件商品,这些请求同时去查缓存中没有数据,然后又同时访问数据库。结果悲剧了,数据库可能扛不住压力,直接挂,这个这就需要加锁,最好使用分布式锁)

(3)项目启动之前,先把缓存进行预热,即事先把所有的商品,同步到缓存中,这样商品基本都能直接从缓存中获取到,就不会出现缓存击穿的问题了,但如果缓存中设置的过期时间不对,缓存提前过期了,或者缓存被不小心删除了,如果不加速同样可能出现缓存击穿。

(4)防止缓存穿透的时候,可以加分布式锁来实现,但是请求的处理性能并不好,这个时候可以用布隆过滤器,系统根据商品id,先从布隆过滤器中查询该id是否存在,如果存在则允许从缓存中查询数据,如果不存在,则直接返回失败。(但是布隆过滤器中的数据无法做到和更缓存中的数据保持一致,所以这个方法也不是太可取)

(5)对于库存问题,采用预扣库存,真正的秒杀商品的场景,不是说扣完库存,就完事了,如果用户在一段时间内,还没完成支付,扣减的库存是要加回去的。

(6)减库存

 数据库扣减方式一:update product set stock=stock-1 where id=product and stock > 0;

在sql最后加上:stock > 0,就能保证不会出现超卖的情况,但需要频繁访问数据库,在高并发的场景下,可能会造成系统雪崩。而且,容易出现多个请求,同时竞争行锁的情况,造成相互等待,从而出现死锁的问题。

  redis扣减库存方式二:

redis的incr方法是原子性的,可以用该方法扣减库存。伪代码如下:

 boolean exist = redisClient.query(productId,userId);

  if(exist) {

    return -1;

  }

  int stock = redisClient.queryStock(productId);

  if(stock <=0) {

    return 0;

  }

  redisClient.incrby(productId, -1);

  redisClient.add(productId,userId);

return 1;

代码流程如下:

 1.先判断该用户有没有秒杀过该商品,如果已经秒杀过,则直接返回-1。

 2.查询库存,如果库存小于等于0,则直接返回0,表示库存不足。

 3.如果库存充足,则扣减库存,然后将本次秒杀记录保存起来。然后返回1,表示成功。

方式三:加个synchronized解决

加synchronized确实能解决库存为负数问题,但是这样会导致接口性能急剧下降,每次查询都需要竞争同一把锁,显然不太合理

(7)减库存的时候可以用lua脚本扣减库存

我们都知道lua脚本,是能够保证原子性的,它跟redis一起配合使用,能够完美解决上面的问题。

(8)在秒杀场景下,每1万个请求,有1个成功。再1万个请求,有1个成功。如此下去,直到库存不足。这就变成均匀分布的秒杀了,跟我们想象中的不一样。可以使用自旋锁。在规定的时间,比如500毫秒内,自旋不断尝试加锁,如果成功则直接返回。如果失败,则休眠50毫秒,再发起新一轮的尝试。如果到了超时时间,还未加锁成功,则直接返回失败。使用redis分布式锁,还有锁竞争问题、续期问题、锁重入问题、多个redis实例加锁问题。可以使用redisson解决

(9)mq异步处理

(秒杀--》下单--〉支付)

而这三个核心流程中,真正并发量大的是秒杀功能,下单和支付功能实际并发量很小。所以,我们在设计秒杀系统时,有必要把下单和支付功能从秒杀的主流程中拆分出来,特别是下单功能要做成mq异步处理的。而支付功能,比如支付宝支付,是业务场景本身保证的异步。因此秒杀下单的流程就变为:秒杀--(发送mq消息)--------------》mq服务端-----(消费mq消息)------〉下单

(10)防止消息丢失的一个处理

秒杀成功了,往mq发送下单消息的时候,有可能会失败。原因有很多,比如:网络问题、broker挂了、mq服务端磁盘问题等。这些情况,都可能会造成消息丢失。

在生产者发送mq消息之前,先把该条消息写入消息发送表,初始状态是待处理,然后再发送mq消息。消费者消费消息时,处理完业务逻辑之后,再回调生产者的一个接口,修改消息状态为已处理。如果生产者把消息写入消息发送表之后,再发送mq消息到mq服务端的过程中失败了,造成了消息丢失。这个时候需要使用job,增加重试机制。用job每隔一段时间去查询消息发送表中状态为待处理的数据,然后重新发送mq消息。

(11)重复消费的问题(加一张消息处理表)

消费者读到消息之后,先判断一下消息处理表,是否存在该消息,如果存在,表示是重复消费,则直接返回。如果不存在,则进行下单操作,接着将该消息写入消息处理表中,再返回。

(12)垃圾消息的处理问题

如果出现了消息消费失败的情况。比如:由于某些原因,消息消费者下单一直失败,一直不能回调状态变更接口,这样job会不停的重试发消息。最后,会产生大量的垃圾消息。每次在job重试时,需要先判断一下消息发送表中该消息的发送次数是否达到最大限制,如果达到了,则直接返回。如果没有达到,则将次数加1,然后发送消息。

(13)延迟消费问题

通常情况下,如果用户秒杀成功了,下单之后,在15分钟之内还未完成支付的话,该订单会被自动取消,回退库存。我们都知道rocketmq,自带了延迟队列的功能。下单时消息生产者会先生成订单,此时状态为待支付,然后会向延迟队列中发一条消息。达到了延迟时间,消息消费者读取消息之后,会查询该订单的状态是否为待支付。如果是待支付状态,则会更新订单状态为取消状态。如果不是待支付状态,说明该订单已经支付过了,则直接返回。

(14)限流的处理方式

1.同一用户限流:同一用户id请求次数是否超过阈值(比如:每分钟请求只能请求5次)

2.验证码进行移动滑块进行限流。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值