缓存的注意点

我们前面花了很多篇幅讲解 redis 的实现原理,并且也自己手写实现了一些基础功能。

你在工作学习中肯定或多或少都接触过 redis 等缓存,本篇对缓存使用的注意点做一个入门介绍。

内容相对轻松简单,请放心食用。

缓存大大提升了我们的查询性能,但是使用不慎很可能掉到坑里。

本文介绍下缓存雪崩、缓存击穿和缓存穿透这三个挖坑的小能手,让你知道如何避免这几个坑,避免加班修BUG。

缓存雪崩

当雪崩时,没有一片雪花认为自己有错误,甚至还想勇闯天涯。

问题描述

缓存雪崩是指缓存不可用或者大量缓存由于超时时间相同在同一时间段失效,大量请求直接访问数据库,数据库压力过大导致系统雪崩。

雪崩

常见场景

我们很多业务代码中,都喜欢在凌晨定时清空缓存,很容易导致数据库压力飙升。

或者是缓存服务挂掉了,这些墨菲定律的悲催事件。

现实总是比电影还有戏剧性,我们公司去年光纤就被施工队伍挖断过。orz

如何避免

为了避免这个问题,我们采取下面的手段:

(1) 增加缓存系统可用性,通过监控关注缓存的健康程度,根据业务量适当的扩容缓存。

这个现在很多公司都可以做到了,高可用天天可不是白说的。

实在不行直接买阿里云的 redis 服务用起来也是省心省力。

(2)采用多级缓存,不同级别缓存设置的超时时间不同,即使某个级别缓存都过期,也有其他级别缓存兜底。

这个一般不太建议使用,除非并发特别高的业务场景,因为多级缓存会提升技术复杂度,并且带来数据一致性的维护等问题。

需要考虑自己团队的技术水平,或者由公司的中间件团队统一沉淀维护也可以。

(3)缓存的 Key 值可以取个随机值,比如以前是设置 10 分钟的超时时间,那每个 Key 都可以随机 8-13 分钟过期,尽量让不同 Key 的过期时间不同。

这个主要针对我们定时清空缓存的场景,推荐都是逐渐清空,而不是一把梭哈。

记得给缓存一个喘气的机会,也多给自己喝咖啡的机会。

咖啡

缓存击穿

从来都是双喜临门,祸不单行。

问题描述

对于某些 Key 设置了过期时间,但是它是热点数据,如果某个 Key 失效,可能大量的请求打过来,缓存未命中,然后去数据库访问,此时数据库访问量会急剧增加。

问题分析

热点数据不可改变的情况下,我们可以改进的点就有两个部分:

(1)缓存的热点数据失效

(2)大量的请求同时打过来

解决手段

为了避免这个问题,我们可以采取下面的两个手段:

(1)加分布式锁

加载数据的时候可以利用分布式锁锁住这个数据的 Key,在 Redis 中直接使用 SetNX 操作即可。

对于获取到这个锁的线程,查询数据库更新缓存,其他线程采取重试策略,这样数据库不会同时受到很多线程访问同一条数据。

ps: 说到分布式锁,我们后续将花2-3节重点讲解下。

(2)异步加载

由于缓存击穿是热点数据才会出现的问题,可以对这部分热点数据采取到期自动刷新的策略,而不是到期自动淘汰。

淘汰也是为了数据的时效性,所以采用自动刷新也可以。

是的,如果能刷新,何必淘汰呢?

缓存穿透

就像是丢盔卸甲的士兵立在战场之上。

战场

问题描述

缓存穿透是指查询的数据在数据库是没有的,那么在缓存中自然也没有,所以在缓存中查不到就会去数据库查询,这样的请求一多,我们数据库的压力自然会增大。

问题分析

最简单的思路就是避免这种不存在的数据区查询数据库。

但是如何避免呢?

解决方案

为了避免这个问题,可以采取下面两个手段:

NULL 的依然缓存

约定:对于返回为 NULL 的依然缓存,对于抛出异常的返回不进行缓存,注意不要把抛异常的也给缓存了。

采用这种手段会增加我们缓存的维护成本,需要在插入缓存的时候删除这个空缓存,当然我们可以通过设置较短的超时时间来解决这个问题。

过滤器

制定一些规则过滤一些不可能存在的数据,小数据用 BitMap,大数据可以用布隆过滤器。

比如你的订单 ID 明显是在一个范围 1-1000,如果不是 1-1000 之内的数据那其实可以直接给过滤掉。

比如黑名单,最常用的解决方案就是布隆过滤器。

你问我啥是布隆过滤器?这个我们下一节详细谈一谈。

小结

本文主要讲解了缓存使用中的三个常见的坑:缓存雪崩、缓存击穿和缓存穿透。

缓存穿透中我们引出了布隆过滤器的概念,这个值得我们花一篇文章好好学习一下。

觉得本文对你有帮助的话,欢迎点赞评论收藏关注一波。你的鼓励,是我最大的动力~

不知道你有哪些收获呢?或者有其他更多的想法,欢迎留言区和我一起讨论,期待与你的思考相遇。