侧边栏壁纸
博主头像
laasc

Coding changes the world

  • 累计撰写 7 篇文章
  • 累计创建 9 个标签
  • 累计收到 2 条评论

目 录CONTENT

文章目录

缓存穿透、缓存击穿、缓存雪崩的理解及解决方案

laasc
2023-08-07 / 0 评论 / 0 点赞 / 466 阅读 / 1,905 字 / 正在检测是否收录...
温馨提示:
本文最后更新于 2023-08-07,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

在生产环境中,除了遇到数据不一致,还有三种缓存异常的问题也经常遇到,分别是缓存穿透、缓存击穿和缓存雪崩。这三个问题一旦发生,会导致大量的请求积压到数据库层。如果并发量大的话,就会导致数据库负载增大,使缓存的作用降低。

一、缓存穿透

1. 缓存穿透的理解

缓存穿透是查询一个不存在的数据,缓存和数据库都无法命中,如果数据库查询不到数据则不会写入到缓存中,这将会导致这个不存在的数据每次请求都要到数据库去查询,失去了缓存保存的意义,如果请求量大的话,可能会导致数据库宕机或是故障。

示意图:

image-1691402176856

造成缓存穿透的基本原因有两种。第一,自身业务代码或者数据出现问题(例如:set 和 get 的key不一致),第二,受到一些恶意攻击、爬虫等造成大量空命中。

2.解决方案

2.1 缓存空值

缓存空值:是指在数据库没有命中的情况,对查询的key进行缓存空值(key,null)。

缓存空值会产生两个问题:

①:value为null也会占用空间,空值做了缓存,代表缓存中有了更多的键,占用了存储空间也增大了维护难度,比较有效的方式是在存储这类数据时可以设置一个较短的过期时间,让其自动删除。

②:缓存和数据库的数据会有一段时间的不一致,可能会对业务有一定影响。例如过期时间设置为5分钟,如果此时数据库中添加了这个数据,那此段时间就会出现缓存层和存储层数据的不一致,此时可以利用消息系统或者其他方式更新缓存中的空值。

2.2 布隆过滤器拦截

在访问缓存和数据库之前,将存在的key用布隆过滤器提前保存起来,做第一层拦截,当收到一个对key请求时先用布隆过滤器验证是key否存在,如果存在在进入缓存、数据库。可以使用Redisson做布隆过滤器。

布隆过滤器的算法原理:

它的底层主要是先去初始化一个比较大数组,里面存放的二进制0或1。在一开始都是0,当一个key来了之后经过3次hash计算,模于数组长度找到数据的下标然后把数组中原来的0改为1,这样的话,三个数组的位置就能标明一个key的存在。查找的过程也是一样的。

当然是有缺点的,布隆过滤器有可能会产生一定的误判,我们一般可以设置这个误判率,大概不会超过5%,其实这个误判是必然存在的,要不就得增加数组的长度,其实已经算是很划分了,5%以内的误判率一般的项目也能接受,不至于高并发下压倒数据库。

二、缓存击穿

1. 缓存击穿的理解

缓存击穿意思是指当一个热点key(例如:一场秒杀活动中),并发量很大。当前热点key缓存在某个时间点过期,此时有大量并发请求访问热点key,查询缓存已经过期未命中,并发请求都发送到数据库,导致数据库负载过大,甚至使数据库宕机或是故障。

2.解决方案

2.1 分布式互斥锁

当缓存失效时,并发请求进来时允许其中一个线程不立即去load db,先使用如 Redis 的 setnx 去设置一个互斥锁,当操作成功返回时再进行 load db的操作并回设缓存,否则重试get缓存的方法,其他线程则处于等待阶段,等这个线程回设缓存成功后然后从缓存中获取数据返回即可

2.1 设置逻辑过期key

①:在设置key的时候,设置一个过期时间字段一块存入缓存中,不给当前key设置过期时间
②:当查询的时候,从redis取出数据后判断时间是否过期
③:如果过期则开通另外一个线程进行数据同步,当前线程正常返回数据,这个数据不是最新

3.两种方案各有利弊

①:如果选择数据的强一致性,建议使用分布式锁的方案,性能上可能没那么高,锁需要等待,也有可能产生死锁的问题。

②:如果选择key的逻辑删除,则优先考虑的高可用性,性能比较高,但是数据同步这块做不到强一致。

三、缓存雪崩

1. 缓存雪崩的理解

缓存在一段时间内集体过期或者失效(也可能是redis宕机),发生大量的缓存击穿,所有的查询都落在数据库上,造成了缓存雪崩,数据库负载过大,导致数据库宕机或是故障。

缓存雪崩没有完美解决方案,尽量让失效时间点分散开,比如可以在原有的失效时间基础上增加一个随机值,比如1-5分钟随机,这样每一个缓存的过期时间的重复率就会降低,就很难引发集体失效的事件。

2.解决方案

2.1 缓存高可靠集群:
可以把缓存层设计成高可用的集群,即使个别节点、个别机器、甚至是机房宕掉,依然可以提供服务。

2.2 做二级缓存,或者双缓存策略:
采用多级缓存,本地进程作为一级缓存,redis作为二级缓存,不同级别的缓存设置的超时时间不同,即使某级缓存过期了,也有其他级别缓存兜底。

2.3 数据预热:
可以通过缓存reload机制,预先去更新缓存,再即将发生大并发访问前手动触发加载缓存不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀。

2.4 服务熔断或请求限流机制
在缓存失效后,通过服务熔断机制,暂停业务应用对缓存服务的访问,从而降低对数据库的访问压力。或者请求限流,我们在业务系统的请求入口前端控制每秒进入系统的请求数,避免过多的请求被发送到数据库。我们使用这两个机制,来降低雪崩对数据库和整个业务系统的影响。

0

评论区