在缓存使用中,“缓存击穿”“缓存穿透”“缓存雪崩” 是常见的问题,以下分别进行讲解:
一、缓存穿透
-
定义:
- 缓存穿透是指查询一个一定不存在的数据,由于缓存中无该数据,所有的请求都会落到数据库上,从而导致数据库压力过大,甚至可能使数据库宕机。
-
产生原因:
- 业务代码自身问题,例如对一些不合理的参数进行查询而这些参数对应的结果在数据库中也确实不存在。
- 恶意攻击,攻击者故意构造大量不存在的数据进行查询,以消耗数据库资源。
-
解决方案:
- 缓存空对象:当查询的数据在数据库中不存在时,也将一个空对象(或特殊标识)缓存起来,并设置较短的过期时间。这样下次相同的查询就可以直接从缓存中获取结果,而不会打到数据库。但这种方法可能会占用一些缓存空间。
- 布隆过滤器:在查询数据之前,先通过布隆过滤器判断数据是否可能存在。如果布隆过滤器判断数据不存在,那么就直接返回,不再查询数据库。布隆过滤器的优点是占用空间小、查询速度快,但可能存在一定的误判率。
二、缓存击穿
-
定义:
- 缓存击穿是指一个非常热点的数据,在缓存过期的瞬间,同时有大量的请求来访问这个数据,由于此时缓存中没有该数据,这些请求都会直接打到数据库上,从而对数据库造成巨大压力。
-
产生原因:
- 热点数据的缓存过期时间设置不合理,导致在某个时间点同时过期。
- 高并发场景下,大量请求同时访问同一个热点数据。
-
解决方案:
- 加互斥锁:当发现缓存中数据不存在时,只让一个请求去查询数据库并更新缓存,其他请求等待。可以使用分布式锁或者本地锁来实现。例如使用 Redis 的 SETNX 命令实现分布式锁,只有获取到锁的请求才能去查询数据库并更新缓存,其他请求在获取锁失败时进行等待,直到缓存被更新后再从缓存中获取数据。
- 热点数据永不过期:对于一些特别热点的数据,可以设置永不过期,或者在缓存即将过期时提前异步更新缓存,避免缓存过期时大量请求同时打到数据库。
三、缓存雪崩
-
定义:
- 缓存雪崩是指在某一时刻,缓存中大量的数据同时过期,或者缓存服务出现故障,导致大量的请求直接打到数据库上,从而使数据库压力瞬间增大,甚至可能导致数据库宕机。
-
产生原因:
- 大量数据同时设置了相同的过期时间。
- 缓存服务器宕机,可能是由于硬件故障、网络问题或者缓存服务自身的问题。
-
解决方案:
- 随机过期时间:在设置缓存数据的过期时间时,给每个数据的过期时间加上一个随机值,避免大量数据同时过期。例如,可以在数据原本的过期时间基础上加上一个随机的时间偏移量,使得过期时间分散在不同的时间点。
- 缓存降级:当缓存服务出现故障时,可以采取缓存降级策略,即直接返回一些默认值或者旧数据,以保证系统的基本可用性,同时尽快修复缓存服务。
- 多级缓存:使用多级缓存,如本地缓存和分布式缓存相结合。当分布式缓存出现问题时,本地缓存还可以提供一定的缓冲,减少对数据库的压力。