看完这篇,让你吃透 Redis 的缓存穿透、缓存击穿、缓存雪崩问题
看完这篇,让你吃透 Redis 的缓存穿透、缓存击穿、缓存雪崩问题
缓存的基本使用
- 前端向后台发送请求
- 判断 Redis 中是否存在
- 假如存在,Redis 直接返回数据,然后返回给前端(end)
- 假如不存在,服务端发起请求查询数据库
- 接收到返回的数据后,更新到 Redis 缓存,保证下次能获取到
- 返回数据给前端(end)
缓存穿透
问题描述
- 用户发起大量的请求
- 结果发现 Redis 中没有数据
- 后台查询数据库,发现数据库也没有数据
- 就这样一直请求,导致数据库崩溃
这种大量的请求,可能是接口本身的并发量就比较大,也可能是有黑客在攻击
解决方案
1. 异常参数校验
比如发起一个 id 为 “-1” 的请求,这一看就是有问题的,哪有可能 id 为负数的。或者一些非空参数,传了空值,极有可能是某人在发起攻击,直接后台判断拦截。
优点: 最快,也是最简单的拦截方式,没有额外的资源开销
缺点: 适用的场景有限
2. 空值缓存
- 直接跳到判断 Redis 是否存在
- 不存在,缓存空数据,设置过期时间一般为 30 秒(这里的空数据,可以直接设置数据为空的情况的值,也可以是空值标识,过期时间根据业务需求调整)
- 服务端直接返回前端
优点: 数据为空时能够快速返回,不需要将压力落到数据库上,避免了数据库压力过大,甚至崩溃的情况
缺点: 缓存层和存储层的数据会有一段时间窗口的不一致,就是数据库明明没值,缓存上却有值,一致性要求高的业务需要注意,做好校验工作
3. 布隆过滤器
假如我们添加值到布隆过滤器中,布隆过滤器会通过计算多种不同的哈希函数来计算出多个 hash 值,然后将对应位置的值设置为 1。因此这个 hash 值是有被多个不同的值对应的可能,所以说布隆过滤器如果命中了,说明值可能存在,如果不命中,肯定不存在。误判率为3%。
- 直接跳到判断 Redis 是否存在
- 不存在,则从布隆过滤器中获取
- 将获取到的值,更新到 Redis(首先布隆过滤器存在值,也不一定是正确的,因此并非所有场景都适用,其次数据需要刷回 Redis,因为 Redis 没值依然会来布隆过滤器上取值,因此数据本身需要对正确性的要求不是很高)
- 服务端返回数据
优点:
- 等同于用布隆过滤器代替了数据库,因此性能高
- 由于顶替了数据库,规避了数据库宕机的风险
缺点:
- 代码逻辑较为复杂
- 需要对布隆过滤器预先做初始化
- 布隆过滤器的初始化与更新较为复杂
- 布隆过滤器不支持删值操作
- 适用场景有限,由于布隆过滤器获取的数据存在不正确的可能性,因此需要数据对本身值的正确性要求较低,适用于注重数据是否存在的场景,比如:IP 访问白名单,之前是否访问过;垃圾邮件,垃圾短信过滤等
缓存击穿
问题描述
- 用户发起大量的请求
- 这时 Redis 中数据大量过期
- 导致瞬间数据库压力暴增,导致数据库宕机
解决方案
1. 设置热点数据永远不过期
也是一种方法,但是总感觉心里不太安心
2. 设置热点数据的存活时间较长,且分布均匀的岔开过期时间
很好理解,存活时间长自然降低了出现大量过期的频率
使用随机数岔开过期时间,使得需要过期的数据先后过期,使得数据库有充足的时间处理过期的数据
3. 限流做安全保障
举个例子:比如一个商场,双 11 我需要大批量的替换图片,这时候如果大量的失效必然会导致数据库崩溃,这时如果有做限流控制,就可以针对获取图片的接口进行限流,等到都替换完,再将限流放开。
缓存雪崩
问题描述
Redis 层由于压力过大,导致 Redis 服务器宕机,所有后端请求直接落在数据库上,导致数据库也宕机。
解决方案
1. Redis 高可用
顾名思义,通过多台的 Redis 架设集群,分摊压力
2. 限流
通过限制访问数量的方式,防止大量的请求进入缓存,可以通过限制 IP 频率,限制请求总量,或者使用消息队列依次处理请求等方式