引入缓存带来的问题以及解决方案

目录

前言

问题与解决方案

缓存击穿

缓存穿透

缓存雪崩

缓存一致性


前言

在提升接口性能的方案中,毫无疑问,使用缓存是最有效果的,但同时也会带来新的问题。

  1. 缓存击穿
  2. 缓存穿透
  3. 缓存雪崩
  4. 缓存一致性

以上问题都是引入缓存需要考虑的,在设计时就需要做好相应的解决措施。

问题与解决方案

缓存击穿

缓存击穿是指在高并发的情况下,某个热点key突然失效或者未被缓存,导致大量请求直接穿透到后端数据库,从而使得数据库负载过高,甚至崩溃的问题。

这种情况通常发生在缓存中的热点数据过期或失效时,由于并发用户特别多,读缓存没读到数据后同时去数据库中取数据,引起数据库压力瞬间增大。

解决方案

  1. 设置热点数据永不过期:对于基本不会发生更新的热点数据,可以将其设置为永不过期,以避免缓存击穿的发生。但这种方法需要谨慎使用,以免影响数据的实时性。
  2. 使用分布式锁:在缓存失效时,通过分布式锁(如Redis、zookeeper等)控制只有一个线程去数据库查询并更新缓存,其他线程则等待锁释放后访问缓存。
  3. 缓存预热:在系统启动或低峰时段,预先将热点数据加载到缓存中,避免在高并发时从数据库加载数据。

缓存穿透

缓存穿透是指查询一个一定不存在的数据,由于缓存是不命中时需要从数据库查询,而查询的结果在数据库中也不存在,因此不会写入缓存。这将导致这个不存在的数据每次请求都要到数据库去查询,进而给数据库带来压力。

当用户或系统查询一个在数据库中不存在的数据时,由于缓存中没有缓存该数据(因为数据本身不存在),每次查询都会失败并直接访问数据库。

解决方案

  1. 使用布隆过滤器:同缓存击穿中的布隆过滤器使用方式,对于不存在的数据直接返回空结果,避免无效的数据库查询。
  2. 空值缓存:对于查询结果为空的数据,也将其缓存起来(但设置一个较短的过期时间),这样再次查询相同的数据时就可以直接从缓存中返回空结果,避免查询数据库。

缓存雪崩

缓存雪崩是指缓存中数据大批量到过期时间,而查询数据量巨大,引起数据库压力过大甚至down机。

当缓存中大量数据同时过期时,如果此时有大量请求需要访问这些数据,那么这些请求都会直接穿透到数据库,导致数据库压力骤增。

解决方案

  1. 随机过期时间:在设置缓存的过期时间时,采用随机时间策略,避免大量缓存同时过期。
  2. 限流降级:在缓存雪崩发生时,通过限流措施限制对数据库的访问频率,同时开启降级策略,将一些非核心功能暂时关闭或简化处理。

缓存一致性

在非并发情况下,由于写数据库和更新缓存之间存在时间差,所以在这段时间差内,就会出现数据不一致问题,即缓存的数据和数据库中的数据不一致。

而在并发情况下,存在写写并发和读写并发的情况,同样也会导致缓存与数据库数据不一致。

写写并发(有两种写入方式:先操作数据库再操作缓存,先操作缓存再操作数据库;这里以先操作数据库再操作缓存为例)

写线程 写线程
写数据库 写入100
写数据库 写入200
写缓存 写入200
写缓存 写入100

读写并发

写线程 读线程
读缓存 缓存无值
读数据库 读取200
写数据库 写入100
写缓存 写入100
写缓存 写入200

解决方案

保证数据库和缓存的数据一致性,有两种方式

  1. 更新缓存
  2. 删除缓存

如果选择更新缓存,无论是先更新缓存再更新数据库,还是先更新数据库再更新缓存,只要第二个更新操作除了问题,就会出现缓存不一致问题了。

如果选择删除缓存,如果是先先更新数据库再删除缓存,那么如果删除缓存操作失败了,那么还是会出现缓存不一致问题。如果是先删除缓存再更新数据库,即使第二步更新数据库失败了,由于缓存被删除了,下一次读取则会从数据库中获取再写入缓存中,不会出现缓存不一致问题。

所以应当采用先删除缓存,再更新数据库的策略来保证缓存一致性

但是采用删除缓存的策略,实际上就会放大了读写并发问题,在高并发场景下,同样会出现缓存不一致问题。为了解决这个问题,可以使用延迟双删的方案,在一定程度上解决。

延迟双删实际上就是在先删除缓存,再更新数据库的基础上,延时一小段时间再去删除缓存。如果真的出现了读写并发导致的缓存不一致问题,延迟删除缓存的操作能够将旧的缓存值删掉,从而解决数据不一致问题。但这种写法实际上是对错误的一个补偿措施,并没有彻底地解决高并发下的缓存一致性问题。

如果业务上真的需要做到强一致性 ,则只能使用分布式锁来控制了,更新数据库和更新缓存的操作使用同一把分布式锁,但是这样就与高并发没有啥关系了。

如果业务上只需要做到最终一致性 的话,使用先删缓存再写数据库,或者是使用延迟双删就能实现了,也可以借助中间件来实现数据库与缓存的数据一致性(MySQL使用canal来同步),但是引入新的中间件实际上又得考虑新的问题,延迟双删针对绝大部分场景实际上就够用了,所以这里就不展开讲了(也没有在实际场景下使用过,我不会

相关推荐
小希与阿树1 小时前
如何解决Redis中的热点key问题
数据库·redis·缓存
lindsayshuo7 小时前
为什么在二维卷积操作中,将宽度(W)维度放在高度(H)之前会破坏空间局部性原则,并影响缓存性能
缓存
代码代码快快显灵10 小时前
Redis 优化秒杀(异步秒杀)
数据库·redis·缓存
代码代码快快显灵12 小时前
Redis之秒杀活动
数据库·redis·缓存·秒杀活动
学是为了不学19 小时前
Eureka缓存机制
java·spring cloud·缓存
huaqianzkh1 天前
Redis的内存预分配策略
数据库·redis·缓存
有馬公生1 天前
有关Redis的相关概述
数据库·redis·缓存
瞳绣1 天前
【redis初阶】初识Redis
数据库·redis·缓存
轻口味2 天前
【每日学点鸿蒙知识】调试、网络、缓存、富文本编辑等
缓存·华为·harmonyos