缓存穿透指查询不存在的数据,缓存层和存储层都不会命中。过程:① 缓存层不命中。② 存储层不命中,不将空结果写回内存(出于容错考虑)。③ 返回空结果。
缓存穿透将导致不存在的数据每次请求都要到存储层查询,可能会使后端负载增大,由于很多后端存储不具备高并发性,甚至可能造成后端宕机。通常在程序中分别统计总调用数、缓存命中数、存储层命中数,如果发现大量存储层空命中,说明可能出现了缓存穿透。
产生原因:自身业务代码或数据出现问题; 一些恶意攻击、爬虫等造成大量空命中。
解决方法:
缓存空对象:如果一个查询返回结果为 null,仍然缓存 null 结果,但其过期时间很短,通常不超过 5 分钟。
布隆过滤器:BloomFilter 是由一个固定大小的位图(bitmap)和一系列映射函数。
在初始状态时,所有位置都被置为0,当新增数据时,通过 K 个映射函数将这个变量映射成位图中的 K 个点,把它们置为 1。查询某个数据的时候我们只要看看这些点是不是都是 1 就可以大概率知道集合中有没有它了
布隆过滤器可以添加元素,但是不能删除元素。
对于热数据的访问量非常大,在其缓存失效的瞬间,大量请求直达存储层,导致服务崩溃。
解决:
可以在缓存时使用固定时间加上一个小的随机数,避免大量热点 key 同一时刻失效
使用随机退避方式,失效时随机 sleep 一个很短的时间,再次查询,如果失败再查询数据库并更新
采用分布式锁,只有拿到锁的第一个线程去请求数据库,然后插入缓存,每次拿到锁后都要去查询缓存看看有没有数据
分布式锁:①获取锁的时候,使用setnx加锁,若不能获取锁就一直循环,否则锁的value值为一个随机生成的UUID,并使用expire命令为锁添加一个超时时间;②获取锁的时候还设置一个获取的超时时间,若超过这个时间则放弃获取锁;③释放锁的时候,通过UUID判断是不是该锁,若是该锁,则执行delete进行锁释放。
如果缓存层因为某些问题不能提供服务,所有请求都会到达存储层,对数据库造成巨大压力。
解决方法: