Redis缓存雪崩、穿透、击穿问题
缓存雪崩
假设我们的服务有大量的查询,因为我们现有的机制一般都是:
$ 服务 ← Redis ← Mysql $
$ 服务 → Redis → Mysql $
这样的一套流程。那么假使流程中 Redis
出问题了,例如大批量的Key失效了
或者Redis宕机了
这些问题,那么这些查询就会顺延到 Mysql
中去查询,再将 Mysql
查询到的结果写回到缓存中,这样下一次查询 Redis
中同样的Key
就能给出相应的数据了。
缓存雪崩,其实就是指的在上面这个过程中大批量的Key失效了
这个问题。
注:Redis 内的数据是有缓存期限的,因为内存有限,不可能让 Redis 一直往内存里灌数据进去保存着。
要防止缓存雪崩这个问题,有三种有效的做法:
- 在
Key
失效时间的基准上加一个Random
值,这样就可以防止大量的Key
在同一时间一起失效。 - 不设置
Key
失效时间,而是采用也监听事务数据变化的方法来让缓存内的数据进行刷新更换。 - 使用
查询 Mysql 并将查询到的对应数据填补进 Redis 的空缺位置
这样一个逻辑,但在这个逻辑里会有一个加锁互斥的操作,让一个线程去查 Mysql 数据库,之后可以很久不用查。查 Redis 的时候发现有数据空缺,加一个互斥锁让一个线程去查 Mysql,查完之后将数据写回到 Redis 内进行空缺填补,最后将互斥锁释放。锁的策略由开发人员决定。
缓存击穿
缓存击穿,类似于缓存雪崩的一个子集,指的是当一个热门的缓存Key
突然失效或者被大量的并发请求同时查询时,就可能发生缓存击穿。也是服务直接跳过 Redis 在数据库进行数据查询。
缓存击穿的预防与减缓方法主要有:
- 设置合理的缓存过期时间: 针对热门数据,可以设置相对较长的缓存过期时间,或者采用永不过期的策略,以减少缓存失效导致的缓存击穿风险。
- 缓存预热: 在系统启动或者数据更新时,可以预先加载热门数据到缓存中,避免在热门数据失效时出现大量的请求穿透。
- 使用互斥锁或分布式锁: 在缓存失效时,可以使用互斥锁或分布式锁来保护后端存储的访问,只允许一个线程去加载数据,其他线程等待加载完成后再获取数据。
- 提供备份数据源: 当缓存失效时,可以提供备份数据源,例如备份数据库或其他存储后端,以便在缓存失效时能够快速获取数据。
- 限流和熔断: 对于大量的并发请求,可以通过限流和熔断等手段来控制请求的并发量,避免对后端存储造成过大的压力。
其实会发现实际上还是和缓存雪崩的方法差不多,毕竟类似缓存雪崩的子问题。
缓存穿透
服务查询 Redis 查询不到,就会根据流程直接查询 Mysql 数据库,但 Mysql 数据库内也查不到,这时候就等于“击穿”了。没有办法按流程将数据写回 Redis 提供查询,导致 Redis 内一直都有没对应数据,下一次来了同样的需求仍然会造成“击穿”现象。
造成这种情况可能是一些非法的请求。这时候我们的做法就要去限制这些非法的请求,使得这些请求不能够触发查询,甚至不构成服务。
要防止上述问题,可以用如下的方法:
- 运维层面封禁。
- 在网关层面限流。
- 代码里对相应的参数进行合法化的校验。
- 在 Redis 和 Mysql 之间加一个布隆过滤器。
检索这个
Key
存不存在,加了这个以后就不需要去查 Mysql 数据库就能够做出相应的返回操作。布隆过滤器由「初始值都为 0 的位图数组」和「 N 个哈希函数」两部分组成。当我们在写入数据库数据时,在布隆过滤器里做个标记,这样下次查询数据是否在数据库时,只需要查询布隆过滤器,如果查询到数据没有被标记,说明不在数据库中。
布隆过滤器会通过 3 个操作完成标记:
- 第一步,使用 N 个哈希函数分别对数据做哈希计算,得到 N 个哈希值;
- 第二步,将第一步得到的 N 个哈希值对位图数组的长度取模,得到每个哈希值在位图数组的对应位置。
- 第三步,将每个哈希值在位图数组的对应位置的值设置为 1;
但是布隆过滤器由于是基于哈希函数实现查找的,高效查找的同时存在哈希冲突的可能性。
查询布隆过滤器说数据存在,并不一定证明数据库中存在这个数据,但是查询到数据不存在,数据库中一定就不存在这个数据。
不过我们还是得进行缓存的占位,不管是有效输入还是为空,都需要写到缓存。
评论(0)