ICode9

精准搜索请尝试: 精确搜索
首页 > 数据库> 文章详细

Redis 缓存穿透、击穿和雪崩

2021-05-26 03:01:32  阅读:180  来源: 互联网

标签:缓存 数据源 object Redis Goods goodsId goods 雪崩


1. 缓存穿透

1.1 概念

请求的 key 在缓存和数据源中都不存在,就会导致每次请求都访问到数据源,失去了缓存的意义。

1.1.1 示例代码
@Override
public Goods searchArticleById(Long goodsId){
    Object object = redisTemplete.opsForValue().get(String.valueOf(goodsId));
    
    //缓存查询命中
    if(object != null){
    	return Goods(object);
    }
    
    //从数据源中查询
    Goods goods = goodsMapper.selectByPrimaryKey(goodsId);
    if(goods != null){
        //将查询结果放入缓存
        redisTemplete.opsForValue().set(String.valueOf(goodsId),goods,60,TimeUnit.MINUTES);
    }
    return goods;
}

1.2 解决方案

1.2.1 缓存空值

如果一个查询没有从数据源获取到数据,不管它是真的不存在还是存在故障,我们都把这个空结果进行缓存,但是要设置一个较短的过期时间。这样在短时间内,再次查询就不会继续去访问数据源了。

1.2.1.1 实例代码
@Override
public Goods searchArticleById(Long goodsId){
    Object object = redisTemplete.opsForValue().get(String.valueOf(goodsId));
    
    //缓存查询命中
    if(object != null){
    	return Goods(object);
    }
    
    //从数据源中查询
    Goods goods = goodsMapper.selectByPrimaryKey(goodsId);
    if(goods != null){
         //将查询结果放入缓存
        redisTemplete.opsForValue().set(String.valueOf(goodsId),goods,60,TimeUnit.MINUTES);
    }else{
        //缓存空值
        redisTemplete.opsForValue().set(String.valueOf(goodsId),goods,60,TimeUnit.SECONDS);
    }
        
    return goods;
}
1.2.2 布隆过滤器

将所有可能请求的数据放入一个足够大 bitmap 中,一个不存在的请求数据就会被拦截到,避免对数据源的冲击。

1.2.2.1 什么是布隆过滤器

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

如果想判断一个元素是不是在一个集合里,一般想到的是将集合中所有元素保存起来,然后通过比较确定。链表、树、散列表(又叫哈希表,Hash table)等等数据结构都是这种思路。但是随着集合中元素的增加,我们需要的存储空间越来越大。同时检索速度也越来越慢,上述三种结构的检索时间复杂度分别为:O (n), O (log n), O (n/k)。

布隆过滤器的原理是,当一个元素被加入集合时,通过 K 个 Hash 函数将这个元素映射成一个位数组中的 K 个点,把它们置为 1。检索时,我们只要看看这些点是不是都是 1 就(大约)知道集合中有没有它了:如果这些点有任何一个 0,则被检元素一定不在;如果都是 1,则被检元素很可能在。这就是布隆过滤器的基本思想。

1.2.2.2 布隆过滤器的实现

在实际应用当中,我们不需要自己去实现 BloomFilter。可以使用 Guava 提供的相关类库即可。

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>25.1-jre</version>
</dependency>
public class Test1 {

    private static int size = 1000000;

    //定义错误率
    private static BloomFilter<Integer> bloomFilter = BloomFilter.create(Funnels.integerFunnel(), size,0.01);

    public static void main(String[] args) {
        for (int i = 0; i < size; i++) {
            bloomFilter.put(i);
        }

        long startTime = System.nanoTime(); // 获取开始时间
        //判断这一百万个数中是否包含29999这个数
        if (bloomFilter.mightContain(29999)) {
            System.out.println("命中了");
        }
        long endTime = System.nanoTime();   // 获取结束时间
        System.out.println("程序运行时间: " + (endTime - startTime) + "纳秒");
    }

}

布隆过滤器本身存在一定的不准确性。

1.2.3 接口限流与熔断、降级

在一定时间流量超出预期后,拒绝预期的访问,给予用户友好提示。

2. 缓存击穿

2.1 概念

某一个 key 访问流量非常大,大并发集中对这个 key 进行发问,当这个 key 失效的瞬间,持续的大并发直接落到了数据源上,对数据源造成冲击。

2.2 解决方案

2.2.1 热点键永不过期

如果可以话,热点键不设置过期时间,这样也就不存在击穿的问题。

2.2.2 互斥锁

当缓存失效的时候,使用互斥锁来让请求进行排队。这样并没有提供并发量,只是解决了可能会造成的数据源的压力。

2.2.2.1 示例代码
@Override
public Goods searchArticleById(Long goodsId){
    Object object = CacleUtils.get(String.valueOf(goodsId));
    
    //缓存查询命中
    if(object != null){
    	return Goods(object);
    }
    
    //先尝试获取一把分布式锁
    Goods goods = null;
    try{
        Boolean result = ReenLock.tryLock(key_mutex,requestId,60000);
        if(result){
            //从数据源中查询
            goods = goodsMapper.selectByPrimaryKey(goodsId);
            if(goods !=null){
                CacheUtils.set(String.valueOf(goodsId),goods,60000);
            }
        }else{
            //稍后再去尝试
            Thread.sleep(100);
            result = searchArticleById(goodsId);
        }
    }finally{
        ReenLock.unLock(key_mutex,requestId);
    }
    
    return goods;
}

3. 缓存雪崩

3.1 概念

缓存雪崩,是指在某一个时间段,缓存集中过期失效。

3.2 解决方案

3.2.1 加锁排队

使用互斥锁,让来进来的请求进行排队处理。

3.2.2 不同过期时间

既然缓存雪崩时同一时间点,大量的key过期,导致请求落到数据源上,那就尽量让不同的键设置不同的过期时间,尽量让缓存失效的时间分布均匀。

3.2.3 双层缓存策略

C1 为原始缓存,C2 为拷贝缓存,C1 失效时,可以访问 C2,C1 缓存失效时间设置为短期,C2 设置为长期。

3.2.4 数据预热

解决冷启动造成的雪崩,可以提前预估热点数据,将相关的缓存数据直接加载到缓存系统,避免冷启动。

3.2.5 热点永不过期

不过期自然也就没有这个问题。

标签:缓存,数据源,object,Redis,Goods,goodsId,goods,雪崩
来源: https://www.cnblogs.com/sivanchan/p/14811651.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有