在 redis 的应用场景中,需要考虑缓存在某些场景下可能出现的问题:
-
缓存穿透
-
缓存击穿
-
缓存雪崩
以下缓存问题的讨论都是基于以下应用架构讨论的:
![](https://wjiaman.fun/media/blog_image/u=2546654357,3169296496&fm=253&app=138&f=PNG.png)
1. 缓存穿透
对应的数据在数据源中并不存在,每次针对此数据的请求从缓存获取不到,请求都会落到数据源,从而给数据源带来巨大的负载。即恶意请求穿透了缓存,落到了数据源,使得 redis 的数据缓存作用失效。
比如用一个不存在的用户 ID 获取用户信息,无论是缓存还是数据库都没有,黑客利用此漏洞进行攻击可能压垮数据库。
解决方法
-
缓存空结果。即使 redis 和数据源都不存在该资源,就将请求的数据作为空结果缓存一段时间,且为该数据设置一个较短的过期时间,否则数据的时效性会产生问题;
-
检验用户的合法性。对用户请求的合法性进行校验,对重复恶意的请求进行拦截(记录恶意用户或恶意 IP);
-
布隆过滤器。布隆过滤器的用途为帮助判断某个值是否存在。布隆过滤器的原理是使用一个 bitmap 数组用作标记,使用多个哈希函数对 key 值进行计算,并将计算值对应的位置在 bitmap 中进行置位,表示该 key 存在:
![](https://wjiaman.fun/media/blog_image/file_1637296487000.png)
![](https://wjiaman.fun/media/blog_image/20210109170328551.png)
-
增加 bitmap 的程度;
-
增加使用的哈希函数
2. 缓存击穿
即针对某一 特定 数据发起大量并发请求,当该数据在 redis 中过期时,这些请求会导致应用同时发起从数据源加载数据的请求,这时集中的大量并发请求可能会瞬间给数据源带来巨大负载。
解决方法
-
设置热点数据永不过期。可以设置定期任务,定期更新热点数据的 expire time;
-
使用互斥锁。即使用互斥锁保证只有一个请求会从数据源加载数据,操作逻辑如下:在缓存失效(取出值判断为控股)时,先尝试获取分布式锁:
-
若获取成功,则发起从数据源加载数据的请求;
-
若获取失败,则休眠一段时间后,尝试重新获取数据
此方法的缺点在于会对吞吐量造成影响。
-
3. 缓存雪崩
即某个时刻 redis 中的热点数据都消失了(缓存服务器重启或大量数据集中在某个时间段失效),这时也会导致应用从数据源加载数据,给数据源带来很大压力。
解决方法
-
保持缓存的高可用性。使用 Redis 哨兵模式或建立 Redis 集群,即使个别节点下线,整个缓存层依然可用;
-
缓存永不过期。Redis 中保存的 key 永不失效,这一就能解决大量缓存同时失效的问题,但随之而来的就是 Redis 需要更多存储空间;
-
设置随机过期时间。为每个数据设置随机的失效时间,避免
-
使用互斥锁重建缓存。即使用互斥锁避免大量的请求到达数据源查询数据,该方法同样会影响高并发场景下的吞吐量。