解决redis缓存穿透,缓存击穿,缓存雪崩

文章讨论了缓存系统中的三种常见问题:缓存穿透、缓存击穿和缓存雪崩。针对这些问题,提出了相应的解决方案,包括对空值进行缓存、使用布隆过滤器、采用互斥锁以及调整过期时间策略等。这些方法旨在提高缓存命中率,减轻数据库压力。
摘要由CSDN通过智能技术生成

缓存穿透

缓存穿透是指用户查询数据,在数据库没有,自然在缓存中也
不会有。这样就导致用户查询的时候,在缓存中找不到,每次
都要去数据库再查询一遍,然后返回空(相当于进行了两次无
用的查询)。这样请求就绕过缓存直接查数据库,这也是经常
提的缓存命中率问题。
解决
对空值缓存 :如果一个查询返回的数据为空(不管数据是否存在),我们仍然把这个空结果缓存,
设置空结果的过期时间会很短,最长不超过 5 分钟。
1
布隆过滤器 :如果想判断一个元素是不是在一个集合里,一般想到的是将集合中所有元素保存起
来,然后通过比较确定。
代码:
/**
 * 布隆过滤器,,解决缓存穿透
 */
@Test
public void filter(){
    BitMapBloomFilter bitMapBloomFilter = new BitMapBloomFilter(10);
    bitMapBloomFilter.add("ads");
    bitMapBloomFilter.add("abc");
    bitMapBloomFilter.add("123");
    bitMapBloomFilter.add("456");

    System.out.println(bitMapBloomFilter.contains("dfgdf"));
    System.out.println(bitMapBloomFilter.contains("abc"));
    System.out.println(bitMapBloomFilter.contains("a"));

}

缓存击穿

某一个热点 key ,在缓存过期的一瞬间,同时有大量的请求打进
来,由于此时缓存过期了,所以请求最终都会走到数据库,造成瞬
时数据库请求量大、压力骤增,甚至可能打垮数据库。(某个key失效时,有多个请求访问)
解决
互斥锁 :在并发的多个请求中,只有第一个请求线程能拿到锁并执行数据库查询操作,其他的线程
拿不到锁就阻塞等着,等到第一个线程将数据写入缓存后,其他线程直接查询缓存。
1
热点数据不过期 :直接将缓存设置为不过期,然后由定时任务去异步加载数据,更新缓存。
代码
/**
 * 互斥锁,,解决缓存缓存击穿
 */
@Test
public void lock(String key) throws InterruptedException {
    Jedis jedis = new Jedis("192.168.66.100", 6379);
    String value = jedis.get("key");

    if (value == null){
        //只有在key不存在时才创建
        long setnx = jedis.setnx(key + "_hc", "1");
        //设置过期时间
        jedis.pexpire(key + "_hc",3*60);
        //设置成功
        if (setnx == 1){
            //db操作
            value="db";
            //保存缓存
            jedis.setex(key,3*60,value);
            //删除
            jedis.del(key+"_hc");
        }else {
            Thread.sleep(5000);
            lock(key);
        }
    }
}

缓存雪崩

缓存雪崩是指在我们设置缓存时采用了相同的过期时间,导致缓存
在某一时刻同时失效,请求全部转发到 DB DB 瞬时压力过重雪
崩。(多个key同一时间失效)
解决
过期时间打散 :既然是大量缓存集中失效,那最容易想到就是让他们不集中生效。可以给缓存的过
期时间时加上一个随机值时间,使得每个 key 的过期时间分布开来,不会集中在同一时刻失效。
热点数据不过期 :该方式和缓存击穿一样,也是要着重考虑刷新的时间间隔和数据异常如何处理的
情况。
加互斥锁 : 该方式和缓存击穿一样,按 key 维度加锁,对于同一个 key ,只允许一个线程去计算,
其他线程原地阻塞等待第一个线程的计算结果,然后直接走缓存即可。
代码
   /**
     * 加锁,解决缓存雪崩
     */
    @Test
    public Object saveLock(String key) {
        Jedis jedis = new Jedis("192.168.66.100", 6379);
        String lockKey = key;
        String value = jedis.get(key);
        if (value == null) {
//            加锁,只允许一个线程执行
            synchronized (lockKey) {
                String s = jedis.get(key);
                if (s == null) {
                    return s;
                } else {
//                    执行查询数据库的值
                    value = "db";
                    jedis.set(key, value);
                    return value;
                }
            }
        }else{
            return value;
        }

    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值