018-学习Redis-理解:缓存击穿、缓存穿透、缓存雪崩

一、缓存击穿

例子:
双十一抢某款手机,结果这款手机的key没有在redis,都去数据库进行获取该key,导致数据库的压力瞬间增大。
解决方案:
如果单纯设为永不过期,存在的问题是:当该key的数据库中的数据更新了,而缓存设为了永不过期,导致没有及时更新。
1.将该key不设置过期时间,在缓存对象添加一个属性表示过期时间,每次获取数据时,检验对象中的过期时间属性,如果数据即将过期,则异步发起一个线程主动更新缓存中的数据。但是这种方案可能导致请求拿到过期的值,看业务是否可以接受
2.将该key设为永不过期。加一个互斥锁保证缓存的单线程写操作。

1、Key的过期淘汰机制

Redis可以对存储在Redis中的缓存数据设置过期时间,比如我们获取的短信验证码一般十分钟过期,我们这时候就需要在验证码存进Redis时添加一个key的过期时间,但是这里有一个需要格外注意的问题就是:并非key过期时间到了就一定会被Redis给删除。

1.定期删除

Redis 默认是每隔 100ms 就随机抽取一些设置了过期时间的 Key,检查其是否过期,如果过期就删除。为什么是随机抽取而不是检查所有key?因为你如果设置的key成千上万,每100毫秒都将所有存在的key检查一遍,会给CPU带来比较大的压力。
存在的问题是:到了过期时间,并没有删除,可能长时间没有被【随机抽取到】,从而没有被删除。

2.惰性删除

定期删除由于是随机抽取可能会导致很多过期 Key 到了过期时间并没有被删除。所以用户在从缓存访问、获取这个key数据的时候,redis会检查这个key是否过期了,如果过期就删除这个key。这时候就会在查询的时候将过期key从缓存中清除。
存在的问题是:长时间没有用户,访问该key,就可能长时间在缓存当中了

3.内存淘汰机制

仅仅使用定期删除 + 惰性删除机制还是会留下一个严重的隐患:如果定期删除留下了很多已经过期的key,而且用户长时间都没有使用过这些过期key,导致过期key无法被惰性删除,从而导致过期key一直堆积在内存里,最终造成Redis内存块被消耗殆尽。那这个问题如何解决呢?这个时候Redis内存淘汰机制应运而生了。Redis内存淘汰机制提供了6种数据淘汰策略:

  • volatile-lru :从已设置过期时间的数据集中挑选最近最少使用的数据淘汰。
  • volatile-ttl :从已设置过期时间的数据集中挑选将要过期的数据淘汰。
  • volatile-random :从已设置过期时间的数据集中任意选择数据淘汰。
  • allkeys-lru :当内存不足以容纳新写入数据时移除最近最少使用的key。
  • allkeys-random :从数据集中任意选择数据淘汰。
  • no-enviction(默认) :当内存不足以容纳新写入数据时,新写入操作会报错。
    一般情况下,推荐使用 volatile-lru 策略,对于配置信息等重要数据,不应该设置过期时间,这样Redis就永远不会淘汰这些重要数据。对于一般数据可以添加一个缓存时间,当数据失效则请求会从DB中获取并重新存入Redis
    中。

2、缓存击穿

首先我们来看下请求是如何取到数据的:当接收到用户请求,首先先尝试从Redis缓存中获取到数据,如果缓存中能取到数据则直接返回结果,当缓存中不存在数据时从数据库获取数据,如果数据库成功取到数据,则更新Redis,然后返回数据
在这里插入图片描述

1.定义:

高并发的情况下,某个热门key突然过期,导致大量请求在Redis未找到缓存数据,进而全部去访问DB请求数据,引起DB压力瞬间增大。

2.解决方案:

缓存击穿的情况下一般不容易造成DB【数据库】的宕机,只是会造成对DB【数据库】的周期性压力。对缓存击穿的解决方案
一般可以这样:

  • Redis中的数据不设置过期时间【永不过期】,然后在缓存的对象上添加一个属性标识过期时间,每次获取到数据时,校验对象中的过期时间属性,如果数据即将过期,则异步发起一个线程主动更新缓存中的数据。但是这种方案可能会导致有些请求会拿到过期的值,就得看业务能否可以接受。
  • 如果要求数据必须是新数据,则最好的方案则为热点数据设置为永不过期,然后加一个互斥锁保证缓存的单线程写入。

二、缓存穿透

1.攻击者恶意传id=-1去查询,缓存中没有去数据库查询也没有。
2.客户查询某个id,缓存没有,去数据库查询也没有。

1、缓存穿透 定义:

缓存穿透是指查询缓存和DB中都不存在的数据。比如通过id查询商品信息,id一般大于0,攻击者会故意传id为-1去查询,由于缓存是不命中则从DB中获取数据,这将会导致每次缓存都不命中数据导致每个请求都访问DB,造成缓存穿透。

2、解决方案:

  • 利用互斥锁,缓存失效的时候,先去获得锁,得到锁了,再去请求数据库,请求完了以后,将数据返回。没得到锁,则休眠一段时间重试。那么访问时间也就提高了,并且用了锁,性能降低。
  • 采用异步更新策略,无论key是否取到值,都直接返回。value值中维护一个缓存失效时间,缓存如果过期,异步起一个线程去读数据库,更新缓存。需要做缓存预热(项目启动前,先加载缓存)操作。
  • 提供一个能迅速判断请求是否有效的拦截机制,比如,利用布隆过滤器(很大的集合,将常用合法请求包含了),内部维护一系列合法有效的key。迅速判断出,请求所携带的Key是否合法有效。如果不合法,则直接返回。
  • 如果从数据库查询的对象为空,也放入缓存,只是设定的缓存过期时间较短,比如设置为60秒。设置时间不能太长,可能是真的是用户在查询,数据库当时没有,后期数据库可能更新此数据库。不能放入缓存中永不过期,比如数据库以及更新了,导致用户一直查询的都是缓存当中的空数据,所以没有去数据库去请求。

三、缓存雪崩

缓存击穿是一个key失效.
缓存雪崩是同一时间段大量的数据集中失效了,大量访问去数据库进行查询,导致大量数据缓存击穿。
所有的压力导致在数据库当中。
从而去数据库当中大量的查询,会导致数据库的宕机。

1、定义:

缓存中如果大量缓存在一段时间内集中过期了,这时候会发生大量的缓存击穿现象,所有的请求都落在了DB上,由于查询数据量巨大,引起DB压力过大甚至导致DB宕机。

2、解决方案:

  1. 给缓存的失效时间,加上一个随机值,避免集体失效。如果Redis是集群部署,将热点数据均匀分布在不同的Redis库中也能避免全部失效的问题。【使用互斥锁,但是该方案吞吐量明显下降了,时间长】
  2. 设置热点数据永远不过期。可能拿到过期数据。
  3. 推荐:双缓存。我们有两个缓存,缓存A和缓存B。缓存A的失效时间为20分钟,缓存B不设失效时间。自己做缓存预热操作。然后细分以下几个小点
    1. 从缓存A读数据库,有则直接返回。如果A没有数据【例如20分钟失效了】,直接从B读数据【永不失效】直接返回。
    2. 从B读完以后,B会异步启动一个更新线程。
    3. 更新线程同时更新缓存A和缓存B。

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值