缓存穿透

  • 缓存穿透指的是业务系统访问不存在的数据,导致大量请求打到数据库,造成的数据库压力,甚至奔溃,此称为缓存穿透。
  • 解决方案:
    (一)缓存空数据
    如果从数据库中没查到值,可以在缓存中记录一个空值,来避免“缓存穿透”。并且要给这个空值设置一个较短的过期时间。(如果时间过长,会导致数据库更新后的数据不能及时更新到redis)
    缓存空对象会有两个问题:
  1. 第一,空值做了缓存,意味着缓存层中存了更多的键,需要更多的内存空间 ( 如果是攻击,问题更严重 ),比较有效的方法是针对这类数据设置一个较短的过期时间,让其自动剔除。
  2. 第二,缓存层和存储层的数据会有一段时间窗口的不一致,可能会对业务有一定影响。例如过期时间设置为 5 分钟,如果此时存储层添加了这个数据,那此段时间就会出现缓存层和存储层数据的不一致,此时可以利用消息系统或者其他方式清除掉缓存层中的空对象。

(二)BloomFilter(空数据的key各不相同、key重复请求概率低)

首先将BloomFilter里面存储目前数据库中存在的所有key,当业务系统有查询请求的时候,首先去BloomFilter中查询该key是否存在。若不存在,则说明数据库中也不存在该数据,因此缓存都不要查了,直接返回null。若存在,则继续执行后续的流程,先前往缓存中查询,缓存中没有的话再前往数据库中的查询。Redisson利用Redis实现了Java分布式布隆过滤器(Bloom Filter)。所含最大比特数量为2^32。

缓存雪崩

  • 缓存大量key集中失效(如缓存过期或服务不可用),导致大量请求同时穿透到数据库的情况,就是所谓的“雪崩效应”
  • 解决方案:
    使用缓存集群(Redis Sentinel 和 Redis Cluster),保证缓存高可用。
    当我们向缓存初始化数据时,要保证每个缓存记录过期时间的离散性。可以采用一个较大的固定值加上一个较小的随机值。比如过期时间可以是:10小时 + 0到3600秒的随机值。

缓存击穿

  • 对于一些请求量极高的热点数据而言,一旦过了有效时间,此刻将会有大量请求落在数据库上,从而可能会导致数据库崩溃,不同于缓存雪崩的点是,缓存雪崩是大量数据的key失效造成的数据库压力,而击穿指的是某个热点key被并发查询,key过期后直接到达数据库的问题。
  • 解决方案
    解决方案:
    (一)互斥锁
    如果从缓存查不到数据,对查询数据加分布式锁,然后查数据库并把数据库查询结果放入缓存。其他线程等待锁释放后,直接从缓存取值。
    当某一个热点数据失效后,只有第一个数据库查询请求发往数据库,其余所有的查询请求均被阻塞,从而保护了数据库。但是,由于采用了互斥锁,其他请求将会阻塞等待,此时系统的吞吐量将会下降。这需要结合实际的业务考虑是否允许这么做。
    (二)永不过期
    为每个 value 设置一个逻辑过期时间,当发现超过逻辑过期时间后,会使用单独的线程去构建缓存