0%

如何应对Redis在缓存应用中的穿透、击穿以及雪崩问题?

“唯之与阿,相去几何?
善之与恶,相去何若?
人之所畏,不可不畏。
荒兮,其未央哉!
众人熙熙,如享太牢,如春登台。
我独泊兮,其未兆,如婴儿之未孩;傫傫兮,若无所归!
众人皆有余,而我独若遗。
我愚人之心也哉!”1

如何应对Redis在缓存应用中的穿透、击穿以及雪崩问题?

Redis在做缓存时,通常在代码逻辑上会做如下处理:

  1. 查询缓存
  2. 缓存有,则返回;缓存没有则查询数据库;
  3. 查询数据库,返回;并放入缓存

问题

问题1: 如果缓存没有,数据库也没有,此为缓存穿透。

譬如获取用户信息,当请求携带一个并不存在的id时(譬如:被攻击),就会将请求集中落在访问数据库上。
解决方法:
在缓存和数据库都无法获取对应信息时,将返回的空对象放入缓存,并设置一个合理的过期时间(譬如30秒等)。

问题2: 高并发访问,导致还是有部分的查询接触到了数据库层面,此为缓存击穿。

解决方法:
2步从数据库获取信息代码块上包上同步锁 synchronized即可。

当某个时刻由于某种原因,缓存集体时效了,导致缓存雪崩。

解决方法:
1.粗暴,设置永不过期;弊端在于需要编写定时任务跑批。
2.对不同类别不同场景的对象设置离散的过期时间。

如下代码供参考:

1
public String getNameByUserId(@RequestParam(value = "userId") Integer userId){
2
    String name = redisServiceClient.get("marvel:user:"+userId);
3
    //双重检测
4
    if(name==null){
5
        name = redisServiceClient.get("marvel:user:"+userId);
6
        //同步锁 //防止击穿
7
        synchronized (this){
8
            TUser user = userRepository.getOne(userId);
9
            if(user==null){
10
                redisServiceClient.set("marvel:user:"+userId,null,30);//防止穿透
11
            }
12
            redisServiceClient.set("marvel:user:"+userId,name,60*60*24);//24小时过期
13
        }
14
    }
15
    return name;
16
}

1 :老子《道德经》第二十章,老子故里,中国鹿邑。