当前位置: 首页 > 后端技术 > Java

秒杀常见问题

时间:2023-04-01 19:40:31 Java

秒杀1.限流固定窗口算法:时间节点不能动态更新,即设置keyexpire10s(1-11秒,2-12秒)没有处理好。滑动窗口算法:zsetmemberidscore10解决问题时间节点score是时间戳,可以动态获取时间段内的总访问量(currentTime-score),从而判断是否达标漏桶算法:在redis中好像有一个类似的实现令牌桶算法:GuavaRateLimiter2。降级(又称熔断)springcloudsentinel3.缓存3.1缓存一致性问题A.先更新Db,再删除redis两个问题:a.成功更新数据库,但删除缓存失败。redis中有脏数据(可以用rocketmq不断重试解决)b.1)缓存刚失效(2)请求A查询数据库得到一个旧值(3)请求B将新值写入数据库(4)请求B删除缓存(5)请求A将查到的旧值写入缓存(根据耗时情况,可以估计概率比较低)B先删除缓存,再更新db(1)线程A删除缓存,写入db并更新到data2(2)b线程查询发现缓存不存在(3)b线程将当前DB的最新数据data1同步到redis删除先删除缓存,再更新DB,再异步删除缓存(通过rocketmq异步重试机制保证删除成功)B.订阅binLog机制阿里开源canal3.3应用秒杀实现限流:zset滑动窗口限流降级:springcloudsentinel+openFeignfuse缓存:延迟双删策略1.提交订单时扣库存问题提交订单付款时,提交订单时预扣库存,超时回复:timed任务轮训数据库无支付订单,超时订单返回库存2.热点数据缓存延时双删策略3.防止超卖库存的乐观锁机制updateupdatestocksale=sale+1,version=version+1,WHEREid=#{id,jdbcType=INTEGER}ANDversion=#{version,jdbcType=INTEGER}orupdatestocksale=sale+1WHEREid=idANDsale={#sale}悲观锁机制在更新表中添加一个事务Service层,让每个线程在更新request时都会先锁住表这一行(悲观锁),更新完库存后释放锁。但是这样也太慢了,1000个线程都等不及了。beginTranse(opentransaction)try{//quantity为请求减去的库存数量$dbca->query('updates_storesetamount=amount-quantitywhereamount>=quantityandpostID=12345');}catch($eException){rollBack(rollback)}commit(committransaction)具体方案是在系统初始化时将商品的库存数量加载到Redis缓存中;当不足时(库存<0,可以在内存中设置一个booleanflag=soldout,不需要请求redis增加网络开销,分布式情况下通过zookeeper获取相应通知),直接返回秒杀失败,否则继续执行步骤3;将请求放入异步队列,返回正在排队;服务器端异步队列会对请求进行出队,请求成功可以生成flashorder减少数据库库存(失败则回滚恢复,redis中库存数据加1),返回秒杀订单详情。后台订单创建成功后,可以通过websocket向用户发送秒杀成功通知。前端以此来判断是否秒杀成功,如果秒杀成功则输入秒杀订单详情,否则秒杀失败。