> 文档中心 > 什么是缓存穿透、雪崩、击穿以及解决方案

什么是缓存穿透、雪崩、击穿以及解决方案

目录

  • 1、缓存穿透
  • 2、缓存雪崩
  • 3、缓存击穿
  • 4、区别总结
  • 5、加锁实现

1、缓存穿透

描述:
在查询一个数据时,在缓存中不存在,将去数据库进行查询并且数据库中也不存在数据,使得缓存中一直不会存在数据,导致在请求时每次都会到数据库中进行查询,那么失去了缓存意义。

解决:
将数据库中查询出来为null的数据,写入到缓存中并且设置较短的过期时间。
伪代码:

// 出现问题情况main(){String id = "123";Object obj = Redis.get(id);if(obj ==null){// redis获取数据为空,查询数据库Object dbObj = db.get(id);if(dbObj!=null){Redis.set(id,dbObj);}return dbObj;}return obj;}// 解决方案main(){String id = "123";Object obj = Redis.get(id);if(obj ==null){// redis获取数据为空,查询数据库Object dbObj = db.get(id);if(dbObj!=null){Redis.set(id,dbObj);}// 如果数据库也为null,将null存入缓存,设置较短过期时间(10S)Redis.set(id,dbObj,10,Seconds); return dbObj;}return obj;}

2、缓存雪崩

描述:
指设置缓存时的大量的key采用了相同的过期时间,在不同的请求进入时缓存的中大量的key刚好过期,导致请求全部转发到数据库中,使得数据库因为压力过大而崩溃。

解决:
在原有过期时间的基础上,加上一个随机值,比如1~60S,使得key的过期时间减少重复性。
伪代码:

// 模拟从数据库查询到大量不同类型数据main(){List<Object> list = db.getData();// 原有过期时间long expireTime = 10;for(Object obj : list){// 加一个随机值(1~60S)long newTime= expireTime  + (long) (Math.random() * 60);;Redis.set(key,obj ,newTime,Seconds);}}

3、缓存击穿

描述:
对于一些设置了过期时间的key,这些key被访问的频率非常高,在大量请求同时请求某个key时刚好出现过期的情况,那么对这个key的请求都会转发到数据库中,这就是缓存击穿。
解决:
在大量请求访问某个key时,进行加锁处理,使得其中某一个请求去查询数据库,其他请求进行等待,然后将查询出的数据写入缓存中,再解锁,那么后续的请求就会从缓存中查询到数据,而不是请求数据库。
伪代码:

main(){// 从redis中获取数据,在请求redis前进行加锁,加锁机制根据实际情况定// lock()方法为伪代码,表示加锁,只允许一个请求向下执行lock();// redis获取数据Object obj = Redis.get(id);if(obj == null){// 请求数据库Object dbObj = db.get(id);// 写回到缓存中Redis.set(id,dbObj);}// unlock()方法为伪代码,表示解锁,使得其他请求继续执行// 在其他请求执行Redis.get(id)时,缓存中已经有了数据,不会再去数据库查询unlock();}

4、区别总结

  • 穿透
    原因:查询redis时查询不到数据,然后去查询数据库也查不到数据,这将导致redis中一直不能缓存到数据。
    解决:将查询为null的数据也放入缓存,并且设置较短的过期时间。

  • 雪崩
    原因:多个key同时失效,高并发下redis查询不到数据,导致大量请求进入到数据库。
    解决:对同一类型不同值的key设置不同的失效时间(比如:对手机类型的各条记录(key)设置不同的过期时间)。

  • 击穿
    原因:对于本该存在于redis中的key,在高并发时key刚好失效,导致大量请求进入到数据库。
    解决:让请求加锁的查询redis,如果没有数据再查询数据库,并且将数据库结果加入到缓存中,后续查询时只能等待上次查询释放锁,再后续请求获取到锁时redis缓存中就已经有了数据,所以不用查询数据库。

5、加锁实现

加锁实现,参考代码《Redis实现分布式锁》

总结:
穿透 — 缓存和数据库中都不存在数据,导致数据一直无法进行缓存;雪崩 — 大量的不同key同时过期,导致大量不同请求访问数据库;击穿 — 大量请求同访问某个在缓存中的key,刚好失效,导致所有请求进入数据库;