“唯之与阿,相去几何?
善之与恶,相去何若?
人之所畏,不可不畏。
荒兮,其未央哉!
众人熙熙,如享太牢,如春登台。
我独泊兮,其未兆,如婴儿之未孩;傫傫兮,若无所归!
众人皆有余,而我独若遗。
我愚人之心也哉!”1
如何应对Redis在缓存应用中的穿透、击穿以及雪崩问题?
Redis在做缓存时,通常在代码逻辑上会做如下处理:
- 查询缓存
- 缓存有,则返回;缓存没有则查询数据库;
- 查询数据库,返回;并放入缓存
问题
问题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 :老子《道德经》第二十章,老子故里,中国鹿邑。