面试题
了解什么是Redis
的雪崩、穿透和击穿?Redis
崩溃之后会怎么样?系统该如何应对这种情况?如何处理Redis
的穿透?
知识点
Redis缓存雪崩
Redis
雪崩又称缓存雪崩,简单来说就是某个时间点,大量的Key失效,导致大量的请求从缓存中获取不到数据而去请求数据库。
上图是一个简单的带有Redis
缓存的业务访问逻辑,如果Redis
缓存失效那么访问者的访问请求全部会发送到最后的数据库上,如果是高并发的场景,数据库服务器势必会出现宕机的情况。这种情况下就算是重启数据库服务也是没有任何作用的,因为即使重启了,大流量的情况还是没有解决,所以还会继续宕机。
Redis缓存雪崩解决方案
加随机值
解决问题之前要知道问题的原因是什么,原因上面有说到缓存雪崩是由于某个节点大量的Key失效而导致的问题,所以由此出发,解决出现某一节点大量Key出现失效的问题。
最为直接的就是将Key的过期时间分散开。也就是在设置Key的过期时间的时候增加一个随机值。
加锁
加锁这个方案是一个不具有适用性的方案,在我看来的话,仅仅是当做问题出现了,雪崩出现了,为了避免数据库宕机出现更大的损失的一个减小损失的办法。不过这种说法到底能不能用我并不敢保证,毕竟我没有做过这个东东。
加锁方案的流程是在多个请求同时到达业务系统的时候,只有一个线程能够获取到锁,然后才能继续去缓存或者是数据库中查询数据,然后后面的流程和之前一样,执行完释放锁,然后其他线程再争抢锁,然后重复流程。
这个流程好处就是能够保护数据库不会被打挂掉,缺点是并发极低,当做应急方案去做吧。
不过这个方案还是有优化的余地,下面的是优化的,后面Redis到数据库的请求可以配合线程池之类的其他手段,只要能够控制到一定的并发数即可。
面试应该怎么答
缓存雪崩的事前事中事后的解决方案如下:
-
事前:
redis
高可用,主从+哨兵,redis cluster
,避免全盘崩溃. -
事中:本地
ehcache
缓存 +hystrix
限流&降级,避免 MySQL 被打死。 -
事后:
redis
持久化,一旦重启,自动从磁盘上加载数据,快速恢复缓存数据。
用户发送一个请求,系统 A 收到请求后,先查本地 ehcache 缓存,如果没查到再查 redis。如果 ehcache 和 redis 都没有,再查数据库,将数据库中的结果,写入 ehcache 和 redis 中。
限流组件,可以设置每秒的请求,有多少能通过组件,剩余的未通过的请求,怎么办?走降级!可以返回一些默认的值,或者友情提示,或者空白的值。
这样做有哪些好处好处:
- 数据库绝对不会死,限流组件确保了每秒只有多少个请求能通过。
- 只要数据库不死,就是说,对用户来说,2/5 的请求都是可以被处理的。
- 只要有 2/5 的请求可以被处理,就意味着你的系统没死,对用户来说,可能就是点击几次刷不出来页面,但是多点几次,就可以刷出来一次。
Redis缓存击穿
在某个时间节点,某个Key一直扛着大量的请求,在后续的某个时间点Key突然失效,那么大量的请求则不能在缓存中请求到数据,这些请求会转而去请求数据库,那么数据库就有可能出现因处理不了大量请求而宕机的情况,这种现象就叫Redis
缓存击穿。
Redis缓存击穿解决方案
这个还相对简单一点,如果说某个Key比较受欢迎,请求量比较大,那么就可以不设置过期时间,如果该key的数据更新了,那么就通过互斥锁的方式将其更新。
那什么是互斥锁?共享资源的使用是互斥的,即一个线程获得资源的使用权后就会将该资源加锁,使用完后会将其解锁,如果在使用过程中有其他线程想要获取该资源的锁,那么它就会被阻塞陷入睡眠状态,直到该资源被解锁才会被唤醒,如果被阻塞的资源不止一个,那么它们都会被唤醒,但是获得资源使用权的是第一个被唤醒的线程,其它线程又陷入沉睡。
互斥锁在各个编程语言中定义其实不太相同,大多数语言中,互斥锁使用线程来实现,假如现在锁被锁住了,那么后面的线程会进入"休眠"状态,直到解锁之后,又会唤醒线程继续执行,这也叫空等待sleep-waiting
。但是严格来说,只要是同一时刻只能被拿到一次的所都叫互斥锁。
为什么要用互斥锁的方式?上面也有说到,互斥锁同一时刻只能被拿到一次,那么它可以避免数据更新是出现数据不一致的情况,这里使用互斥锁可以保证缓存和数据库的一致性,当然会牺牲一点效率。
Redis缓存穿透
某个不存在的Key一直被访问,结果发现数据库中也没有这样的数据,最终导致访问该Key的所有请求都直接请求到数据库,相当于每次的请求都"视缓存于无物",如果是高并发的场景,那么数据库极容易被打垮。
Redis缓存穿透解决方案
缓存空数据
假设某个Key数据并不存在,那么对应Key久存一个NULL,但是要设置过期时间,假设ID=3
的记录本来不存在,然后本次访问没有查询到数据,缓存中存的是NULL如果过段时间之后新增了一条记录为ID=3
的数据,缓存不设置过期时间,那么这条数据就永远获取不到。
布隆过滤器
什么是布隆过滤器?Bloom Filter,它是一种数据结构,更准确的是一种概率性的数据结构,它实际上是一个很长的二进制向量和一系列随机映射函数,能够判断某个元素一定不存在或者可能存在。
在初始状态时,对于长度为m的位数组,它的所有位都被置为0,当有变量被加入集合时,通过K个映射函数将这个变量映射成位图中的K个点,把它们置为 1,查询某个变量的时候我们只要看看这些点是不是都是 1 就可以大概率知道集合中有没有它了,如果这些点有任何一个 0,则被查询变量一定不在;如果都是 1,则被查询变量很可能存在。
通常我们会遇到很多要判断一个元素是否在某个集合中的业务场景,一般想到的是将集合中所有元素保存起来,然后通过比较确定。链表、树、散列表(又叫哈希表,Hash table)等等数据结构都是这种思路。但是随着集合中元素的增加,我们需要的存储空间也会呈现线性增长,最终达到瓶颈。同时检索速度也越来越慢,上述三种结构的检索时间复杂度分别为$O(n)$,$O(logn)$,$O(1)$。在这种状况下布隆过滤器应运而生。
了解了这些基本上方案思路就出来了。通过布隆过滤器判断请求的key是否存在,如果可能存在再去数据库查询,如果布隆过滤器中不存在那么就需要再去数据库查询了。
其他知识点
缓存预热
缓存预热就是将一些可能经常使用的数据在系统启动的时候预先设置到缓存中,这样可以避免在使用到的时候先去数据库中查询。还有一种方式就是添加一个缓存刷新页,这样通过人工干预的方式将一些可能为热点的key添加到缓存中。
缓存降级
当访问量突然剧增、服务出现问题或非核心服务影响到核心流程的性能时,仍然需要保证服务还是可用的,即使是有损服务。系统可以根据一些关键数据进行自动降级,降级的最终目的是保证核心服务可用,即使是有损的。但是有的一些业务的核心服务是不能降级的。这是一种丢卒保帅的思想。
Comments | NOTHING