目录
缓存穿透
什么是缓存穿透
缓存穿透说简单点就是大量请求的key是不合理的,根本不存在于缓存中,也不存在于数据库中。这就导致这些请求直接到了数据库上,根本没有经过缓存这一层,对数据库造成了巨大的压力,可能直接就被这么多请求宕机了。
举个例子:某个黑客故意制造一些非法的key发起大量请求,导致大量的请求落到数据库上,结果数据库上也没有查到对应的数据。也就是说这些请求最终都落到了数据库上。
有哪些解决办法?
最基本的的就是首先做好参数校验,一些不合法的参数请求直接抛出异常信息给客户端。比如查询的数据库id不能小于0,传入的邮箱格式不对的时候直接返回错误消息给客户端等等。
缓存的无效key
如果缓存和数据库都查不到某个key的数据,就写一个到Redis中并设置过期时间,具体命令如下,set key value EX 10086。这种方式可以针对key变换不频繁的情况,如果黑客恶意共计,每次构建的key不同,会导致redis中有许多无效的key。很明显这种问题不能从根本上解决问题。如果非要用这种方式来解决穿透问题的话,尽量将无效key的过期时间设置短一点,比如一分钟。
一般情况下是这样设置key的:表名:列名:主键名:主键值
使用Java代码差不多是这样的
public Object getObjectInclNullById(Integer id) {
// 从缓存中获取数据
Object cacheValue = cache.get(id);
// 缓存为空
if (cacheValue == null) {
// 从数据库中获取
Object storageValue = storage.get(key);
// 缓存空对象
cache.set(key, storageValue);
// 如果存储数据为空,需要设置一个过期时间(300秒)
if (storageValue == null) {
// 必须设置过期时间,否则有被攻击的风险
cache.expire(key, 60 * 5);
}
return storageValue;
}
return cacheValue;
}
布隆过滤器
布隆过滤器是一个非常神奇的数据结构,通过它我们可以非常方便地判断一个给定数据是否存在于海量数据中。我们可以把它看作由二进制向量(或者说位数组)和一系列随机映射函数(哈希函数)两部分组成的数据结构。相比于我们平时常用的 List、Map、Set 等数据结构,它占用空间更少并且效率更高,但是缺点是其返回的结果是概率性的,而不是非常准确的。理论情况下添加到集合中的元素越多,误报的可能性就越大。并且,存放在布隆过滤器的数据不容易删除。
Bloom Filter会使用一个较大bit数据来保持所有的数据,数组中每个元素都只占用1bit,并且每个元素只能是0或者1(代表false或者true),这也是Bloom Filter节省内存的核心所在。这样算来的话,申请一个100w个元素的位数只占用122KB空间。
具体是这样的,把所有可能存在的请求的值都存放在布隆过滤器当中,当用户请求过来,先判断用户发来的请求的值是否存在布隆过滤器中。不存在的话,直接返回请求参数错误信息给客户端,存在的话才会走下面的流程。
加入布隆过滤器之后的缓存处理流程图如下:
接口限流
根据用户或者IP对接口进行限流,对于异常频繁的访问行为,还可以采取黑名单机制,例如将异常的IP列入黑名单。
缓存击穿
什么是缓存击穿
缓存击穿中,请求的key对应的是热点数据,该数据存在于数据库中,但不存在于缓存中(通常是因为缓存中的那份数据已经过期)。这就可能会导致瞬间大量的请求直接打到数据库上,可能直接就被这么多请求弄宕机了。
举个例子:秒杀进行过程中,缓存中的某个秒杀商品的数据突然过期,这就导致瞬时大量对该商品的请求直接落到数据库上,对数据库造成了巨大的压力。
有哪些解决办法?
1.永不过期(不推荐):设置热点数据永不过期或者是过期时间较长。
2.提前预热(推荐):针对热点数据提前预热,将其存入缓存中并设置合理的过期时间比如秒杀场景下的数据在秒杀结束之前不过期。
3.加锁(看情况):在缓存失效后,通过设置互斥锁确保只有一个请求查询数据库并更新缓存。
缓存穿透和缓存击穿有什么区别
缓存穿透中,请求的 key 既不存在于缓存中,也不存在于数据库中。
缓存击穿中,请求的 key 对应的是 热点数据 ,该数据 存在于数据库中,但不存在于缓存中(通常是因为缓存中的那份数据已经过期) 。
缓存雪崩
什么是缓存雪崩
实际上,缓存雪崩描述的就是这样一个简单的场景:缓存在同一时间大面积的失效,导致大量的请求都直接落到了数据库上,对数据库造成了巨大的压力。另外缓存服务宕机也会导致缓存雪崩现象,导致所有的请求都落到了数据库上。
有那些解决办法
针对Redis服务不可用的情况:
1.Redis集群:采用Redis集群,避免单机出现问题整个缓存服务都没办法使用。
2.多级缓存:设置多级缓存,例如本地缓存+Redis缓存的二级缓存组合,当Redis缓存出现问题时,还可以从本地缓存获取到部分数据。
针对大量缓存同时失效的情况:
- 设置随机失效时间(可选):为缓存设置随机的失效时间,例如在固定过期时间的基础上加上一个随机值,这样可以避免大量缓存同时到期,从而减少缓存雪崩的风险。
- 提前预热(推荐):针对热点数据提前预热,将其存入缓存中并设置合理的过期时间,比如秒杀场景下的数据在秒杀结束之前不过期。
- 持久缓存策略(看情况):虽然一般不推荐设置缓存永不过期,但对于某些关键性和变化不频繁的数据,可以考虑这种策略。