Redis:缓存穿透

缓存穿透

缓存穿透就是查询一个数据库一定不存在的数据,首先根据key去查询缓存,若缓存中不存在或者缓存已经过期就去查询数据库,把查询出来的结果放入缓存,如果为空则不放入缓存。

public Goods getGoods(@PathVariable("id") Integer id) {
        
    //查询缓存
    Object obj = redisTemplate.opsForValue().get(String.valueOf(id));
    if (Objects.nonNull(obj)) {
        return (Goods) obj;
    }

    //查询数据库
    Goods goods = goodsMapper.getGoods(id);
    if (Objects.nonNull(goods)) {
        redisTemplate.opsForValue().set(String.valueOf(id), goods, 60, TimeUnit.MINUTES);
    }
    return goods;
}

根据数据库主键自增策略,若传入的参数是-1,那么这个参数一定在数据库中查不到结果,而且每次都不会进行缓存,假如有恶意攻击,就会利用这个漏洞压垮数据库,怎么破?
可以利用缓存空值的方式,如果在数据库中查不到结果,就把null放入缓存中,设置缓存过期时间较小,比如60秒。

public Goods getGoods(@PathVariable("id") Integer id) {

    //查询缓存
    Object obj = redisTemplate.opsForValue().get(String.valueOf(id));
    if (Objects.nonNull(obj)) {
    	System.out.println("从缓存中取数据");
        return (Goods) obj;
    }

    //查询数据库
    Goods goods = goodsMapper.getGoods(id);
    System.out.println("从数据库中取数据");
    if (Objects.nonNull(goods)) {
        redisTemplate.opsForValue().set(String.valueOf(id), goods, 60, TimeUnit.MINUTES);
    } else {
        redisTemplate.opsForValue().set(String.valueOf(id), null, 60, TimeUnit.SECONDS);
    }
    return goods;
}

但是以上代码在高并发下依然存在问题,当我用压测工具压测接口的时候结果如图:
在这里插入图片描述
多线程下很多数据都是从数据库取,但是我们想要的效果是只有第一次取数据的时候走数据库,其余都走缓存,所以有了如下优化:

public Goods getGoods(@PathVariable("id") Integer id) {
    //查询缓存
    Goods goods = (Goods) redisTemplate.opsForValue().get(String.valueOf(id));
    if(Objects.nonNull(goods)){
        System.out.println("查询缓存");
        return goods;
    }
    //加入双重检查锁
    synchronized (this) {
        goods = (Goods) redisTemplate.opsForValue().get(String.valueOf(id));
        if (Objects.isNull(goods)) {
            //查询数据库
            goods = goodsMapper.getGoods(id);
            System.out.println("查询数据库");
            if (Objects.isNull(goods)) {
                redisTemplate.opsForValue().set(String.valueOf(id), null, 60, TimeUnit.SECONDS);
            }
            //缓存空值
            redisTemplate.opsForValue().set(String.valueOf(id), goods, 60, TimeUnit.MINUTES);
        }
    }
    return goods;
}

结果如图:
在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值