栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

缓存穿透、缓存击穿、缓存雪崩区别和解决方案

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

缓存穿透、缓存击穿、缓存雪崩区别和解决方案

一、缓存处理流程

      前台请求,后台先从缓存中取数据,取到直接返回结果,取不到时从数据库中取,数据库取到更新缓存,并返回结果,数据库也没取到,那直接返回空结果。

二、缓存穿透

       描述: 缓存穿透是指缓存和数据库中都没有的数据。正常的使用缓存流程大致是,数据查询先进行缓存查询,如果key不存在或者key已经过期,再对数据库进行查询,并把查询到的对象,放进缓存。如果数据库查询对象为空,则不放进缓存。如攻击者发起id为“-1”的数据或id为特别大等不存在的数据

     解决方案:

1 过滤器

1)控制层代码校验:如用户鉴权校验,id基础校验,id不规范的的直接拦截;

2)布隆过滤器:在访问缓存层和存储层之前,将存在的key用布隆过滤器提前保存起来,做第一层拦截,当收到一个对key请求时先用布隆过滤器验证是key否存在,如果存在在进入缓存层、存储层。

这种方法适用于数据命中不高、数据相对固定、实时性低的应用场景,代码维护较为复杂,但是缓存空间占用少。

布隆过滤器实际上是一个很长的二进制向量和一系列随机映射函数。布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率和删除困难。

@Cacheable(value="key1")
public String get(String key) {
    String value = redis.get(key);  
    // redis中不存在该缓存
    if (value  == null) {
    //布隆过滤器也没有,直接返回
        if(!bloomfilter.mightContain(key)){
            return null; 
        }else{
            //布隆过滤器中能查到,不代表一定有,查出来放入redis,同样也可以避免缓存穿透
            value = db.get(key);
            redis.set(key, value); 
        }    
    }
    return value;
}

2 缓存空对象

从缓存和数据库中没有取到的数据,在缓存中缓存一个默认空值key-null,同时设置缓存有效时间,如30秒,这样可以防止攻击用户反复用同一个id暴力攻击。

public object GetProductListNew()
        {
            const int cacheTime = 30;
            const string cacheKey = "product_list";

            var cachevalue = CacheHelper.Get(cacheKey);
            if (cachevalue != null)
                return cachevalue;
                
            cachevalue = CacheHelper.Get(cacheKey);
            if (cachevalue != null)
            {
                return cachevalue;
            }
            else
            {
                cachevalue = GetProductListFromDB(); //数据库查询不到,为空。
                
                if (cachevalue == null)
                {
                    cachevalue = string.Empty; //如果发现为空,设置个默认值,也缓存起来。                
                }
                CacheHelper.Add(cacheKey, cachevalue, cacheTime);
                
                return cachevalue;
            }
        }    
三、缓存击穿

      描述:

      缓存击穿,是指缓存中没有但数据库中有的数据,并且某一个key非常热点,在不停的扛着大并发,大并发集中对这一个点进行访问,当这个key在失效的瞬间(一般是缓存时间到期),持续的大并发就穿破缓存,直接请求数据库,就像在一个屏障上凿开了一个洞。

      解决方案:

1)设置热点数据永远不过期


2)加互斥锁

让一个线程构建缓存,其他线程等待构建缓存的线程执行完,重新从缓存获取数据就可以了。

参考代码如下:

public String get(key) {
      String value = redis.get(key);
      if (value == null) {           //设置分布式锁,只允许一个线程去查询DB,同时指定过期时间为1min,防止del操作失败,导致死锁,缓存过期无法加载DB数据
      		if (redis.setnx(key_mutex, 1, 1 * 60) == 1) {  //代表设置成功
               value = db.get(key);
               redis.set(key, value, expire_secs);
               //释放分布式锁
               redis.del(key_mutex);
          } else {  //这个时候代表锁被占用了,睡眠5s在尝试
               Thread.sleep(5000);
               get(key);  //重试
              }
      } else {
          return value;      
   }
 }
四、缓存雪崩

      描述:

      缓存雪崩是指缓存中数据大批量到过期时间,而查询数据量巨大,引起数据库压力过大甚至down机。和缓存击穿不同的是,缓存击穿指并发查同一条数据,缓存雪崩是不同数据都过期了,很多数据都查不到从而查数据库。

     解决方案:

1)缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生。
2)如果缓存数据库是分布式部署,将热点数据均匀分布在不同搞得缓存数据库中。
3)设置热点数据永远不过期。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/691618.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号