Redis缓存查询(防止缓存穿透)原因以及解决方法
前言
缓存击穿产生的场景?
为了解决内存与磁盘速度不匹配的问题,就要用到缓存。将热点数据存放在redis中随用随取,不仅可以降低连接到数据库的请求,避免数据库挂掉;还可以减少频繁重复的复杂逻辑结果。需要注意的是,无论是击穿、穿透与雪崩,都是在高并发前提下。
什么是缓存击穿? 缓存击穿就是在处于集中式高并发访问的情况下,当某个热点 key 在失效的瞬间,大量的请求在缓存中获取不到。瞬间击穿了缓存,所有请求直接打到数据库,就像是在一道屏障上击穿了一个洞,单一使用数据库来保存数据的系统会因为面向磁盘,磁盘读/写速度比较慢的问题而存在严重的性能弊端,一瞬间成千上万的请求到来,需要系统在极短的时间内完成成千上万次的读/写操作,这个时候往往不是数据库能够承受的,极其容易造成数据库系统瘫痪,最终导致服务宕机的严重生产问题。 为了克服上述的问题,项目通常会引入NoSQL技术,这是一种基于内存的数据库,并且提供一定的持久化功能。 redis技术就是NoSQL技术中的一种,但是引入redis又有可能出现缓存穿透,缓存击穿,缓存雪崩等问题。
示意图
-
缓存穿透:key对应的数据在数据源并不存在,每次针对此key的请求从缓存获取不到,请求都会到数据源,从而可能压垮数据源。比如用一个不存在的用户id获取用户信息,不论缓存还是数据库都没有,若黑客利用此漏洞进行攻击可能压垮数据库。 缓存击穿:key对应的数据存在,但在redis中过期,此时若有大量并发请求过来,这些请求发现缓存过期一般都会从后端DB加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把后端DB压垮。 缓存雪崩:当缓存服务器重启或者大量缓存集中在某一个时间段失效,这样在失效的时候,也会给后端系统(比如DB)带来很大压力。
解决办法
在方法内加入同步代码块,如果从缓存中无法读取,限制访问,只允许一个请求进入方法,通过数据库查询数据,将数据卡在服务端,防止瞬时访问数据库压力 下面只讲一种数据类型,大致思路很好理解
@Autowired private StringRedisTemplate redisTemplate; @Autowired private UserMapper userMapper; public <?> getResourceByConsumer(String consumerId){ try { if(redisUtil.hasKey(consumerId)){ String demoValue= redisTemplate.opsForValue().get(demoKey); return demoValue; }else { synchronized (this){ String message=userMapper.getMessage(consumerId); redisTemplate.opsForValue().set("message-"+consumerId,message); return message; } }catch (Exception e){ } }