Redis:缓存穿透

释放双眼,带上耳机,听听看~!

缓存穿透

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


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
1public Goods getGoods(@PathVariable("id") Integer id) {
2        
3    //查询缓存
4    Object obj = redisTemplate.opsForValue().get(String.valueOf(id));
5    if (Objects.nonNull(obj)) {
6        return (Goods) obj;
7    }
8
9    //查询数据库
10    Goods goods = goodsMapper.getGoods(id);
11    if (Objects.nonNull(goods)) {
12        redisTemplate.opsForValue().set(String.valueOf(id), goods, 60, TimeUnit.MINUTES);
13    }
14    return goods;
15}
16
17

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


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
1public Goods getGoods(@PathVariable("id") Integer id) {
2
3    //查询缓存
4    Object obj = redisTemplate.opsForValue().get(String.valueOf(id));
5    if (Objects.nonNull(obj)) {
6       System.out.println("从缓存中取数据");
7        return (Goods) obj;
8    }
9
10    //查询数据库
11    Goods goods = goodsMapper.getGoods(id);
12    System.out.println("从数据库中取数据");
13    if (Objects.nonNull(goods)) {
14        redisTemplate.opsForValue().set(String.valueOf(id), goods, 60, TimeUnit.MINUTES);
15    } else {
16        redisTemplate.opsForValue().set(String.valueOf(id), null, 60, TimeUnit.SECONDS);
17    }
18    return goods;
19}
20
21

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


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
1public Goods getGoods(@PathVariable("id") Integer id) {
2    //查询缓存
3    Goods goods = (Goods) redisTemplate.opsForValue().get(String.valueOf(id));
4    if(Objects.nonNull(goods)){
5        System.out.println("查询缓存");
6        return goods;
7    }
8    //加入双重检查锁
9    synchronized (this) {
10        goods = (Goods) redisTemplate.opsForValue().get(String.valueOf(id));
11        if (Objects.isNull(goods)) {
12            //查询数据库
13            goods = goodsMapper.getGoods(id);
14            System.out.println("查询数据库");
15            if (Objects.isNull(goods)) {
16                redisTemplate.opsForValue().set(String.valueOf(id), null, 60, TimeUnit.SECONDS);
17            }
18            //缓存空值
19            redisTemplate.opsForValue().set(String.valueOf(id), goods, 60, TimeUnit.MINUTES);
20        }
21    }
22    return goods;
23}
24
25

结果如图:
Redis:缓存穿透

给TA打赏
共{{data.count}}人
人已打赏
安全运维

OpenSSH-8.7p1离线升级修复安全漏洞

2021-10-23 10:13:25

安全运维

设计模式的设计原则

2021-12-12 17:36:11

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索