秒杀商品设计
前端限制
前端控制,不能重复点击
精简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限流
限流的实现可以直接使用 Guava
的 RateLimit
方法,分布式的话,可以缓存在redis
中
使用redis加载商品,数据库乐观锁更新
- 将商品数据加载到
redis
- 用户进来查看
redis
中的库存数量num
,库存不足直接抛出 - 进入数据库,更新数据库库存(需要注意的是,这里要进行乐观锁判断,并且还要校验库存数量
num
,可以正好一起做乐观锁判断),不匹配也直接抛出 - 更新
db
后,更新redis
缓存
这里有一个乐观锁判断,如果一个线程
A
在走上面的流程,线程B
进入,查看到库存,在进入步骤3进行更新数据时,因为缓存中库存与数据库中的库存不一致,导致更新失败。
异步下单
为了提升下单的效率,并且防止下单服务的失败。 需要将下单这一操作进行异步处理。
最常采用的办法是使用队列,队列最显著的三个优点: 异步、削峰、解耦 。
这里可以采用rabbitmq,在后台经过了限流、库存校验之后,流入到这一步骤的就是有效请求。 然后发送到队列里,队列接受消息,异步下单。
下完单,入库没有问题可以用短信通知用户秒杀成功。 假如失败的话,可以采用补偿机制,重试。
服务降级
假如在秒杀过程中出现了某个服务器宕机,或者服务不可用,应该做好后备工作。 之前的博客里有介绍通过Hystrix进行服务熔断和降级,可以开发一个备用服务。
假如服务器真的宕机了,直接给用户一个友好的提示返回,而不是直接卡死,服务器错误等生硬的反馈。