前提了解
在讲解这些问题时,我们需要了解使用 Redis 后,数据是如何进行查询的:
前台发送请求,后台先从缓存中取数据,
取到直接返回结果
取不到从数据库中取,
- 数据库取到就更新缓存,并返回结果
- 数据库也没取到,就返回空结果!
缓存穿透
问题描述:
我们知道当查询数据,在缓存中不存在时,会从数据库中查询!这个时候如果没有查到,会返回空值,而不会将这个不存在的值写入缓存!
当这种(无效的)数据量一大,每次请求都会到数据库中取,会导致数据库压力过大,从而宕机!
解决办法
- 进行数据验证:
如:id不小于0,邮箱、电话的格式等等- 缓存无效的key:
如果缓存和数据库中都查不到,使用 SET key value EX 10086;命令写入redis并加上过期时间!(key的格式: 表名:列名:主键名:主键值!)
此方法存在的问题:只适用于无效key变化不大的情况,加入是恶意攻击,大量不同的无效key,这个方法就行不通了,如果非要用,只能将过期时间设置的短一点!- 布隆过滤器:
在讲这个方法前,我们先来了解一下布隆过滤器这个东西!
布隆过滤器
什么是布隆过滤器?
本质
: 布隆过滤器 是一种数据结构,底层是一个 bit(二进制)数组!(和hashmap类似,不同的是这个数组的值是0、1)
作用
:快速查询一个数据是否存在!(适用于大量数据),但存在一点点问题,请看下文!
实现原理
:将一个数据映射(初始化)到布隆过滤器上时,要使用多个不同的哈希函数生成多个哈希值,并将对应的bit数组改为1!
- 思考:为什么是多个hash函数?(为了降低hash冲突)
工作流程
:首先对给定元素再次执行哈希计算,得到与添加元素时相同的位数组位置,判断所得位置是否都为 1,如果其中有一个为 0,那么说明元素不存在,若都为 1,则说明元素有可能存在。
- 为什么是可能存在?
原因很简单,那些被置为 1 的位置也可能是由于其他元素的操作而改变的。比如,元素1 和 元素2,这两个元素同时将一个位置变为了 1(图1所示)。在这种情况下,我们就不能判定“元素 1”一定存在,这是布隆过滤器存在误判的根本原因。
误判率
问题:对于误判,我们知道bit数组的长度越长,冲突的可能性越低;但是会消耗空间;所以我们通常会根据经验取一个合适的误判率!只要不超过这个误判率即可!
布隆过滤器解决缓存穿透:
通过上面的了解,我们可以得到如下总结:
- 优点:二进制组成的数组,占用内存极少,并且插入和查询速度都足够快。
- 缺点:随着数据的增加,误判率会增加;还有无法判断数据一定存在;另外还有一个重要缺点,无法删除数据。
- 布隆过滤器说不存在,那就一定不存在!如果说存在,则是可能存在!
我们可以将布隆过滤器放在如下位置!
- 除了布隆过滤器,还有一个布谷鸟过滤器,大家可以去了解一下!
缓存击穿
问题描述:
对于设置了过期时间的 key,缓存在某个时间点过期的时候,恰好这时间点对这个 Key 有大量的并发请求过来,这些请求发现缓存过期一般都会从后端 DB 加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把 DB 压垮。
某一个热点key过期,导致压力来到数据库!
解决方案:
- 互斥锁!
当缓存失效时,不立即去 load db,先使用如 Redis 的 setnx 去设置一个互斥锁,当操作成功返回时再进行 load db 的操作并回设缓存,否则重试 get 缓存的方法。- 设置热点key永不过期!物理不过期,但逻辑过期(后台异步线程去刷新)。
Redis相关命令:
- Expire
EXPIRE key_name
设置成功返回 1 。 当 key 不存在或者不能为 key 设置过期时间时(比如在低于 2.1.3 版本的 Redis 中你尝试更新 key 的过期时间)返回 0 。- PERSIST(设置为永不过期)
PERSIST key_name
当过期时间移除成功时,返回 1 。 如果 key 不存在或 key 没有设置过期时间,返回 0 。- TTL
TTL key_name
当 key 不存在时,返回 -2 。 当 key 存在但没有设置剩余生存时间时,返回 -1 。 否则,以秒为单位,返回 key 的剩余生存时间。
注意:在 Redis 2.8 以前,当 key 不存在,或者 key 没有设置剩余生存时间时,命令都返回 -1 。
缓存雪崩
问题描述:
设置缓存时采用了相同的过期时间,导致缓存在某一时刻同时失效,请求全部转发到 DB,DB 瞬时压力过重雪崩。与缓存击穿的区别:雪崩是很多 key,击穿是某一个key 缓存。
大量热点key,同一时间过期!
解决办法:
- 互斥锁!
- 热点key永不过期!
- 将过期时间打散!
比如可以在原有的失效时间基础上增加一个随机值,比如 1-5 分钟随机,这样每一个缓存的过期时间的重复率就会降低,就很难引发集体失效的事件。