专栏名称: CSDN
CSDN精彩内容每日推荐。我们关注IT产品研发背后的那些人、技术和故事。
目录
相关文章推荐
51好读  ›  专栏  ›  CSDN

常说的「缓存穿透」和「击穿」是什么

CSDN  · 公众号  · 科技媒体  · 2020-12-15 17:44

正文

请到「今天看啥」查看全文


为了你们能更懂流程,作为暖男的我还是一如既往的给你们准备了伪代码啦:

public String getData(String key){    String data = redisTemplate.opsForValue().get(key);    if (StringUtils.isNotEmpty(data)){        return data;    }    String lockKey = this.getClass().getName() + ":" + key;    RLock lock = redissonClient.getLock(lockKey);    try {        boolean boo = lock.tryLock(5, 5, TimeUnit.SECONDS);        if (!boo) {            // 休眠一会儿,然后再请求            Thread.sleep(200L);            data = getData(key);        }        // 读取数据库的数据        data = getDataByDB(key);        if (StringUtils.isNotEmpty(data)){            // 把数据构建到缓存中            setDataToRedis(key,data);        }    } catch (InterruptedException e) {        // 异常处理,记录日志或者抛异常什么的    }finally {        if (lock != null && lock.isLocked()){            lock.unlock();        }    }    return data;}

当然,采用互斥锁的方案也是有缺陷的,当缓存失效的时候,同一时间只有一个线程读数据库然后回写缓存,其他线程都处于阻塞状态。如果是高并发场景,大量线程阻塞势必会降低吞吐量。这种情况该如何处理呢?我只能说没什么设计是完美的,你又想数据一致,又想保证吞吐量,哪有那么好的事,为了系统能更加健全,必要的时候牺牲下性能也是可以采取的措施,两者之间怎么取舍要根据实际业务场景来决定,万能的技术方案什么的根本不存在。


缓存雪崩


缓存雪崩也是 key 失效后大量请求打到数据库的异常情况 ,不过,跟缓存击穿不同的是,缓存击穿因为指一个热点 key 失效导致的情况,而 缓存雪崩是指缓存中 大批量的数据同时过期 ,巨大的请求量直接落到 db 层,引起 db 压力过大甚至宕机 ,这也符合字面上的“雪崩”说法。

解决方案

缓存雪崩的解决方案和击穿的思路一致,可以 设置 key 不过期或者互斥锁 的方式。

除此之外,因为是 预防大面积的 key 同时失效,可以给不同的 key 过期时间加上随机值,让缓存失效的时间点尽量均匀 ,这样可以保证数据不会在同一时间大面积失效。

redisTemplate.opsForValue().set(Keyvaluetime + Math.random() * 1000, TimeUnit.SECONDS); 

同时还可以 结合主备缓存策略来让互斥锁的方式更加的可靠。

主缓存: 有效期按照经验值设置,设置为主读取的缓存,主缓存失效后从数据库加载最新值。

备份缓存: 有效期长,获取锁失败时读取的缓存,主缓存更新时需要同步更新备份缓存。

一般来说,上面三种缓存异常场景问的比较多,了解这几种基本就够了,但有些面试官可能喜欢剑走偏锋,进一步延伸其他的异常情景做询问,以防万一,我们也加个菜,介绍下另外两种常见缓存异常。


缓存预热


缓存预热就是 系统上线后,先将相关的数据构建到缓存中,这样就可以避免用户请求的时候直接查库。

这部分预热的数据主要取决于 访问量和数据量大小。 如果数据的访问量不大的话,那么就没必要做预热,都没什么多少请求了,直接按正常的缓存读取流程执行就好。

访问量大的话,也要看数据的大小来做预热措施。

  1. 数据量不大的时候,工程启动的时候进行加载缓存动作,这种数据一般可以是电商首页的运营位之类的信息;

  2. 数据量大的时候,设置一个定时任务脚本,进行缓存的刷新;

  3. 数据量太大的时候,优先保证热点数据进行提前加载到缓存,并且确保访问期间不能更改缓存,比如用定时器在秒杀活动前30分钟就把商品信息之类的刷新到缓存,同时规定后台运营人员不能在秒杀期间更改商品属性。


缓存降级


缓存降级是 指缓存失效或缓存服务器挂掉的情况下,不去访问数据库,直接返回默认数据或访问服务的内存数据。

在项目实战中通常会将部分热点数据缓存到服务的内存中,类似 HashMap、Guava 这样的工具,一旦缓存出现异常,可以直接使用服务的内存数据,从而避免数据库遭受巨大压力。

当然,这样的操作对于业务是有损害的, 分布式系统中很容易就出现数据不一致的问题 ,所以,一般这种情况下,我们都优先保证从运维角度确保缓存服务器的高可用性。比如 Redis 的部署采用集群方式,同时做好备份。总之,尽量避免出现降级的影响。


最后


关于缓存的几大异常处理我们就讲解到这了。虽然每种异常我们都给出了解决的方案,但不是说这玩意直接套上就能用了。现实开发过程中还是要根据实际情况来针对缓存做相应措施,比如用布隆过滤器预防缓存穿透虽然很有效,但并不算特别常用。这年头, 防止恶意攻击什么的都是先在运维层面做限制,业务代码层面更多的是对参数和数据做校验。

如果每个使用缓存的地方都要考虑的这么复杂的话,那工作量无疑会更加繁杂,过度设计只会让代码维护起来也麻烦,而且实用性还不一定强,没必要啊。程序员嘛,给自己增添烦恼的事情越少越好,毕竟我们最大的敌人不是996,而是那珍贵的发量啊。


更多精彩推荐






请到「今天看啥」查看全文