这是我参与「第五届青训营 」伴学笔记创作活动的第 17 天
前言
在业务中,我们经常使用redis来做缓存信息,redis技术就是NoSQL技术中的一种,但是引入redis又有可能出现缓存穿透,缓存击穿,缓存雪崩等问题。
以下几种使用redis面临的问题
- 缓存击穿:key对应的数据存在,但在redis中过期,此时若有大量并发请求过来,这些请求发现缓存过期一般都会从后端DB加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把后端DB压垮。
- 缓存穿透:key对应的数据在数据源并不存在,每次针对此key的请求从缓存获取不到,请求都会到数据源,从而可能压垮数据源。比如用一个不存在的用户id获取用户信息,不论缓存还是数据库都没有,若黑客利用此漏洞进行攻击可能压垮数据库。
- 缓存雪崩:当缓存服务器重启或者大量缓存集中在某一个时间段失效,这样在失效的时候,也会给后端系统(比如DB)带来很大压力。
缓存击穿解决方案
key可能会在某些时间点被超高并发地访问,是一种非常“热点”的数据。这个时候,需要考虑一个问题:缓存被“击穿”的问题。
使用互斥锁(mutex key)
业界比较常用的做法,是使用mutex。简单地来说,就是在缓存失效的时候(判断拿出来的值为空),不是立即去load db,而是先使用缓存工具的某些带成功操作返回值的操作(比如Redis的SETNX或者Memcache的ADD)去set一个mutex key,当操作返回成功时,再进行load db的操作并回设缓存;否则,就重试整个get缓存的方法。
缓存穿透解决方案
一个一定不存在缓存及查询不到的数据,由于缓存是不命中时被动写的,并且出于容错考虑,如果从存储层查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到存储层去查询,失去了缓存的意义。
有很多种方法可以有效地解决缓存穿透问题,最常见的则是采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被 这个bitmap拦截掉,从而避免了对底层存储系统的查询压力。另外也有一个更为简单粗暴的方法(我们采用的就是这种),如果一个查询返回的数据为空(不管是数据不存在,还是系统故障),我们仍然把这个空结果进行缓存,但它的过期时间会很短,最长不超过五分钟。
缓存雪崩解决方案
缓存雪崩,指的是大面积的 key 同时过期,导致大量并发打到我们的数据库。
不像击穿,只是因为 1 个 key 的过期。
正常的思路就是,就是让并发分散开来。
首先一个很常见的做法就是,分散 key 的过期时间。
确实,这么做是可行的,因为这个问题的本质,就是要让瞬间到来的并发,把它分散开。
而给了一定的随机过期时间之后,就能够使得 key 会分散开,一个一个过期,
所以,并发量就会分成一部分,一部分,少量的打到数据库上。
看起来就很像一个削峰的操作。
这个方法,是最简单有效的。所以一般情况,我们都采用这种方式
总结
对于缓存穿透,缓存击穿还有多种解决方法,需要为我们结合实际进行选择,多考虑可能面临的场景,以维护redis的缓存能力,减少数据库压力。