Redis - 缓存穿透以及缓存雪崩

Redis - 缓存穿透以及缓存雪崩

1.缓存收益以及代价

 当我们使用一项技术时,我们就需要对它有一定的了解,知道我们为什么要去使用它,能够分析使用这项技术所带来的的回报以及我们所需要付出的代价。
 缓存所带来的收益:

  1. 高速读写:缓存会加速读写速度,利用CPU L1/L2/L3 Cache、Linux page Cache加速硬盘读写、浏览器缓存、Ehcache缓存缓存数据,其性能都会比关系型数据库高很多,内存级别的读写性能大大优于磁盘级别的读写性能。
  2. 降低后端负载:后端服务器业务通过使用Redis减少对MySQL的请求访问,降低了MySQL负载等。

 缓存所带来的代价:

  1. 数据不一致:缓存层和数据库层面数据不一致,例如Redis已经对某条复杂的数据进行缓存,此时通过后台修改该数据,由于大部分情况下我们不会主动对Redis进行数据删除,因为从性能以及包容性来说都不需要进行删除操作,所以就导致缓存数据同步不及时,这些都和我们自身应用更新策略有关,需要根据实际情况合理设置数据缓存过期时间等相关操作。
  2. 代码维护成本(人工成本):不适用Redis缓存的情况下,我们只需读写MySQL就能实现功能,但当我们加入缓存之后就需要去维护缓存数据的处理逻辑,增加了代码复杂度。某些情况下会降低项目开发以及测试效率,例如测试人员需要测试有缓存时的情况,也要测试没有缓存时的情况,另外当测试人员测试功能无需关注缓存时需手动清除对应缓存,否则需要等待数据缓存过期,延长测试时间。
  3. 性能风险:堆内缓存由于是存储在本地服务器中,由JVM或者本地服务器来维护数据,可能带来内存溢出的风险甚至影响用户进程,如ehCacheloadingCache

 堆内缓存和远程服务器缓存如何选择?对于这个问题主要考虑以下几点:

  1. 堆内缓存一般性能更好,远程缓存需要套接字传输。
  2. 用户级别缓存尽量采用远程缓存(例如集群架构下,多台机器会重复缓存同一组数据)。
  3. 大数据量尽量采用远程缓存,避免造成应用服务器压力过大,遵循服务节点化原则。

2.缓存穿透

 在我们使用Redis的过程中,Redis的确帮助我们解决了很多的问题,但是当技术和业务结合在一起时就会发现一些让人深思的问题。缓存穿透就是其中一个。

2.1 什么是缓存穿透?

在这里插入图片描述

 上面这张图是我们正常业务场景下的一个流程(客户端->服务端[Redis->DB]),那么缓存穿透指的是当用户查询数据,再缓存中不存在,并且在数据库也不存在时,导致用户查询这样的数据(或者恶意攻击) 在缓存中找不到对应key的value,每次都需要要在数据库中查询一遍,然后返回空值。其实这就相当于进行了两次无用的查询,这样请求就绕过缓存直接查数据库,对数据库造成了很大的压力。

2.2 如何防止缓存穿透?

 说到如何防止缓存穿透,这里我主要提出两点建议:

  1. 缓存空值:如果DB查询返回数据或者业务结果为空,此时我们仍然将空结果进行缓存,设置较短的过期时间(不超过五分钟)。
  2. 采用布隆过滤器BloomFilter:事先将所有可能存在的数据哈希后放入到一个足够大的BitMap中,若一个数据一定不存在则会被拦截掉,从而避免了对底层存储系统的查询压力。关于布隆过滤器后面会写一篇博客对其进行详细介绍。
    在这里插入图片描述

3.缓存雪崩

3.1 什么是缓存雪崩?

 如果缓存集中在一段时间内失效,发生大量的缓存穿透,所有查询都落在数据库上,就会造成了缓存雪崩。 由于原有缓存失效,新缓存未到期间所有原本应该访问缓存的请求都需要去查询数据库,从而对数据库服务CPU和内存造成巨大压力,严重可能会造成数据库服务宕机。

3.2 如何防止缓存雪崩?

 这里我们看看如何防止缓存雪崩:

  1. 加锁排队:维护一个mutex互斥锁,通过RedisSETNX命令去设置一把当前业务操作的锁(例如setnx lock_uid_001 value nullValue、setnx lock_white_list value nullValue),当操作成功时,再进行LoadDB操作回设缓存,否则重试get缓存。
  2. 数据预热:缓存预热就是当我们新部署一个项目系统时,将相关缓存数据直接加载到缓存中。这样可以避免在用户请求时候,再去查询数据库放入缓存。用户可以直接查询事先被预热的缓存数据,可以预热大并发量、热度高的Key缓存数据。
  3. 双层缓存策略:同一条数据我们可以维护两套缓存,C1为原始缓存,C2为拷贝缓存,当C1失效时,可以读取C2,都不存在时再去访问数据库回设,当然C1缓存失效时间需要设置为短期,而C2设置为长期。这种方式最大缺点就是占用的内存翻倍。
  4. 定时更新缓存策略:对于失效性要求低的缓存,可以在容器启动初始化时加载放入缓存,采用定时任务更新或移除缓存。
  5. 时间浮动策略:设置不同过期时间,让缓存失效的时间点尽量均匀,避免集中失效。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章