Redis缓存穿透解决方案

Redis缓存穿透解决方案

1、什么是缓存穿透

查询的key在redis中不存在,对应的ID在数据库也不存在,此时被非法用户进行攻击,大量的请求会直接冲入数据库上造成宕机,从而影响整个系统,这种现象称之为:缓存穿透

@GetMapping("/findCategory/{cid}")
    public List<Category> findCategory(@PathVariable("cid") Integer cid) {
        // 判断分类id是否传入,如果没有传入那么直接返回
        KAssert.isEmpty(cid, 401, "分类不存在");
        List<Category> categoryList = new ArrayList<>();
        // 先去缓存中去获取分类信息
        String categories = (String) redisTemplate.opsForValue().get("subCid:" + cid);
        if (StringUtils.isEmpty(categories)) {
            log.info("数据库查询");
            categoryList = categoryService.findCategroies(cid);
            if (!CollectionUtils.isEmpty(categoryList)) {
                redisTemplate.opsForValue().set("subCid:" + cid, JsonUtil.obj2String(categoryList));
            }
        } else {
            categoryList = JsonUtil.string2Obj(categories, List.class, Category.class);
        }
        return categoryList;
    }

以上的代码是在开发中非常常见的代码。

但是如果有一些黑客或者肉机,故意输入一些不存在的ID,或者大量的用户输错了地址,那么缓存永远都是null。永远都命中到数据库。如果这个时候出现的请求并发很大,可能就会把数据库冲垮。

比如:cid==666 在数据表中不存在

String categories = (String) redisTemplate.opsForValue().get("subCid:" + cid);

上面的缓存代码为空,就会进入到数据db去查询,如果黑客或者肉机,大量错误输入,请求非常大的话就造成缓存穿透。

2、解决方案

2.1 将不存在的key,存一个默认值:null ,并且给予过期时间。
@GetMapping("/findCategory/{cid}")
public List<Category> findCategory(@PathVariable("cid") Integer cid) {
    // 判断分类id是否传入,如果没有传入那么直接返回
    KAssert.isEmpty(cid, 401, "分类不存在");
    List<Category> categoryList = new ArrayList<>();
    // 先去缓存中去获取分类信息
    String categories = (String) redisTemplate.opsForValue().get("subCid:" + cid);
    if (StringUtils.isEmpty(categories)) {
        log.info("数据库查询");
        // 如果不存在就从数据库查询
        categoryList = categoryService.findCategroies(cid);
        // 判断如果集合有数据,那么就会放入缓存。就是这个判断,造成了缓存穿透。
        if (CollectionUtils.isEmpty(categoryList)) {
            // 将不存在的key,存一个默认值:null,并且给予过期时间
            redisTemplate.opsForValue().set("subCid:" + cid, JsonUtil.obj2String(categoryList), 5 * 60, TimeUnit.SECONDS);
        } else {
            // 放入到redis缓存中
            redisTemplate.opsForValue().set("subCid:" + cid, JsonUtil.obj2String(categoryList));
        }
    } else {
        // 是否存在,如果存在直接从缓存返回
        categoryList = JsonUtil.string2Obj(categories, List.class, Category.class);
    }
    return categoryList;
}
2.2 布隆过滤器
@GetMapping("/findCategory/{cid}")
    public List<Category> findCategory(@PathVariable("cid") Integer cid) {
        // 判断分类id是否传入,如果没有传入那么直接返回
        KAssert.isEmpty(cid, 401, "分类不存在");
//         // 进行布隆过滤 bf.madd redis:bloom:category “1” "2" "3" "4" "5"
//         // 进行布隆过滤 bf.mexists redis:bloom:category “1”
        boolean[] booleans = reBloomClient.existsMulti("redis:bloom:category", cid + "");
//         // 如果cid没有在布隆过滤器中,说明你数据不存在,直接返回
        if (!booleans[0]) {
            throw new ValidationException(401, "分类不存在");
        }

        // BloomFilter的认识
        // 用了Redis缓存就真的不会进入数据库了吗?
        List<Category> categoryList = new ArrayList<>();
        // 先去缓存中去获取分类信息
        String categories = (String) redisTemplate.opsForValue().get("subCid:" + cid);
        if (StringUtils.isEmpty(categories)) {
            log.info("数据库查询");
            categoryList = categoryService.findCategroies(cid);
            if (!CollectionUtils.isEmpty(categoryList)) {
                redisTemplate.opsForValue().set("subCid:" + cid, JsonUtil.obj2String(categoryList));
            }
        } else {
            categoryList = JsonUtil.string2Obj(categories, List.class, Category.class);
        }
        return categoryList;
    }
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
对于 Redis 缓存穿透、击穿和雪崩问题,可以采取以下解决方案: 1. 缓存穿透:当请求的数据在缓存中不存在时,会直接访问数据库,如果有恶意攻击者大量请求不存在的数据,会给数据库造成很大压力。解决方案可以是在查询前进行参数校验,比如对请求的参数进行合法性检查,或者使用布隆过滤器等技术来快速判断请求的数据是否存在。 2. 缓存击穿:当某个热点数据过期或被删除时,大量请求同时涌入,导致请求直接访问数据库。为了解决这个问题,可以使用互斥锁(Mutex)或者分布式锁来避免多个请求同时访问数据库。在获取锁之前,首先尝试从缓存获取数据,如果缓存中存在,则直接返回;如果不存在,则获取锁,并从数据库中获取数据并放入缓存,最后释放锁。 3. 缓存雪崩:当缓存中的大量数据同时过期时,会导致大量请求直接访问数据库,给数据库带来巨大压力。为了解决这个问题,可以采取以下措施: - 设置合理的缓存过期时间,使得不同数据的过期时间错开,避免同时失效。 - 使用热点数据预加载,提前将热点数据加载到缓存中,避免同时失效。 - 使用多级缓存架构,将缓存分为多个层级,不同层级的缓存设置不同的过期时间,从而降低缓存失效的风险。 - 引入限流和熔断机制,对请求进行限制,避免大量请求同时访问数据库。 通过以上措施,可以有效地解决 Redis 缓存穿透、击穿和雪崩问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

南宫拾壹

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值