秒杀商品设计

秒杀商品设计

前端限制

前端控制,不能重复点击

精简sql

典型的一个场景是在进行扣减库存的时候,传统的做法是先查询库存,再去update。 这样的话需要两个sql,而实际上一个sql我们就可以完成的。
可以用这样的做法: update miaosha_goods set stock =stock-1 where goos_id ={#goods_id}
and version = #{version} and sock>0 ;
这样的话,就可以保证库存不会超卖并且一次更新库存,还有注意一点这里使用了版本号的乐观锁,相比较悲观锁,它的性能较好。

同一个用户xx秒内重复请求直接拒绝

具体多少秒需要根据实际业务和秒杀的人数而定,一般限定为10秒。
具体的做法就是通过redis的键过期策略,首先对每个请求都从 String value = redis.get(userId) ;
如果获取到这个 value为空或者为null,表示它是有效的请求,然后放行这个请求。如果不为空表示它是重复性请求,直接丢掉这个请求。
如果有效,采用 redis.setexpire(userId,value,10).value
可以是任意值,一般放业务属性比较好,这个是设置以userId为key,10秒的过期时间(10秒后,key对应的值自动为null)

使用redis限流

限流的实现可以直接使用 GuavaRateLimit 方法,分布式的话,可以缓存在redis

使用redis加载商品,数据库乐观锁更新

  1. 将商品数据加载到redis
  2. 用户进来查看redis中的库存数量num,库存不足直接抛出
  3. 进入数据库,更新数据库库存(需要注意的是,这里要进行乐观锁判断,并且还要校验库存数量num,可以正好一起做乐观锁判断),不匹配也直接抛出
  4. 更新db后,更新redis缓存

这里有一个乐观锁判断,如果一个线程A在走上面的流程,线程B进入,查看到库存,在进入步骤3进行更新数据时,因为缓存中库存与数据库中的库存不一致,导致更新失败。

异步下单

为了提升下单的效率,并且防止下单服务的失败。 需要将下单这一操作进行异步处理。
最常采用的办法是使用队列,队列最显著的三个优点: 异步、削峰、解耦

这里可以采用rabbitmq,在后台经过了限流、库存校验之后,流入到这一步骤的就是有效请求。 然后发送到队列里,队列接受消息,异步下单。
下完单,入库没有问题可以用短信通知用户秒杀成功。 假如失败的话,可以采用补偿机制,重试。

服务降级

假如在秒杀过程中出现了某个服务器宕机,或者服务不可用,应该做好后备工作。 之前的博客里有介绍通过Hystrix进行服务熔断和降级,可以开发一个备用服务。
假如服务器真的宕机了,直接给用户一个友好的提示返回,而不是直接卡死,服务器错误等生硬的反馈。

参考:

秒杀

面试官问我:如何设计一个秒杀场景?

Redis队列实现Java版秒杀系统(无脚本、可用于生产)

Java上亿级别秒杀系统优化 ,实现真正的高性能高并发!

posted @ 2023-01-15 21:12  hongdada  阅读(43)  评论(0编辑  收藏  举报