Redis学习笔记(三) | 青训营笔记

67 阅读2分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 3 天

前面记录了redis的一些基本操作,从本节开始学习日常业务中利用redis开发可能会遇到的一些问题以及实战等。本节主要学习和记录缓存穿透这一问题。

  • 什么是缓存穿透?
    • 传个不存在的id过来,redis查不到会去查mysql,mysql查不到会返回空,同时redis同样没有数据,不断重复该操作,导致大量请求到达mysql使mysql压力很大。
  • 解决方案
    • 缓存空对象
      • 对于经过mysql查找后确认不存在的key,直接在redis缓存一个空对象,缓存时间不能太长,待key再请求时直接从redis获取空对象返回
      • 优点:实现简单,维护方便
      • 缺点:额外的内存消耗;有概率造成短期的不一致
    • 布隆过滤
      • 在访问redis前依靠算法判断该数据是否存在,存在则放行,否则直接返回空
      • 实现原理:一个byte[]存储二进制位,把数据基于hash算法计算出hash值,然后将这些值转化为二进制位,保存到布隆过滤器里,判断数据是否存在时就判断对应数据是0或1
      • 注意:概率事件,不存在则真的不存在,存在则不一定存在,仍有穿透风险
      • 优点:内存占用小,无多余key
      • 缺点:实现复杂;存在误判可能
    • 主动方案
      • 增强id的复杂度,避免被猜测id规律
      • 做好数据的基础格式校验
      • 加强用户权限校验
  • 案例
    public Shop queryWithPassThrough(Long id){
        String key = RedisConstants.CACHE_SHOP_KEY + id;
        Shop shop = new Shop();
        try {
            shop = redisService.getObject(key, Shop.class);
    
            if(!Objects.isNull(shop)){
                //存在直接返回
                return shop;
            }else {
                String s = redisService.get(key);
                //在保存空值前,该会报空指针异常
                if(s.equals("null")){
                    return null;
                }
            }
        } catch (Exception e){
            log.info("null");
        }
    
        //数据库查询
        shop = getById(id);
        if(Objects.isNull(shop)){
            //保存空值,针对缓存穿透问题
            redisService.add(key,"null",RedisConstants.CACHE_NULL_TTL, TimeUnit.MINUTES);
            return null;
        }
        //非空则放入redis
        redisService.add(key,shop,RedisConstants.CACHE_SHOP_TTL, TimeUnit.MINUTES);
        return shop;
    }