乐趣区

关于redis:深入理解redis缓存雪崩缓存击穿缓存穿透

1. 缓存雪崩

2. 缓存击穿

3. 缓存穿透

4. 总结

1. 缓存雪崩
缓存雪崩是如何产生的?
1)redis 服务间接挂掉,redis 全盘解体
2)redis 中有大量缓存同时过期,导致大量查问直击 mysql

解决
1.1)redis 缓存集群实现高可用,主从 + 哨兵
1.2)ehcache 本地缓存 + Hystrix 或者阿里 sentinel 限流 & 降级
1.3) 开启 Redis 长久化机制 aof/rdb,尽快恢复缓存集群

2.1)设置缓存更新的工夫都为 固定工夫 + 随机肯定的工夫 ,造成 不会同时过期

2. 缓存击穿
缓存雪崩是如何产生的?
大量的申请 正在拜访 同一个 key, 此时这个 key 正好生效了,就会导致 大量的申请到数据库下面。

危害:
会造成某一时刻的 mysql 压力过大,有宕机危险。

解决方案:
1)对于高热点 key,设置永不过期
2)设置一个 互斥锁,来避免缓存击穿:

    public TUser findById(Integer id) {TUser user  = (TUser)redisTemplate.opsForValue().get(CACHE_KEY_USER + id);
        if(user==null){
            // 小厂用
          /*  user = userMapper.selectById(id);
            if(user!=null) {redisTemplate.opsForValue().set(CACHE_KEY_USER + id, user);
            }*/
            // 大厂用,对于高 qps 的优化,进来就先加锁,保障一个申请操作,让里面的 redis 期待一下,防止击穿 mysql
            synchronized (TUserServiceImpl.class){
                // 双端检索,再次查问 redis
                user  = (TUser)redisTemplate.opsForValue().get(CACHE_KEY_USER + id);
                if(user==null){
                    // 还是为空,就去查 mysql
                    user = userMapper.selectById(id);
                    if(user!=null){redisTemplate.opsForValue().setIfAbsent(CACHE_KEY_USER + id, user,7L, TimeUnit.DAYS);
                    }
                }
            }
        }
        return user;
    }

3)双缓存保留,定时轮询,互斥更新,差别生效工夫

需要:假如咱们要实现网页上的一个聚划算性能,每隔两个小时换一批商品。比方:8:000~10:00 一批商品,10:00~12:00 一批商品。如果依照定时工作的做法,8:00 开始更新商品,假如数据量很大,mysql 在很难查出来的状况下,到点了可能缓存曾经过期了,然而 mysql 还没查问出商品,又会有大量查问攻打 mysql,导致服务宕机。

思路:咱们设置两个缓存,都保留雷同的内容,两个缓存的过期工夫不同。缓存 B 比缓存 A 过期工夫更长一些,这样就能够保障缓存里永远有数据。

查问:先查问缓存 A,如果 A 没有,查问缓存 B。

// 采纳 redis list 数据结构的 lrange 命令实现分页查问
list = this.redisTemplate.opsForList().range(Constants.JHS_KEY_A, start, end);
if (CollectionUtils.isEmpty(list)) {log.info("========= A 缓存曾经生效了,记得人工修补,B 缓存主动连续 5 天");
// 用户先查问缓存 A(下面的代码),如果缓存 A 查问不到(例如,更新缓存的时候删除了),再查问缓存 B
this.redisTemplate.opsForList().range(Constants.JHS_KEY_B, start, end);

更新:先更新缓存 B,再更新缓存 A。

// 先更新 B 缓存
this.redisTemplate.delete(Constants.JHS_KEY_B);
this.redisTemplate.opsForList().leftPushAll(Constants.JHS_KEY_B,list);
this.redisTemplate.expire(Constants.JHS_KEY_B,20L,TimeUnit.DAYS);
// 再更新 A 缓存
this.redisTemplate.delete(Constants.JHS_KEY_A);
this.redisTemplate.opsForList().leftPushAll(Constants.JHS_KEY_A,list);
this.redisTemplate.expire(Constants.JHS_KEY_A,15L,TimeUnit.DAYS);

留神:采纳互斥更新和查问,保障两个 key 都有值。

3. 缓存穿透
缓存穿透是什么:
申请查问一条记录,先去 redis 查问后查问不到数据 再去 mysql 查问后也查问不到数据 ,所以每次申请都会攻打 mysql, 导致数据库压力暴增

危害:第一次来查问后,个别咱们有回写 redis 机制,所以偶发一次没关系,然而频繁产生就有安全隐患。

解决方案:
1)空对象或者缺省值
一般来说这么做是没问题的,然而如果黑客大量拿未知的 key 来攻打咱们的零碎,每次都要去查问,且 redis 中无用 key 越写越多。

2)Redis 布隆过滤器解决缓存穿透
深刻了解 redis——布隆过滤器 BloomFilter
能够应用布隆过滤器来解决缓存穿透,布隆过滤器曾经在这篇文章里解说过了。

4. 总结
明天学习了缓存雪崩 / 缓存击穿 / 缓存穿透的景象以及对应计划。

退出移动版