缓存穿透是指缓存和数据库中都没有的数据,而用户不断发起请求查询该数据,导致数据库压力过大。
1、接口校验
如鉴权校验、数据合法性校验
2、布隆过滤器
将数据库中所有的查询条件,放入布隆过滤器中,当一个查询请求过来时,先经过布隆过滤器进行查,如果判断请求查询值存在,则继续查;如果判断请求查询不存在,直接丢弃。
3、缓存空结果
当没有命中缓存时,也没有从数据库取到数据时,我们给它缓存一个空结果,并且设置一个较短的过期时间(例如30s),之后当有请求访问时,可以直接从缓存中获取,保护后端数据源。
这个解决方案会存在两个问题:
1.会需要更多的空间来存储更多的键,因为这当中可能会产生很多的空值键;
2.数据一致性的问题,即时给空值设定了过期时间,缓存层和存储层还有会有一定时间的不一致,这对需要保持数据一致性的业务会有影响。
缓存击穿是缓存中没有但数据库有的数据(一般是“热点数据”缓存时间到期了),这个时候由于并发量特别大,读缓存的时候读取不到数据,都同时去数据库读取数据了,导致数据库的压力瞬间增大,
1、设置热点数据永不过期
2、加互斥锁
当多个线程同时去数据库查询这一条数据时,我们可以在第一个查询时使用互斥锁来锁住它,其他的线程走到这一步拿不到锁就进入等待的状态,等第一个线程查完数据,写入缓存后。后面的线程进来就可以直接走缓存了。
贴代码,仅供参考:
1 static Lock reenLock = new ReentrantLock(); 2 public Object getData() throws InterruptedException{ 3 //从缓存中获取数据 4 Object object = getDataFromRedis(); 5 if(object == null){ 6 if(reenLock.tryLock()){ 7 try { 8 System.out.println("拿到锁,从数据库中获取数据并写入缓存中"); 9 //从数据库中查询数据 10 object = getDataFromDB(); 11 //写入缓存 12 setDataToRedis(object); 13 } finally { 14 reenLock.unlock();// 释放锁 15 } 16 }else{ 17 object = getDataFromRedis();// 先查一下缓存 18 if (object == null) { 19 System.out.println("我没拿到锁,缓存也没数据,先等待一下"); 20 Thread.sleep(100); 21 return getData();// 重试 22 } 23 } 24 } 25 return object; 26 }
描述:
缓存雪崩与缓存击穿不同的一点是,缓存雪崩是指大量的热点Key设置了相同的过期时间,导致缓存在同一时刻大量失效,这时由于并发量过大,会导致所有的请求都需要到数据库去读取数据,数据库压力骤增,引起雪崩。
解决方案:
1、过期时间打散
既然是因为大量缓存集中失效导致的,那第一想到的应该是让它们不要集中失效就好了。可以给缓存的过期时间加一个随机值时间,使得每一个Key的过期时间都错开来,不会在同一时刻失效。
2、热点数据永不过期
3、加互斥锁
该方式和缓存击穿一样,按Key维度去加锁,同一个Key,指允许一个线程去请求数据库,后续的都从缓存中读取。
欢迎补充,点个推荐鼓励一下吧!