Redis高并发常见问题:缓存穿透缓存雪崩缓存击穿的概念及解决方案

    科技2025-03-14  19

    1.缓存穿透

    概念

    缓存穿透就是访问数据库中不存在的数据,高并发情况下或有人恶意的不停的访问该数据,导致请求打到数据库,直至数据库崩溃. 比如我们在写项目的时候,商品的主键id很少为负数.那么可能有人就会不停的访问id为-1的商品.而这个商品又不存在,导致我们的数据库崩溃.

    解决方案

    这里我通常的解决方案是在一个请求打到数据库返回null值时,在redis中给这个商品的key存入一个empty数据.使后边的请求直接访问redis而不是再去访问数据库.

    jedis.setex(key,60,"empty");

    但是这并不是绝对安全的解决方案,如果你想了解更多,可以查看我的另一篇文章布隆过滤器

    2.缓存雪崩

    概念

    缓存雪崩就是我们在设置缓存数据时,设置的有效期相同,导致在同一时刻,大部分的缓存同时到期,这时候,所有的请求在redis中拿不到数据,都去访问数据库,数据库就受到了所有请求的压力,在高并发的情况下,成千上万个请求同时到达数据库,导致数据库崩溃.

    解决方案

    对于缓存雪崩,我们通过概念可以发现,之所以会雪崩,就是因为多个缓存同时到期,那我们让他不同时到期就可以了,实现方式就是利用setex命令和Random类生成随机数的方式为缓存设置随机有效时间.这样就基本上不会出现多个缓存同时到期的现象了,也就是基本上不会出现缓存雪崩的现象了.

    int random=new Random().nextInt(60*10); jedis.setex(key,random,"empty");

    3.缓存击穿

    概念

    缓存击穿像是缓存穿透和缓存雪崩的结合,意思是一个数据太热点,在高并发情况下,这个数据的缓存到期,成千上万的请求又绕过了redis直接打到了数据库,导致数据库的崩溃.

    解决方案

    分布式锁. 分布式锁就是一把限制人流的锁,像一个保安一样,守护着进入数据库的入口.对于抢到锁的那一个请求才会放行,后面的请求只能排队.等待抢到锁的人进入数据库将数据从数据库取出来存到缓存中,才允许后面的人进来访问,此时访问的时候,前面的人已经将数据存放到缓存中,则后面的人就可以从Redis中取数据而不是直接访问数据库了. 关于分布式锁的具体实现方式及解释请参考我的这篇文章

    缓存击穿,缓存雪崩与缓存穿透的关系

    缓存击穿和缓存雪崩都是缓存穿透,只不过是它的特殊表现形式

    #.处理三个问题的完整代码

    public Student selectStudentById(Integer id) { String key="student-20-10-2:"+id+"-key"; String lockKey=key+":lock-"+id; String value= UUID.randomUUID().toString(); Jedis jedis = redisUtils.getJedis(); String s = jedis.get(key); Student student=null; if (s==null){ System.out.println("key:["+key+"]访问数据库获取数据"); RLock lock = redissonClient.getLock(lockKey); lock.lock(50, TimeUnit.SECONDS); /* String set = jedis.set(lockKey, value, "NX", "EX", 50);*/ if (lock.isLocked()){ student = studentMapper.selectByPrimaryKey(id); int random=new Random().nextInt(60*10); if (student==null){ jedis.setex(key,random,"empty"); }else{ jedis.setex(key,random,JSON.toJSONString(student)); } /* if (jedis.get(lockKey).equals(value)){ jedis.del(lockKey); }*/ lock.unlock(); }else{ /*没有抢到锁,进行回调*/ try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } return selectStudentById(id); } }else{ System.out.println("key:["+key+"]从Redis中获取数据"); if (s.equals("empty")) { student=null; }else{ student = JSON.parseObject(s, Student.class); } } return student; }
    Processed: 0.018, SQL: 8