Redis的缓存穿透、缓存击穿、缓存雪崩原因以及解决方案
缓存穿透
下图是包含大多数缓存场景的一个数据请求图,一个用户请求进来,首先会根据对应的key去请求缓存,如果命中缓存,则直接会返回缓存中的数据给用户,不会查询数据库,如果没有命中缓存的话,这时候会去请求数据库,如果查询到对应的数据,放入缓存中,并将请求内容进行返回。
问题梳理【缓存穿透】
这时候看下图红色的这条线,如果一个数据不存在的时候,每次访问否会通过这条线进入数据库,这时候如果有居心叵测的人,利用这个key进行恶意访问,那么对于我们的数据库来说是一个巨大的安全隐患,可能会扛不住压力,宕机。
缓存穿透是针对多个key的,并不一定是一个key带来的问题,缓存穿透具体的请求问题如下图所示:
解决方案【缓存穿透】
- 布隆过滤器
布隆过滤器它实际上是一个很长的二进制向量和一系列随机映射函数。他可以告诉我们什么东西一定不存在,什么东西可能存在
以Redis中的布隆过滤器实现为例,Redis中的布隆过滤器底层是一个大型位数组(二进制数组)+多个无偏hash函数。一个大型位数组(二进制数组):
多个无偏hash函数:
无偏hash函数就是能把元素的hash值计算的比较均匀的hash函数,能使得计算后的元素下标比较均匀的映射到位数组中。
布隆过滤器的优点和缺点
优点:时间复杂度低,节约内存空间,布隆过滤器的数据也是存储在内存中的
缺点:
- 误判率,hash冲突产生的问题,无法彻底解决,可以调整参数,增加hash函数,3次hash增加至5次,可以降低误判率
- 数据不一致,他和数据库的数据是两个数据源,会在特殊情况下造成双方不一致问题,比如在同步key到布隆过滤器的时候,发生意外情况,没有同步过去,用户在请求的时候就会请求不到,这个在某些特定的业务是无法忍受的
- 布隆过滤器对元素的删除不太支持,目前有一些变形的特定布隆过滤器支持元素的删除!关于为什么对删除不太支持,其实也非常好理解,hash冲突必然存在,删除肯定是很苦难的!
保密性强(不存储数据本身,但是本人觉得都是自己判断key,自己搞,中间不会涉及到外部参与,所有这个优点不算很优,甚至算是一个缺点,因为并不能拿到数据本身,导致多一次判断请求)
- 缓存空值
如果数据库中不存在对应的数据,直接返回空值给缓存 - 校验参数
在数据请求到缓存时,对数据进行业务上的规则判断,例如请求数据的长度,前缀等
实例:系统数据:user:1111111111
请求数据:1482149712
这样的数据我们就可以直接在前面进行拦截并返回非法请求(403:forbidden)
缓存击穿
大量的用户请求同一个热点,但该热点在缓存中失效了,一下子这些用户请求都直接怼到数据库,可能会造成瞬间数据库压力过大,而直接挂掉
问题梳理【缓存击穿】
关键在于某个热点的key失效了,导致大并发集中打在数据库上。所以要从两个方面解决,第一是否可以考虑热点key不设置过期时间,第二是否可以考虑降低打在数据库上的请求数量。
解决方案【缓存击穿】
1.加锁(降低访问数据库的量)
数据库压力过大的根源是,因为同一时刻太多的请求访问了数据库。
如果我们能够限制,同一时刻只有一个请求才能访问某个热点信息,其他的请求先自旋等待,不就可以解决这个问题了吗?
2.缓存不失效(热点过期时间设置)
对于部分热点key,其实是可以不用设置过期时间,让其永久有效的,等待业务量下来之后,这块用不上了,再把这部分删除掉,例如说秒杀活动
3.自动续期(热点过期时间设置)
每隔一段时间增加缓存的失效时间,比如说缓存10小时过期,那我们每五个小时去循环增加过期时间,这样每次过期前,都能保证有效增长时间,这个与缓存不失效的效果差不多,唯一的优点就是等到业务巅峰过了,不用我们自己手动去删除缓存,直接停用这个定时任务即可。
缓存雪崩
某一个时刻出现大规模的缓存失效的情况,那么就会导致大量的请求直接打在数据库上面,导致数据库压力巨大,如果在高并发的情况下,可能瞬间就会导致数据库宕机。
问题梳理【缓存雪崩】
有两种情况会出现缓存雪崩:
1.大量缓存同时失效
2.缓存服务宕掉,整个缓存直接失效
解决方案【缓存雪崩】
1.过期时间设置随机数(针对情况1)
2.高可用-哨兵模式、集群模式(针对情况2)