回顾一下咱们为何要用缓存(Redis):面试
如今有个问题,若是咱们的缓存挂掉了,这意味着咱们的所有请求都跑去数据库了。redis
在前面学习咱们都知道Redis不可能把全部的数据都缓存起来(内存昂贵且有限),因此Redis须要对数据设置过时时间,并采用的是惰性删除+按期删除两种策略对过时键删除。数据库
若是缓存数据设置的过时时间是相同的,而且Redis刚好将这部分数据所有删光了。这就会致使在这段时间内,这些缓存同时失效,所有请求到数据库中。设计模式
这就是缓存雪崩:缓存
缓存雪崩若是发生了,极可能就把咱们的数据库搞垮,致使整个服务瘫痪!架构
对于“对缓存数据设置相同的过时时间,致使某段时间内缓存失效,请求所有走数据库。”这种状况,很是好解决:并发
对于“Redis挂掉了,请求所有走数据库”这种状况,咱们能够有如下的思路:分布式
好比,咱们有一张数据库表,ID都是从1开始的(正数):ide
可是可能有黑客想把个人数据库搞垮,每次请求的ID都是负数。这会致使个人缓存就没用了,请求所有都找数据库去了,但数据库也没有这个值啊,因此每次都返回空出去。高并发
缓存穿透是指查询一个必定不存在的数据。因为缓存不命中,而且出于容错考虑,若是从数据库查不到数据则不写入缓存,这将致使这个不存在的数据每次请求都要到数据库去查询,失去了缓存的意义。
这就是缓存穿透:
缓存穿透若是发生了,也可能把咱们的数据库搞垮,致使整个服务瘫痪!
解决缓存穿透也有两种方案:
上面讲缓存穿透的时候也提到了:若是从数据库查不到数据则不写入缓存。
通常咱们对读操做的时候有这么一个固定的套路:
若是仅仅查询的话,缓存的数据和数据库的数据是没问题的。可是,当咱们要更新时候呢?各类状况极可能就形成数据库和缓存的数据不一致了。
从理论上说,只要咱们设置了键的过时时间,咱们就能保证缓存和数据库的数据最终是一致的。由于只要缓存数据过时了,就会被删除。随后读的时候,由于缓存里没有,就能够查数据库的数据,而后将数据库查出来的数据写入到缓存中。
除了设置过时时间,咱们还须要作更多的措施来尽可能避免数据库与缓存处于不一致的状况发生。
通常来讲,执行更新操做时,咱们会有两种选择:
首先,要明确的是,不管咱们选择哪一个,咱们都但愿这两个操做要么同时成功,要么同时失败。因此,这会演变成一个分布式事务的问题。
因此,若是原子性被破坏了,可能会有如下的状况:
若是第一步已经失败了,咱们直接返回Exception出去就行了,第二步根本不会执行。
下面咱们具体来分析一下吧。
操做缓存也有两种方案:
通常咱们都是采起删除缓存缓存策略的,缘由以下:
基于这两点,对于缓存在更新时而言,都是建议执行删除操做!
正常的状况是这样的:
若是原子性被破坏了:
若是在高并发的场景下,出现数据库与缓存数据不一致的几率特别低,也不是没有:
要达成上述状况,仍是说一句几率特别低:
由于这个条件须要发生在读缓存时缓存失效,并且并发着有一个写操做。而实际上数据库的写操做会比读操做慢得多,并且还要锁表,而读操做必需在写操做前进入数据库操做,而又要晚于写操做更新缓存,全部的这些条件都具有的几率基本并不大。
对于这种策略,实际上是一种设计模式:Cache Aside Pattern
删除缓存失败的解决思路:
正常状况是这样的:
若是原子性被破坏了:
看起来是很美好,可是咱们在并发场景下分析一下,就知道仍是有问题的了:
因此也会致使数据库和缓存不一致的问题。
并发下解决数据库与缓存不一致的思路:
咱们能够发现,两种策略各自有优缺点:
Cache Aside Pattern
设计模式)
能够用databus或者阿里的canal监听binlog进行更新。
这是几道Redis常见的面试题,但愿你们看完有所帮助,顺利拿到offer!