Redis的那些事

Redis的那些事

讲到Redis,我就有几个疑问:

  • redis是存在于内存当中,那么肯定会占用内存,数据进内存简单,那怎么从内存移除?

  • redis的缓存穿透以及缓存击穿是什么?

  • redis是基于内存的非关系型数据库,那么持久化怎么保证?

  • redis在分布式环境下宕机怎么办?

针对前面提出的问题,我们一一来解答!

一、redis的内存淘汰机制

第一个问题的解答,就是要搞明白redis的内存淘汰机制,而redis对于淘汰总共有 8 中策略。

可以大概分为三类:全局淘汰、部分淘汰(设置了过期时间的keys中)、不淘汰

1、全局淘汰

  • allkeys-lru:淘汰长时间未使用的所有keys

  • allkeys-lfu:淘汰使用频率最少的所有keys

  • allkeys-random:随机在所有keys中淘汰一些key

2、部分淘汰

  • volatile-lru:淘汰范围:所有设置了 过期时间 的 keys,淘汰最久未使用的 key

  • volatile-lfu:淘汰范围:所有设置了 过期时间 的 keys,淘汰使用频次最少的 key

  • volatile-random:淘汰范围:所有设置了 过期时间 的 keys,随机淘汰 key

  • volatile-ttl:淘汰范围:所有设置了 过期时间 的 keys,淘汰 剩余时间最少的 key

3、 不淘汰

  • noeviction(默认策略):不淘汰key,直接返回错误。

从上面描述的8种淘汰策略,我们可以看出 由于Redis中会存放大量的重要数据,但是又因为是基于内存的数据库,内存资源十分宝贵,所以我们要在这些数据间进行抉择,哪些更值得留下来。

所以我们应该如何去选择使用哪种策略呢?这些策略又有哪些优缺点呢?

针对以上策略,我们还可以根据其算法类型进行划分:

  • LRU算法

  • LFU算法

  • TTL算法(移除剩余时间最少的key)

LRU算法

LRU算法(Least Recently Used):即最近最少使用的淘汰,这也是操作系统比较经典的内存淘汰算法,将最近使用最少的数据淘汰掉,因为太久没使用了,所以认为暂时不会使用了。

通过上述的描述,我们可以知道,这种算法可以把我们最近常使用的数据保留下来,这样做对于CPU来说是比较友好的,不会频繁的将数据调入或调出内存。而redis采用的是近似LRU的算法,redis中会随机选取出5个(默认)key,从中淘汰掉最少使用的key。

为什么不从所有的key中找到最少使用的key淘汰呢?

这是因为redis中存有大量数据时,如果每次淘汰都对整个redis中的数据进行扫描判断,那将会大大折扣速度,尤其是我们通常选用redis是因为其强大的高并发处理能力,可以支持频繁的修改操作,若redis已经满了,但是还有1k个数据要进行储存,这样就会让redis一直在扫描全表,这显然是非常消耗时间的。

Redis在3.0版本中对近似LRU算法进行了优化

新算法会维护一个候选池(大小为16),池中的数据根据访问时间进行排序,第一次随机选取的key都会放入池中,随后每次随机选取的key只有在访问时间小于池中最小的时间才会放入池中,直到候选池被放满。当放满后,如果有新的key需要放入,则将池中最后访问时间最大(最近被访问)的移除。

当需要淘汰的时候,则直接从池中选取最近访问时间最小(最久没被访问)的key淘汰掉就行。

LFU算法

LFU(Least Frequently Used)算法是 Redis4.0新增的淘汰策略,即根据访问的频率进行淘汰,更能反映数据是否被频繁访问,确定其是否为热点数据。

接下来讨论下,应该怎么使用哪种策略呢

  • 当数据没有明显的冷热之分,可以使用 allkeys-random 策略

  • 如果需要最近的热点数据,那最好使用allkeys-lru或者volatile-lru算法。

  • 若需要依靠对数据的访问频率,则使用LFU算法的相关策略。

  • 选用allkeys还是volatile,可以根据有些数据是要常驻内存的,不能被淘汰的。

选用淘汰策略很重要,若没有选择好淘汰策略会导致什么问题?

二、redis过期key的删除策略

1、定时删除

  • 在设置key的过期时间的同时,创建一个定时器,让定时器在key的过期时间来临时,立即执行对key的删除操作;

  • 定时删除操作对于内存来说是友好的,内存不需要操作,而是通过使用定时器,可以保证尽快的将过期key删除,但是对于CPU来说不是友好的,如果过期key比较多的话,起的定时器也会比较多,每一个定时器会占用到CPU的资源;

2、惰性删除

  • 不管key有没有过期都不主动删除,但是每次从键空间中获取键值时,都检查取得的key的过期时间,如果过期的话,返回null,然后删除key即可;

  • 惰性操作对于CPU来说是友好的,过期key只有在程序读取时判断是否过期才删除掉,而且也只会删除这一个过期键,但是对于内存来说是不友好的,如果多个key都已经过期了,而这些key又恰好没有被访问,那么这部分的内存就都不会被释放出来;

3、定期删除

  • 每隔一段时间,程序就对数据库进行一次检查,删除掉过期key;

  • 定期删除是上面两种方案的折中方案,每隔一段时间来删除过期key,并通过限制删除操作执行的时长和频率来减少删除操作对CPU时间的影响,除此之外,还有效的减少内存的浪费;但是该策略的难点在于间隔时长,这个需要根据自身业务情况来进行设置,并且可能由于扫描不及时,导致过期的key也被返回。

目前,Redis采用的是惰性删除+定期删除的方案;Redis 的定期扫描只会扫描设置了过期时间的键,Redis 通过一个单独的过期字典expires(可以看作是 hash 表)来保存设置了过期时间的数据过期的时间,所以不会出现扫描所有键的情况,即使如此,redis也是默认是每隔 100ms 就随机抽取过期字典中的 key,检查其是否过期,如果过期就删除。如果不定时随机抽查而是全部扫描,那么将可能有很长的时间导致服务对外不可用,这是无异于一场灾难。

由于是随机扫描,那么对于已经过期但没有被扫描到的key怎么办,这时就要靠 惰性删除,在获取某个 key 的时候,Redis 会检查一下 ,这个 key 如果设置了过期时间那么是否过期,如果过期了此时就会删除,返回null。

三、缓存雪崩、击穿和穿透

这三个redis的问题可以说是比较热点的,接下来我们一一来说。

  1. 缓存雪崩 && 缓存击穿

缓存雪崩就是指redis中有大量热点数据过期删除,但是这时又有大量请求要访问这些热点数据,由于缓存中没有这些数据,所以这些请求又被打到DB上,DB扛不住这些请求,就导致DB因为扛不住挂掉了,这时DB重启,但是这些请求还在,马上DDB又被打挂了。总得来说,就是因为热点数据的缺失,导致请求全部打到DB上,然后DB就在打挂和重启中循环。

缓存雪崩 就是 缓存击穿 更严重,一个是大面积的

在微服务中,如果打挂了一个服务的DB,那跟这个服务相关的其他服务也肯定会报错,如果没有做熔断的话,到时候肯定会一片服务报错。

解决缓存雪崩的办法

缓存雪崩的引起就是因为热点数据一下子全部过期导致

  • 将Redis热点数据的过期时间采取随机时间,这样就可以让热点数据一下子全部过期。

  • 如果Redis是集群部署的话,各个热点数据分布在不同的服务中,也能有效避免,但是还是会导致上述的问题

  • 设置热点数据永不过期,有更新操作只要更新缓存就行(比如运维更新了首页商品,那你刷下缓存就完事了,不要设置过期时间),电商首页的数据也可以用这个操作,保险。

综上所述,设置过期时间最好!

  1. 缓存穿透

缓存穿透是指缓存和数据库中都没有的数据,而用户不断发起请求。这时的用户很可能是攻击者,攻击会导致数据库压力过大,严重会击垮数据库。

举个例子:由于Redis中没有此数据,redis日子过的还挺舒坦,不过 MySQL 大哥就没我这么舒坦了,有时候遇到些烦人的请求,查询的数据不存在,MySQL 就要白忙活一场!不仅如此,因为不存在,我也没法缓存啊,导致同样的请求来了每次都要去让 MySQL 白忙活一场。这就是人们常说的缓存穿透

这样看来,MySQL心里苦啊,他就叫他的小弟redis,小弟能不能帮我想想办法挡掉这些请求啊,再这样下去我就要挂了。这时候就有一个新小弟解决这个问题——布隆过滤器,这里就不细讲了。它的主要作用就是擅长从超大的数据集中快速告诉你查找的数据存不存在(我的这位朋友有一点不靠谱,它告诉你存在的话不能全信,其实有可能是不存在的,不过它要是告诉你不存在的话,那就一定不存在)。

谈完缓存穿透,那就再谈谈遇到了该怎么去解决?

  • 首先我们可以对其进行一些参数进行校验,比如数据库的自增id都是大于0的, 这时候如果我用小于0 的id一直去向DB请求,一旦恶意攻击你的话,大量的这种请求将会很容易打崩DB。

  • 若缓存中和DB中都没有这个数据,则可以向缓存中写入这个键的值为null,或者错误等信息。然后缓存的时间可以设置 的小一点,5s这样。这样可以防止一个ip不断暴力的攻击。

  • 也可以在nginx网关那里配置,可以让运维对单个ip每秒的访问次数超过一定值就拉黑。

  • 布隆过滤器,他的原理也很简单就是利用高效的数据结构和算法快速判断出你这个Key是否在数据库中存在,不存在你return就好了,存在你就去查了DB刷新KV再return。

主要靠的还是参数校验和布隆过滤器,其余两点是防止有人恶意攻击。

针对Redis的一些基本问题已经总结完了,接下来对Redis集群高可用,主从同步,哨兵等知识点的问题继续进行深入了解~

一般避免以上情况发生我们从三个时间段去分析下:

发生前:Redis 高可用,主从+哨兵,Redis cluster,避免全盘崩溃。

发生中:本地 ehcache 缓存 + Hystrix 限流+降级,避免MySQL 被打死。

发生后:Redis 持久化 RDB+AOF,一旦重启,自动从磁盘上加载数据,快速恢复缓存数据。

参考资料:

阿里面试Redis最常问的三个问题:缓存雪崩、击穿、穿透(带答案)_敖 丙的博客-CSDN博客

Redis 内存淘汰策略,从根儿上理解 - 掘金

Redis的八种淘汰策略(五) - 掘金

Redis 缓存有哪些淘汰策略?_秃头大魔王_的博客-CSDN博客_redis缓存淘汰策略描述正确的是

我是 Redis,MySQL 大哥被我害惨了!_语言之家的博客-CSDN博客

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值