Redis笔记-关于缓存雪崩、击穿和穿透
今天我们讲讲用 redis 缓存时容易遇到的一些问题。首先要明确一点的是,缓存雪崩、击穿、穿透这些名词虽然是针对缓存的,但是实际的受害方往往是数据库(存储层)。
今天我们讲讲用 redis 缓存时容易遇到的一些问题。首先要明确一点的是,缓存雪崩、击穿、穿透这些名词虽然是针对缓存的,但是实际的受害方往往是数据库(存储层)。因为我们之所以用缓存,就是为数据库服务,因为数据库的抗压性相对要低于缓存系统。当我们的缓存由于一些原因使用不当时,就会导致数据库压力剧增,由此产生上面的几种问题。
题外话:我觉得一些技术上的专有名词很扯,有些是某位大神直接自己翻译过来传开的,其实它的本意可能翻译的并不准确。所以终归到底,专有名词能记住最好,有装逼的资本;没记住也没必要强记,理解含义和场景是正解,同时面对面试官先下手为强,避免忘记名词时的尴尬。
雪崩
缓存雪崩往往是指在一段时间内,灭霸打了个响指,缓存大批量的失效(同一时间过期),没有缓存怎么办,当然就去数据库找数据了,如果请求量巨大,数据库很容易就崩了,由此就叫做缓存导致的雪崩。
雪崩时,没有一片雪花是无辜的。
这句话说得多好,在同时过期的这一瞬间,没有一个缓存是无辜的,大片的缓存过期就像一场雪崩,淹没了脆弱的数据库。
缓存雪崩的英文原意是 stampeding herd(奔逃的野牛),指的是缓存层宕掉后,流量会像奔逃的野牛一样,奔向后端存储。
举个例子就比如双11期间,晚上 12 点有一批需要抢购的商品,我们把它们放在缓存里,抢购时间也就是过期时间 1 小时,结果在 1 点的时候这批商品的访问量还是很大,就可能产生缓存雪崩了。
防止雪崩的常用解决方案就是给失效时间设置一个随机数:
setRedis(Key, value, time + Math.random() * 10000);
其实单单一批缓存数据的同时过期可能还好,数据库也能顶一顶,因为算是周期性的。但是比较可怕的是大批量同时过期导致了缓存服务器崩溃,那就相当于彻底崩了,数据库就很难顶了。
击穿
击穿跟雪崩类似,缓存雪崩是因为大面积的缓存失效,使数据层压力剧增,而缓存击穿不同的是指一个 Key 非常热点,在不停的扛着大并发,大并发集中对这一个点进行访问,当这个 Key 在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库,就像在一个完好无损的桶上凿开了一个洞。
这个场景的例子,就比如一个做活动的爆款商品,聪明的同学应该已经猜到了,和雪崩发生了类似的是,活动结束时,缓存失效,来自各个端的访问量还是很大的时候,就可能发生击穿的情况。
击穿的解决方案有很多,例如给缓存加分布式锁,因为热点数据很可能被多个程序多个场景访问,所以加锁可以控制访问的频率,即一个时间点之后一个地方可以访问到。但是分布式锁的方式想要做好很复杂,相对而言更推荐临时性的设置热点缓存不过期,后台异步的去更新数据或者删除这些缓存。
穿透
如果你看了前面的2个,缓存穿透也很好理解,都说穿透了,其实也就是缓存没数据,数据库也没数据,关键请求量还很大,你说气不气。
穿透的发生很可能是来自于故意攻击,指的是专门大批量的去请求不存在的数据,例如 id 是 -1 的数据。
简单的解决方案就是判断id的值范围,判断 id≤1 直接 return 了。当然 id 只是一个例子,其实针对客户端过来的参数,永远保持不信任的状态,给予合理的判断减少潜在的数据库压力是个好习惯。
或者就是对查询为空的情况也做一个缓存,相当于一个空缓存,当然你需要给这个缓存设置比较短的过期时间。
另外一种方式是提前使用布隆过滤器(布隆过滤器是 redis 的常见应用场景,详细说明请看我之后的文章),布隆过滤器简单来说就是过滤缓存的 key,不存在的 key 就直接返回了,存在的话再去查询具体的 key 的数据。