彻底分清缓存穿透、缓存击穿、缓存雪崩

在使用 Redis 等缓存技术时,缓存穿透、缓存击穿和缓存雪崩是常见且需要重点关注的问题。理解并有效解决这些问题,对于保障系统的高性能和稳定性至关重要。

一、缓存穿透

1. 定义

缓存穿透指的是查询一个一定不存在的数据,由于缓存中没有,所以直接查询数据库,而数据库中也没有该数据,导致每次请求都要穿过缓存直达数据库。如果这种请求大量存在,就会给数据库带来巨大压力,甚至可能导致数据库瘫痪。

2. 问题场景分析

  • 恶意攻击:黑客故意构造大量不存在的 key 进行请求,如在电商系统中,不断请求不存在的商品 ID,数据库不断响应不存在的结果,造成数据库资源浪费。
  • 数据异常:业务数据本身存在异常情况,例如数据同步过程中部分数据丢失,而前端不知情仍进行查询。比如在库存管理系统中,库存数据在从旧系统迁移到新系统时部分 ID 丢失,用户查询这些 ID 对应的库存时就会出现缓存穿透。

3. 解决方案

  • 布隆过滤器(Bloom Filter):在缓存之前使用布隆过滤器。布隆过滤器可以快速判断一个元素是否存在。当请求到来时,先通过布隆过滤器判断该 key 是否存在,如果不存在,直接返回,无需查询数据库。布隆过滤器存在一定的误判率,但可以通过调整参数来控制。例如在 Java 中可以使用 Google 的 Guava 库来实现布隆过滤器。

说明:客户端发起请求,首先经过布隆过滤器。若布隆过滤器判断请求的 key 不存在,直接返回无数据,避免查询数据库。若布隆过滤器认为 key 存在,再去查询缓存。缓存命中则返回数据,未命中时查询数据库,查到数据后更新缓存并返回。

  • 空值缓存:当查询数据库发现数据不存在时,也将该 key - value(value 可以是 null 或者一个特殊标识)存入缓存,并设置较短的过期时间。这样下次再查询相同的 key 时,缓存可以直接响应,避免穿透到数据库。但要注意过期时间的设置,过短可能无法有效防止穿透,过长则可能导致数据更新不及时。

说明:客户端请求先查询缓存,缓存命中则返回数据。未命中时查询数据库,若数据库有数据,更新缓存并返回。若数据库无数据,设置空值缓存后返回无数据,下次同样请求可直接从缓存获取无数据结果。

记忆技巧

可以将缓存穿透想象成子弹穿透了缓存这层 “防护网” 直接打到了数据库。就像现实中子弹如果能轻易穿透防护网,会对后面的目标造成严重伤害,缓存穿透也会对数据库造成巨大压力。

二、缓存击穿

1. 定义

缓存击穿是指一个热点 key,在某个时间点过期的时候,恰好在这个时间点对这个 Key 有大量的并发请求过来,这些请求发现缓存过期后,都会去查询数据库,从而给数据库带来很大压力。与缓存穿透不同的是,缓存击穿针对的是热点数据,而缓存穿透针对的是不存在的数据。

2. 问题场景分析

  • 热点商品抢购:在电商促销活动中,某款热门商品的信息存放在缓存中。当缓存过期的瞬间,大量用户同时请求购买该商品,这些请求都发现缓存过期,进而同时去查询数据库获取商品信息,数据库瞬间承受巨大压力。
  • 热门新闻查看:在新闻资讯平台,某条热点新闻的详情缓存过期时,大量用户在同一时刻刷新查看该新闻,导致所有请求都去数据库获取新闻内容,可能使数据库不堪重负。

3. 解决方案

  • 互斥锁(Mutex):在缓存过期时,使用互斥锁来保证只有一个请求能去查询数据库并更新缓存。其他请求等待该请求完成,从缓存中获取数据。例如在 Java 中可以使用RedisSETNX(SET if Not eXists)命令来实现互斥锁。获取锁的线程查询数据库,更新缓存后释放锁。但这种方法可能会降低系统的并发性能,因为同一时间只有一个线程能操作数据库。

说明:客户端请求到达后先查询缓存,命中则返回数据。缓存未命中时尝试获取互斥锁,获取成功的请求查询数据库,更新缓存并释放锁,然后返回数据。获取锁失败的请求等待,直到获取到锁后再重复上述过程。

  • 热点数据永不过期:对于一些热点数据,不设置过期时间,这样就不会出现缓存过期瞬间大量请求穿透到数据库的情况。不过需要额外的机制来更新这些数据,比如在数据发生变化时主动更新缓存,或者定时异步更新缓存。

说明:客户端请求先查询缓存,命中即返回数据。缓存未命中时查询数据库,查到数据后更新缓存并返回。同时,通过定时任务或数据变更机制主动更新缓存,确保数据的实时性。

记忆技巧

可以把缓存击穿想象成在一堵墙上,有一个特别热门的 “洞”(热点 key),当这个洞的 “补丁”(缓存)过期时,大量的水流(请求)同时通过这个洞涌进来,冲击后面的数据库,就像水流击穿了墙一样。

三、缓存雪崩

1. 定义

缓存雪崩是指在某一时刻,大量的缓存 key 同时过期失效,导致大量请求直接访问数据库,数据库瞬间压力过大,甚至可能崩溃。它与缓存击穿的区别在于,缓存击穿是单个热点 key 过期,而缓存雪崩是大量 key 同时过期。

2. 问题场景分析

  • 缓存过期策略不当:如果设置了大量缓存数据具有相同的过期时间,比如在电商系统中,为了方便管理,将所有商品的缓存都设置为凌晨三点过期。当凌晨三点到来时,所有商品缓存同时失效,大量用户的请求直接涌向数据库,数据库很可能因无法承受压力而崩溃。
  • 缓存服务器故障:假设使用的是 Redis 集群,当其中一个节点出现故障,该节点上的所有缓存数据都无法访问,相当于这些缓存同时失效,从而引发大量请求访问数据库,造成类似缓存雪崩的效果。

3. 解决方案

  • 随机过期时间:在设置缓存过期时间时,给每个 key 加上一个随机的过期时间偏移量,避免大量 key 在同一时间过期。例如原本设置过期时间为 30 分钟,可以改为在 25 - 35 分钟之间随机选择一个时间作为过期时间。这样可以使缓存过期时间分散开,降低同时过期的风险。

说明:在设置缓存时,随机生成过期时间后存入缓存。客户端请求到达,先查询缓存,命中则返回数据。未命中时查询数据库,更新缓存并设置新的随机过期时间,然后返回数据,使缓存过期时间分散。

  • 二级缓存:采用二级缓存架构,例如在 Redis 主缓存之外,再设置一层备用缓存(如 Memcached)。当主缓存中的数据过期时,先从备用缓存中获取数据。如果备用缓存也没有,则查询数据库,并将数据同时更新到主缓存和备用缓存中。这样可以在一定程度上缓解数据库的压力。

说明:客户端请求先查询主缓存,命中则返回数据。主缓存未命中时查询二级缓存,二级缓存命中返回数据。若二级缓存也未命中,查询数据库,然后将数据同时更新到主缓存和二级缓存,最后返回数据。

  • 缓存预热:在系统启动时,提前将部分热点数据加载到缓存中,并设置不同的过期时间,避免在系统运行过程中这些数据同时过期。同时,在业务低峰期,可以主动对一些即将过期的缓存进行更新,防止在高峰期出现大量缓存同时过期的情况。

说明:系统启动时加载热点数据到缓存,并设置不同过期时间。客户端请求到达,先查询缓存,命中返回数据。未命中时查询数据库,更新缓存并返回数据。在业务低峰期主动更新即将过期的缓存,防止高峰期大量缓存同时过期。

记忆技巧

可以把缓存雪崩想象成一场雪崩,大量的雪(缓存 key)在同一时间从山上(缓存)崩塌下来,冲向山脚下的村庄(数据库),对村庄造成巨大破坏,形象地体现了大量缓存同时过期给数据库带来的严重压力。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值