缓存穿透
什么是缓存穿透?
缓存穿透说简单点就是大量请求的 key 是不合理的,根本不存在于缓存中,也不存在于数据库中 。这就导致这些请求直接到了数据库上,根本没有经过缓存这一层,对数据库造成了巨大的压力,可能直接就被这么多请求弄宕机了。
举个例子:某个黑客故意制造一些非法的 key 发起大量请求,导致大量请求落到数据库,结果数据库上也没有查到对应的数据。也就是说这些请求最终都落到了数据库上,对数据库造成了巨大的压力。
有哪些解决办法?
最基本的就是首先做好参数校验,一些不合法的参数请求直接抛出异常信息返回给客户端。比如查询的数据库 id 不能小于 0、传入的邮箱格式不对的时候直接返回错误消息给客户端等等。
1. 设置空值
当在数据库中也未查询到该key时,在缓存中为key设置空值,防止对数据库发起重复查询。设置空值方案存在问题:不在系统中的key值是无限的,如果均设置key值为空,会造成内存资源的极大浪费,引起性能急剧下降。
2. 布隆过滤器
使用布隆过滤器来过滤掉系统中不存在的key,从而避免请求穿透缓存去查询数据库。
实现方式:在缓存之前添加一个布隆过滤器,将系统中所有可能存在的key全部映射到布隆过滤器中。当查询请求到来的时候,先到布隆过滤器里面对key进行过滤,只允许系统中存在的key进行后续查询。
「布隆过滤器」是一个很长的二进制向量和一系列随机映射函数,用于快速判断元素是否存在于集合中。
布隆过滤器的原理:
1. 布隆过滤器使用一个位数组(Bit Array),初始时所有的位都设置为0。
2. 当向布隆过滤器中添加元素时,使用多个哈希函数将元素映射到位数组中的几个位置,把这几个位置的值设置为1。
3. 当要判断一个元素是否属于集合时,使用这些哈希函数将元素映射到位数组中的几个位置,并检查对应位置的值是否为1。如果对应的位置都为1,则说明元素可能属于集合;如果任意一个位置的值为0,则说明元素一定不属于集合。
布隆过滤器的优点:
1. 占用内存小。
2. 查询效率高。
3. 不需要存储元素本身。
布隆过滤器的缺点:
1. 有一定的误判率。
2. 不能获取元素本身。
3. 一般情况下不能从布隆过滤器中删除元素。
缓存击穿
什么是缓存击穿?
缓存击穿中,请求的 key 对应的是 热点数据 ,该数据 存在于数据库中,但不存在于缓存中(通常是因为缓存中的那份数据已经过期) 。这就可能会导致瞬时大量的请求直接打到了数据库上,对数据库造成了巨大的压力,可能直接就被这么多请求弄宕机了。
举个例子:秒杀进行过程中,缓存中的某个秒杀商品的数据突然过期,这就导致瞬时大量对该商品的请求直接落到数据库上,对数据库造成了巨大的压力。
击穿的解决方案
1. 缓存失效后,通过加锁、队列的方式控制写缓存的线程数量,保证缓存的单线程写,使得缓存更新串行化,大量的缓存更新操作排队进行,从而降低更新频率。
2. 缓存预热:系统上线后,提前加载热门数据到缓存中,避免在流量高峰期读数据库。
3. 限流策略:通过限制并发访问的请求数量,避免系统在缓存失效时被大量请求冲垮。
4. 热点数据永不过期:不给缓存设置过期时间,而是将过期时间存在key对应的value里,开启一个定时任务,定期查看缓存的过期时间,如果发现快要过期了,通过一个后台的异步线程去主动更新缓存。
缓存雪崩
什么是缓存雪崩?
我发现缓存雪崩这名字起的有点意思,哈哈。
实际上,缓存雪崩描述的就是这样一个简单的场景:缓存在同一时间大面积的失效,导致大量的请求都直接落到了数据库上,对数据库造成了巨大的压力。 这就好比雪崩一样,摧枯拉朽之势,数据库的压力可想而知,可能直接就被这么多请求弄宕机了。
另外,缓存服务宕机也会导致缓存雪崩现象,导致所有的请求都落到了数据库上。
举个例子:数据库中的大量数据在同一时间过期,这个时候突然有大量的请求需要访问这些过期的数据。这就导致大量的请求直接落到数据库上,对数据库造成了巨大的压力。
雪崩的解决方案
1. 设置随机的缓存失效时间:给不同的key设置随机的失效时间,可以在原有的失效时间按上增加一个随机值,使失效时间的分布尽量均匀,从根源上避免大量缓存key值同时失效。
2. 设置两级或多级缓存:将缓存划分为多个层级,每个层级具有不同的过期时间和容量。
3. 限流:通过限制并发访问的请求数量,避免系统在缓存失效时被大量请求冲垮。
4. 熔断:在数据库访问出现故障时,通过切断对数据库的请求并提供备选方案来防止故障蔓延。
总结
穿透:查询的数据在缓存中不存在,数据库中也不存在。--- key不存在
击穿:热点数据的缓存突然失效。--- 热点的key失效
雪崩:大量的缓存在某一时刻同时失效。--- 大规模的key失效