Redis:缓存击穿、缓存穿透与缓存雪崩的区别、解决方案

0、前言

        近期学习redis相关原理,记录一下开发过程中Redis的一些常见问题及应对方法。

1、缓存穿透

一句话总结:先查redis发现没数据,再去数据库查发现还是没数据

这种情况下缓存永远不会生效,数据库将承担巨大压力。

        我们知道,redis的缓存作用,是在客户端发起查询请求时:
        (1)先找redis,如果redis内命中数据则直接返回。
        (2)如果未命中,则不得不去数据库中查询,把数据放到redis里,然后再返回数据给客户端。
        对于缓存穿透的情况,例如多次恶意查询数据库和redis里都没有的数据,将给数据库带来巨大压力。

常见解决方法:

         缓存空对象,就是发现数据库也没有这个数据的时候,缓存一个空值在redis里,那么下次这种访问就会从redis里返回null,而不会继续查询数据库了。

2. 缓存雪崩

一句话:大量的缓存key同时失效(比如同时过期了),或者redis服务宕机(redis寄了),导致大量请求直接给到数据库,压力过大寄了。

        缓存雪崩,指的是大面积的 key 同时过期,导致大量并发打到我们的数据库。不像击穿,只是因为 1 个 key 的过期。所以,对于雪崩来说,一般少量的 key 失效,所带来的数据库的并发压力是不会太大的。而大量 key 的同时失效,导致所有 key 的并发加起来,会影响到我们的数据库。

        那就算一个 key 失效,也会对数据库造成很大的影响,那么你把雪崩的所有 key 拆成一个一个 key 来看,也就是雪崩可以拆分成一个一个缓存击穿的集合。

        其实在真实场景中,雪崩才是一个更容易发生的一个问题,它不像击穿那么极端,一个 key 就成千上万的并发,直接把数据库打垮了;而是,可能就一个 key 几十几百的并发,然后大量的 key 一过期,然后就使得好多并发,同时叠加起来,累积到上千上万个,把数据库打崩了。

解决方案

        怕key同时失效,那就把缓存的TTL时间分散开, 比如在失效时间的基础上加上一个随机值。

        针对redis宕机情况,可以进行Redis集群的布置,或者添加多级缓存,增强系统的容错性。

3、缓存击穿

        概括:要查的key对应的数据是存在的(不像穿透,是不存在的数据),但redis里没有(比如过期了,这很常见),如果这个key的数据是热点数据,即可能会有大量并发请求

        这么多的并发请求都发现redis没有对应的数据,则都要去查数据库,这样一来将造成巨大压力。

         例如下图中,在线程1把数据写入缓存之前,其他多个并发线程都会去查数据库,这样将造成巨大的压力。

 解决方案

        有的人可能会觉得,热点key过期容易导致缓存击穿,那我们把它的存活时间设成永久不就好了?
        这样肯定是不可以的,毕竟redis空间有限,不可能缓存那么多数据,而且“热点数据”也是会随时间变化的,也不可能把所有数据当成热点数据来缓存。

方案1:互斥锁(开发常用)

        假设会有 1w 个并发来访问这个 key,那么它们就会先查询 redis,然后都发现,这个 key 不存在;然后,它们就会对应的,往 redis 用 setnx 设置一个 key,来表示这是一把锁;

        这里介绍一下redis的setnx,该方法的作用可以标识锁,什么意思呢?

        setnx的使用方法是这样的:
        setnx key value

        如果key没有value,设置值成果。如果key已经有对应value,设置失败。

        类似于我们的互斥。上锁就是赋值、释放锁就是删掉锁(del key即可)

        一般会设置有效期,防止死锁。


        该方法通过传入锁的id,返回一个布尔值,来模拟获取锁的过程。

        然后,只有一个线程,会设置成功,然后去读取数据库,写回 redis;其他的 9999 个线程,则 sleep 一小会,然后再去访问我们的 redis。
        有人看到这,首先会问,这个 sleep 要多久?
        这个是要根据压测,以及线上环境进行调整的,一般会给出一个合适的值,也就是大约从数据库取出数据的时间。

 

 

 方案2:逻辑过期

        这种情况,我们假设key不会过期,而是通过人为地设置”expire time“一个属性,来进行人为地判断数据是否过期(比如,热门商品,那就在活动期间把它变为不会过期地key,等活动过了以后在主动让他过期)。

        中心思路就是,线程1抢到锁以后,将查数据库、返回缓存,这项比较耗时的任务交给别的线程(线程2)去做,然后多个并发线程就可以先返回过期的数据,等更新好了再返回新的。

        这样避免了大量线程休眠等待的情况。

 

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

好奇的7号

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值