秒杀
1.限流
固定窗口算法:工夫节点无奈动静更新,即 set key expire 10s(1-11秒,2-12秒)解决不好
滑动窗口算法: zset member id score 10 解决工夫节点问题 score即工夫戳,能够动静获取(currentTime - score)工夫内的总访问量,借此判断是否达标
漏桶算法:redis中如同有相似实现
令牌桶算法:Guava RateLimiter
2.降级(也叫做熔断)
spring cloud sentinel
3.缓存
3.1缓存一致性问题
A. 先更新Db,再delete redis
两个问题:
a .更新Db胜利,删除缓存失败。导致redis中有脏数据(能够借助rocketmq一直重试解决)
b.(1)A线程查问Db数据为cache1
(2)B线程更新Db,数据为ca che2
(3)B线程删除缓存
(4)A线程将ca che1同步到redis缓存中。脏数据产生了(依据耗时状况能够推算出概率比拟低)
B 先删除缓存,再更新Db
(1)A线程写操作data2,删除缓存
(2)b线程查问发现缓存不存在
(3)b线程同步以后d b最新数据da ta1到redis
(4)a线程将数据写入DB .脏数据产生
3.2 解决方案
A.延时双删
先删除缓存,再更新DB,再异步删除缓存(通过rocketmq异步重试机制,确保删除胜利)
B.订阅binLog机制
阿里开源的canal
3.3利用 秒杀实现
限流:zset滑动窗口限流
降级:spring cloud sentinel+open Feign熔断
缓存:
延时双删策略
1.库存扣减问题
提交订单时
付款时
提交订单时事后扣减库存,超时回复:定时工作轮训数据库没有付款的订单,超时订单偿还库存
2.热点数据缓存
延时双删策略
3.库存避免超卖问题
乐观锁机制 update
update stock sale = sale + 1, version = version + 1, WHERE id = #{id,jdbcType=INTEGER} AND version = #{version,jdbcType=INTEGER}或者 update stock sale = sale + 1 WHERE id = id AND sale={#sale}
乐观锁机制
在Service层给更新表增加一个事务,这样每个线程更新申请的时候都会先去锁表的这一行(乐观锁),更新完库存后再开释锁。可这样就太慢了,1000个线程可等不及。
beginTranse(开启事务)try{ //quantity为申请减掉的库存数量 $dbca->query('update s_store set amount = amount - quantity where amount>=quantity and postID = 12345');}catch($e Exception){ rollBack(回滚)}commit(提交事务)
具体计划
- 在零碎初始化时,将商品的库存数量加载到Redis缓存中;
- 接管到秒杀申请时,在Redis中进行预减库存,当Redis中的库存有余时(库存<0能够在内存中设置一个标记量boolean=曾经卖光,就不须要再去申请redis减少网络开销了,分布式状况下通过zookeeper取得相应告诉),间接返回秒杀失败,否则持续进行第3步;
- 将申请放入异步队列中,返回正在排队中;
- 服务端异步队列将申请出队,出队胜利的申请能够生成秒杀订单,缩小数据库库存(失败就会回滚,并且会复原redis中的库存数据即加1),返回秒杀订单详情。
- 当后盾订单创立胜利之后能够通过websocket向用户发送一个秒杀胜利告诉。前端以此来判断是否秒杀胜利,秒杀胜利则进入秒杀订单详情,否则秒杀失败。