缓存穿透、缓存雪崩与缓存击穿

2019/03/22 Redis

缓存穿透、缓存雪崩与缓存击穿

缓存穿透(cache penetration)

缓存穿透是==指查询一个根本不存在的数据==,缓存中不命中导致每次都要去访问数据库再查询一遍,然后返回空。

由于很多后端存储不具备高并发性,甚至可能造成后端存储宕掉。

在程序中分别统计总调用数、缓存层命中数、存储层命中数,如果发现大量存储层空命中,可能就是发生了缓存穿透问题。

解决方案

  1. 缓存空对象

存储层不命中后,仍然将空对象保存到缓存层中,并设置一个较短的过期时间。

问题:

  • 缓存层需要更多的内存保存存储层不存在的数据。如果是被攻击,可能造成缓存层内存不足。

  • 造成缓存层和存储层数据不一致。

如过期时间设为5分钟,在5分钟内存储层添加了该数据,而在缓存层数据值仍为空。

  1. 布隆过滤器

在访问缓存层和存储层之前,将存在的key用布隆过滤器提前保存,做第一层拦截,过滤掉不存在的key。

更加适用于数据相对固定、实时性低的情况。

缓存雪崩(stampeding herd[奔逃的野牛])

缓存层宕机、或==大量key同时失效==的情况下,大量请求被转发到后端,给数据库CPU和内存造成巨大压力,甚至宕机。

解决方案

  1. 加锁排队

加锁排队只能减轻后端数据库的压力,并不能提高系统吞吐量,治标不治本。

  1. 为key设置不同的有效时间

在设置key的有效时间时,加上一个随机值,防止key同一时刻失效。

缓存击穿(Cache Breakdown,热点key重建)

对于设置了过期时间的热点key,在缓存过期的时间点,会有大量的并发请求到后端。如果重建缓存不能在短时间内完成,会有大量线程来重建缓存,造成后端负载加大,应用崩溃。

解决方案

  1. 互斥锁(分布式锁)

加锁后再重建。只允许一个线程重建缓存,其他线程需要等待重建缓存的线程执行完。

  1. 永远不过期
  • redis上的热点key不设置过期时间。
  • 采用逻辑时间,将过期时间保存在value中,当取得key后如果发现key要过期,通过一个后台的异步线程重建。
  • 在构建时同样要使用互斥锁。

相比于单纯互斥锁方法,不会阻塞其余线程。缺点是,在后台异步线程重建缓存时,其余线程可能访问的是老数据。

运维碰到的问题

  1. 缓存与数据库不一致。

线程A删除了缓存中的数据,然后将新数据写入到主库中,主库中数据没来得及同步到从库。线程B读缓存失败,从从库中读取到旧数据,并写回到缓存。导致缓存中数据为旧数据且和数据库不一致。

解决方案:

所有对从库的数据更新也写回到缓存中,当主库对从库的更新到达从库时,缓存会被更新。

本文地址:https://cheng-dp.github.io/2019/03/22/cache-penetration-stampeding-herd-cache-breakdown/

Search

    Table of Contents