我们一起来看下redis缓存的常见问题,也是常见的面试题。
这个是一开始决定使用缓存时就该考虑的问题。被缓存的数据在数据源发生变化时需要对缓存进行更新,数据源可能是DB,也可能是远程服务。
当数据源是DB时,可以在更新完DB后直接更新缓存。
当数据源是远程服务时,可能无法及时感知数据变化,这种情况一般选择对缓存数据设置失效期,也就是数据不一致的最大容忍时间。这种情况下,可以选择失效后更新,当key不存在时先请求数据源获取最新数据,然后再次缓存,并更新失效时间。
但是这样做有个问题,如果远程服务在缓存更新期间不可用,那就会导致数据整体不可用。改进的方法是异步更新,即当失效时先不清理数据,继续使用旧数据,然后由异步线程去执行更新任务,这样就避免了失效瞬间的空窗期。还有一种纯异步更新方式,定时对数据进行更新。
同样的,只要使用缓存就要考虑这个问题。缓存数据不一致的原因一般是主动更新失败,例如,更新DB后,在更新Redis时由于网络请求超时等原因造成Redis更新失败,或者是因为异步更新失败导致。
数据一致性可以说是分布式系统中必然存在的问题,可以分为:
1、强一致性:任何时刻都保持一致
2、最终一致性:允许短暂的不一致,但是最后还是一致的
要保持缓存和主存储的强一致性,需要借助复杂的分布式一致性协议,倒不如不用缓存。毕竟缓存的优势还是读多写少的场景,或许写多读少的场景并不适合使用缓存。
解决方法是使用
产生这个问题的原因可能是外部的恶意攻击,例如:对用户信息进行了缓存,但恶意攻击者使用不存在的用户id频繁请求接口,导致查询缓存不命中,就会有大量请求穿透缓存访问到DB,对DB造成压力。
解决方法是:使用BloomFilter过滤器,特点是数据在写入DB的同时将这个ID同步到BloomFilter过滤器中,当请求ID不存在BloomFilter过滤器中则说明数据一定不在DB中,就不要去DB查询了。
就是某个热点数据失效时,大量针对这个数据的请求会穿透到DB。
解决方案:
1、使用锁,当发现缓存失效时,不是立即从DB加载数据,而是让请求去获得分布式锁,获得锁才执行数据库操作,否则等待,这样只让一个请求去操作DB
2、预热,提前把热门数据存入redis,并设置一个超大的过期时间
3、过期时间+随机值,对于热点数据不加过期时间。同时针对多个热点数据同时失效的问题,可以在失效时间加上一个随机数,避免大量热点数据在同一时刻失效。
产生的原因就是缓存挂掉,所有的请求都会击穿到DB。
解决方法:
1、对接口限流、熔断,减少DB压力;
2、构建主从或集群模式来尽量保证Redis服务的高可用。
官方表示,因为Redis是基于内存的操作,CPU不是Redis的瓶颈,瓶颈最有可能是机器内存大小或者网络带宽。既然单线程容易实现,而且CPU不会成为瓶颈,那就顺理成章采用单线程的方案了。
单线程的好处:
a) 完全基于内存操作,绝大多数操作是内存操作,数据在内存中。
b) 数据结构简单,对数据的操作也简单,Redis的数据结构是专门设计的。
c) 采用单线程,避免了不必要的线程上下文切换和竞争条件,也不存在各种加锁的问题,
d) 采用多路IO复用模型,非阻塞IO。
多路指的是多个Socket连接,复用指的是复用同一个线程。Redis基于Reactor模式开发了自己的网络事件处理器—文件事件处理器,文件事件处理器使用IO多路复用程序来同时监听多个socket,并根据socket目前执行的任务来为socket关联不同的事件处理器,当被监听的socket准备好执行连接应答、读取、写入、关闭等操作时,与操作相对应的文件事件就会产生,此时文件事件处理器就会调用socket关联好的事件处理器来处理这些事件。
redis常见问题及面试题持续更新中。。。