缓存策略之缓存雪崩、穿透、并发、击穿问题

前言:作为一名中高级开发工程师我们在工作中或面试中避不开的是关于缓存的技术知识点。闲来无事做下相关策略设计方法.....

1.缓存穿透问题

缓存穿透指的是每次查询个别 key 时,key 在缓存系统不命中,此时应用系统就会从数据库中查询,如果数据库中存在这条数据,则获取数据并更新缓存系统。但如果数据库中也没有这条数据,这个时候就无法更新缓存,就会造成一个问题:查询缓存中不存在的数据时,每次都要查询数据库。(简单来说:缓存穿透是指缓存和数据库中都没有的数据,而用户不断发起请求)

 

那么如果有人利用“查询缓存中不存在的数据时,每次都要查询数据库”恶意攻击的话,数据库会承担非常大的压力,甚至宕机。

解决缓存穿透方案:

方案1:

给所有指定的 key 预先设定一个默认值,比如空字符串“Null”,当返回这个空字符串“Null”时,我们可以认为这是一个不存在的 key,在业务代码中,就可以判断是否取消查询数据库的操作,或者等待一段时间再请求这个 key。如果此时取到的值不再是“Null”,我们就可以认为缓存中对应的 key 有值了,这就避免出现请求访问到数据库的情况,从而把大量的类似请求挡在了缓存之中。

方案 2:

可以用布隆过滤器。布隆过滤器本质是一个二进制向量,是一个位数组,位数组就是数组的每个元素都只占用 1 bit 。每个元素只能是 0 或者 1。使用 K 个哈希函数对元素值进行 K 次计算,得到 K 个哈希值。 根据得到的哈希值,在位数组中把对应下标的值置为 1。首先初始化项目的时候从数据库查询出来所有的key值,然后放到布隆过滤器中;

 

2.缓存并发问题

假设在缓存失效的同时,出现多个客户端并发请求获取同一个 key 的情况,此时因为 key 已经过期了,所有请求在缓存数据库中查询 key 不命中,那么所有请求就会到数据库中去查询,然后当查询到数据之后,所有请求再重复将查询到的数据更新到缓存中。

这里就会引发一个问题,所有请求更新的是同一条数据,这不仅会增加数据库的压力,还会因为反复更新缓存而占用缓存资源,这就叫缓存并发。那你怎么解决缓存并发呢?

 

缓存并发设计思路:

1.首先,客户端发起请求,先从缓存中读取数据,判断是否能从缓存中读取到数据;

2.如果读取到数据,则直接返回给客户端,流程结束;

3.如果没有读取到数据,那么就在 Redis 中使用 setNX 方法设置一个状态位,表示这是一种锁定状态;

4.如果锁定状态设置成功,表示已经锁定成功,这时候请求从数据库中读取数据,然后更新缓存,最后再将数据返回给客户端;(关于分布式锁看之前一篇文章专门介绍

5.如果锁定状态没有设置成功,表示这个状态位已经被其他请求锁定,此时这个请求会等待一段时间再重新发起数据查询;

6.再次查询后发现缓存中已经有数据了,那么直接返回数据给客户端。

这样就能保证在同一时间只能有一个请求来查询数据库并更新缓存系统,其他请求只能等待重新发起查询,从而解决缓存并发的问题

 

3.缓存雪崩问题

我们在实际开发过程中,通常会不断地往缓存中写数据,并且很多情况下,程序员在开发时,会将缓存的过期时间设置为一个固定的时间常量(比如 1 分钟、5 分钟)。这就可能出现系统在运行中,同时设置了很多缓存 key,并且这些 key 的过期时间都一样的情况,然后当 key 到期时,缓存集体同时失效,如果此时请求并发很高,就会导致大面积的请求打到数据库,造成数据库压力瞬间增大,出现缓存雪崩的现象。

 

解决缓存雪崩方案:

方案1:将缓存失效时间随机打散: 我们可以在原有的失效时间基础上增加一个随机值(比如 1 到 10 分钟)这样每个缓存的过期时间都不重复了,也就降低了缓存集体失效的概率。

方案2:设置缓存不过期: 我们可以通过后台服务来更新缓存数据,从而避免因为缓存失效造成的缓存雪崩,也可以在一定程度上避免缓存并发问题。

 

4.如何设计一个缓存策略,可以动态缓存热点数据呢?

举电商平台场景中的例子,现在要求只缓存用户经常访问的 Top 1000 的商品。

那么缓存策略的总体思路:

@1:就是通过判断数据最新访问时间来做排名,并过滤掉不常访问的数据,只留下经常访问的数据,具体细节如下。

1.先通过缓存系统做一个排序队列(比如存放 1000 个商品),系统会根据商品的访问时间,更新队列信息,越是最近访问的商品排名越靠前。

2.同时系统会定期过滤掉队列中排名最后的 200 个商品,然后再从数据库中随机读取出 200 个商品加入队列中。

3.这样当请求每次到达的时候,会先从队列中获取商品 ID,如果命中,就根据 ID 再从另一个缓存数据结构中读取实际的商品信息,并返回。

4.在 Redis 中可以用 zadd 方法和 zrange 方法来完成排序队列和获取 200 个商品的操作。

@2:通过 MySQL Binlog + Canal + MQ 的方式。(canal 组件不了解的自行百度下)

比如用户在应用系统的后台添加一条配置信息,配置信息存储到了 MySQL 数据库中,同时数据库更新了 Binlog 日志数据,接着再通过使用 Canal 组件来获读取最新的 Binlog 日志数据,然后解析日志数据,并通过事先约定好的数据格式,发送到 MQ 消息队列中,最后再由应用系统将 MQ 中的数据更新到 Redis 中,这样也完成了缓存操作和业务代码之间的解耦。

找了张图如下:

 

5.缓存击穿问题

 缓存击穿是指缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力

解决方案:

  1. 设置热点数据永远不过期。
  2. 加互斥锁

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值