使用缓存时,会存在缓存雪崩,缓存击穿和缓存穿透等异常情况,下面分析这些缓存异常和常见的处理方案。
缓存雪崩
(1) 什么是缓存雪崩
缓存雪崩:一段时间内大量请求透过缓存,发送到数据库或者下一层缓存进行处理,导致对数据库和下一层缓存的访问压力增大,严重时可能导致数据库和下一层缓存崩溃,从而导致整个系统崩溃,就像雪崩一样,引发连锁效应。
(2) 缓存雪崩的原因
- 大量缓存同时过期,导致本应请求到缓存的需重新从数据库或者下一层缓存中获取数据
- 缓存本身故障,例如redis出现故障,无法处理请求,导致请求到数据库中
(3) 缓存雪崩的解决方案
- 设置缓存过期时间时,应当避免大量key同时过期的场景,可以通过随机,微调等方式设置过期时间,避免统一时间过期
- 双key策略,主key是原始缓存,备key是拷贝缓存,当主key失效时,可以访问备key,主key缓存时间比备key缓存时间短
- 后台更新缓存,采用定时任务或者消息队列的方式进行缓存更新或移除
- 使用互斥锁,构建缓存的操作不会在同一时间进行
- 高可用架构,例如redis可以使用哨兵模式或者集群模式,避免出现单节点故障导致整个redis服务不可用
- 使用服务降级,可以返回一些默认数据,保证服务有损但可用
缓存击穿
(1) 什么是缓存击穿
缓存击穿:缓存击穿指大量并发用户同时请求缓存中不存在但数据库或下一级缓存中有的数据,也就是同时读缓存没读到数据,又同时请求数据库或下一级缓存,引起数据库和下一级缓存压力瞬间增大。缓存击穿和缓存雪崩不同的是,缓存击穿指得是并发差同一条数据,缓存雪崩是不同数据都过期,很多数据查询不到从而查询数据库或下一级缓存。
(2) 缓存击穿的原因
- 缓存击穿主要是某个热点数据过期,因为是热点数据,请求并发量又大,所以过期时会有大量请求同时过来,来不及更新缓存就全部打到数据库或下一级缓存了。
(3) 缓存击穿的解决方案
- 热点数据不设置过期时间,后续如果想清理,可以通过后台进行清理
- 使用互斥锁,当数据过期时,除了第一个查询请求可以获取到锁请求数据库或下一级缓存,并更新到缓存中,其他的请求会被阻塞住,直到锁释放。同时新的缓存也被更新到缓存中,后续的请求可以直接请求到缓存上,而不会出现缓存击穿。
- 使用自动续期,在key要过期之前,通过定时任务自动给缓存续期
缓存穿透
(1) 什么是缓存穿透
缓存穿透:缓存穿透指访问不存在的数据,即数据不在缓存中,也不在数据库和下一级缓存中,导致每次请求时,在缓存中找不到对应的key之后,每次再去数据库和下一级缓存中查询一遍,相当于进行了多次无用的查询。
(2) 缓存穿透的原因
原因:相关数据不存在,或者外部恶意攻击
(3) 缓存穿透的解决方案
- 非法请求时,通过参数检验,鉴权检验等,从一开始将大量非法请求拦截
- 缓存空值或者默认值,当从缓存和数据库中获取不到数据时,可以将空结果进行缓存,同时设置一个较短的过期时间,通过将默认值存放到缓存中,第二次请求时缓存中就有值了,从而不会继续访问数据库和下一级缓存。
- 使用布隆过滤器快速判断数据是否存在。布隆过滤器通过引入多个相互独立的哈希函数,保证在给定的空间和误判率下,完成元素判重。该方法的优点是空间效率高,查询时间短,缺点是存在一定的误识别率。如果在布隆过滤器中查询没有该数据,那么一定不存在该数据;如果存在该数据,可能会有一定的识别错的概率。
缓存预热
缓存预热是在系统上线前后,将相关的缓存数据直接加载到缓存系统中,而不依赖用户访问时加载。这样可以避免在用户请求时,出现大量先查询缓存,再查询数据库或下一级缓存,最后存储缓存的情况。用户直接查询事先预热的缓存数据,可以避免系统上线初期,对于高并发流量,都会访问到数据库中,对数据库或下一级缓存造成流量压力。
缓存预热可以在系统启动时自动进行加载,也可以后台定时刷新,也可在数据量较大时,只对热点数据进行预加载缓存。
缓存降级
缓存降级是指在缓存失效或者缓存服务出现问题时,为了防止缓存服务故障,导致数据库和下一级缓存一起发生雪崩,而不去访问数据库和下一级缓存,可以使用缓存降级策略,保证服务基本可用,但是会有一些损失(例如缓存数据不准确)。
缓存降级可以直接放回系统设置的默认值,或者返回内存部分的数据缓存。