redis缓存击穿如何解决
是指缓存中没有数据但数据库中有数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,导致短时间内数据库压力剧增,导致崩溃。
缓存击穿的解决方案:
1.设置热点数据永远不过期(热点数据key快要过期时,后台异步线程重新设置缓存)
2.设置互斥锁。在并发的多个请求中,只有第一个请求线程能拿到锁并执行数据库查询操作,其他的线程拿不到锁就阻塞等着,等到第一个线程将数据写入缓存后,直接走缓存。
// ---------------- { String lockId = UUID.randomUUID().toString(); //循环抢锁 while (Boolean.FALSE.equals(stringRedisTemplate.opsForValue().setIfAbsent("lock1020", lockId, 30, TimeUnit.SECONDS)) ) { try { Thread.sleep(50L); } catch (InterruptedException e) { e.printStackTrace(); } } //拿到这把锁, //value 商品库存800 try { String testLock10 = stringRedisTemplate.opsForValue().get("testLock10"); int i = Integer.parseInt(testLock10); i--;//商品库存-1 stringRedisTemplate.opsForValue().set("testLock10", String.valueOf(i)); } finally { String lockIdInRedis = stringRedisTemplate.opsForValue().get("lock1020"); if (lockId.equals(lockIdInRedis)) { //此锁是我加的,释放锁this lock is set by me,unlock it; stringRedisTemplate.delete("lock1020"); } } } // -------------------------
(可以使用 Redis 分布式锁)
(1) 就是在缓存失效的时候(判断拿出来的值为空),不是立即去加载数据库。
(2) 先使用缓存工具的某些带成功操作返回值的操作(比如Redis的SETNX,java对应redisTemplate.setIfAbsent("
))去set一个互斥锁。
(3) 当操作返回成功时,再进行加载数据库的操作,并回设缓存,最后删除mutex key(互斥锁);
(4) 当操作返回失败,证明有线程在加载数据库,当前线程睡眠一段时间再重试整个get缓存的方法。
3.接口限流与熔断,降级。重要的接口一定要做好限流策略,防止用户恶意刷接口,同时要做好降级准备,当接口中的某些服务不可用时候,进行熔断,建立失败快速返回机制。(可以借助sentinel,gateway等处理熔断降级)
下一篇:
脏读(脏数据、脏页)、不可重复读、幻读