Redis是怎么解决缓存占满内存的

3A网络
• 阅读 320

Redis最为常用的是拿来做缓存,而Redis之所以这么快的原因之一是搭上了内存那纳秒级别的处理速度来存储数据,极大提升了应用服务的性能。(从用户角度翻译过来就是这玩意反应快了)

但是,但凡技术总有它的局限性,例如在计算机中内存空间远比磁盘空间要小得多,而且内存比磁盘贵。所以我们要是把数据都放内存,显然是一件成本高,性价很低的事情。

所以更多的是采取让Redis存放热数据,从统计上来说,在大部分业务场景中,按二八定律,是20%的数据贡献了的访问量和访问频率可能接近或超过80%(当然总有部分例外)。

但是,内存空间大小就这么多,随着业务缓存数据量不断增多,不可避免就会将有限的内存空间不小心给占满。

Redis是怎么解决缓存占满内存的

那Redis是怎么解决缓存占满内存的?

我们先来看Java,使用Java都知道,Java是运行在JVM上的,而JVM的一大亮点就是拥有不用让C或C++的同学一样去关心内存回收情况,也就是垃圾回收机制。Redis也有自己的内存回收机制,但是相对JVM来说,Redis要"简单"一些,因为Redis内存回收机制主要两个方面的策略。

Redis内存回收机制策略

Redis 删除过期键策略

惰性删除:

顾名思义,惰性删除并不主动做任何操作,而是当客户端读取到设置了超时的键时,如果已经超过过期时间就会删除。但是很明显的问题就是当过期键一直没有访问到而及时删除,那么就会导致不能让内存及时释放。

定时删除:

定时删除实际上就是在Redis内部开启了一个定时任务,通过默认每秒定时的运行多少次和按键过期比例以及快慢的速率模式去回收键。

除了删除过期键策略当然还远不够,所以就进一步通过算法来筛选数据淘汰的淘汰策略。

Redis淘汰策略

但是不管前面的删除过期键策略,还是淘汰策略目的本身都是来防止内存溢出的这一点。Redis淘汰策略提供了8种淘汰策略,Redis4.0实现了6种淘汰策略,4.0之后又增加了2种策略,所以Redis有 8 种淘汰策略

可以分成两类:

  • 不淘汰数据的策略,仅有 noeviction 一种。
  • 会淘汰数据的有7种策略。

我们这里主要关注淘汰数据的7种策略,这7种细看可以再次归成两类:

会在所有数据中淘汰的: allkeys-lruallkeys-randomallkeys-lfu

会在设置过期时间数据中淘汰的:volatile-lruvolatile-randomvolatile-ttlvolatile-lfu

小伙伴们,我把熬夜整理的思维导图放在这了。

Redis是怎么解决缓存占满内存的

Redis淘汰策略详解

默认情况,当Redis的内存超过maxmemory时,noeviction是作为默认策略的,并不会淘汰任何数据。在Redis缓存一旦被占满之后的写请求都不会再处理,会直接的返回错误。

接下来是 allkeys-lruallkeys-randomallkeys-lfu 四种淘汰策略。它们会在设置过期时间的数据中进行淘汰,所以它们筛选的数据范围都在设置了过期键上。当数据过期时,即使缓存没有写满也会被淘汰删除。

volatile-ttl:根据键的ttl(生存时间值),删除设置过期时间最近的键,先过期的被先删除。

volatile-random:random也就是随机,设置了过期的 key 会随机的删除。

volatile-lru:在设置过期时间的 key,使用LRU算法筛选淘汰键。

volatile-lfu:在设置过期时间的 key,使用LFU算法筛选淘汰键。

allkeys-lruallkeys-randomallkeys-lfu 前缀都带着 all 这三种淘汰策略的淘汰数据范围包括了所有的键值,范围是所有键值就是无论是否设置过期时间都会进行淘汰。

allkeys-random:在所有键中随机淘汰数据。

allkeys-lru:在所有键中使用LRU算法筛选数据。

allkeys-lfu:在所有键中使用lfu算法筛选数据。

不管是 ttl 还是 random 算法规则是比较简单,而主要 lrulfu 算法也不复杂,让我们一起看看。

LRU(Least Recently Used)

LRU (Least Recently Used) 是最近最少使用原则,是将最近最不常用使用的数据进行筛选,最近不常使用的淘汰,最近常使用的数据留在缓存。

具体怎么筛选可以看下面的例子,假设在一块有限的空间里,最近访问会被移到顶端,最近没访问到的会移到末端,也就是LRU端。当空间被占满时,此时刚好有新增的数据时,就会把LRU端的末尾 key 替换淘汰掉。

Redis是怎么解决缓存占满内存的

你看LRU算法是不是很有用户体验 “如果有数据最近被访问过,那么再被访问的几率也会很高

那如果要去实现LRU算法,自然就需要有支撑它的数据结构,此时就可以使用链表,用链表来存放所有缓存数据。不过只使用链表,会有问题,那就是当面临数据量大的情况,链表的移动也会显得笨拙而带来耗时,进而影响Redis性能。

Redis自然不会放过这个可以优化的机会,所以Redis在LRU算法上动手脚。所以在一开始就记录每个数据最近一次被访问的时间戳。之后当Redis准备淘汰数据时,首先第一次随机的选出N个数据,然后将其作为候选集合,最后比较这N个数据携带的lru字段,最小的会从缓存中被淘汰。

Redis提供 maxmemory-samples 的配置参数,让Redis选出数据作为候选数据集。

当面临淘汰数据,Redis需要挑选数据,那么就会进到首次创建的淘汰候选集合。

挑选标准是:进入候选集合的数据lru属性值必须小于候选集合中最小的lru值。

当有新数据进入候选数据集后,如果候选数据集中的数据个数达到了maxmemory-samples,Redis就把候选数据集中lru字段值最小的数据淘汰出去。

你看这样一来Redis缓存就可以不用为不断增多的数据维护一个也不断增大的大链表,省去每次数据访问都移动链表的开销,缓存的性能就能得到提升。

LFU(Least Frequently Used)

LFU (Least Frequently Used)是最近最少频率使用,楞一看LFU缓存策略跟 LRU很相似,相似就对了,因为LFU就是在LRU的策略基础上优化出来的缓存策略。

LFU不同与LRU的是LFU把LRU原来的24bitlru字段拆分成Idt值和 counter 值两部分。其中Idtlru字段的前16bit 表示访问时间戳。counter 值是lru字段的后8bit 也就是表示访问次数。

LFU算法用的是访问频次递增和访问频次衰减两种方式。

访问频次递增

是通过counter来递增,但是它所能表示的最大值只有255,所以采用了更优的计数方式。每当数据被访问时,计数器值乘以lfu_log_factor再加1,取其倒数,得到p值;之后p值和取值范围 0 和 1 之间的随机数 r 比大小,当 p 值大于r值,计数器才加 1.

1/(baseval * lfu_log_factor + 1)

Redis官网 提供的一张表,当lfu_log_factor取不同值,不同访问次数,计数器值的变化情况。

Redis是怎么解决缓存占满内存的

从表中可以看到,lfu_log_factor 取值为1,访问次数100k时, counter值就到顶255,没法区分访问次数。当lfu_log_factor取值为100时,访问次数10M,counter值达到255,此时,访问次数小于10M的不同数据都可以通过counter值区分出来。

访问频次衰减

Redis实现LFU策略时,除了访问频次递增,还设计了一个衰减机制。因为从上可知,counter 一直递增会到达顶 255,而且纯粹的递增不能反应一个 key 的热度,所以 key 如果一段时间不被访问,counter也需要对应减少。

递减的速度由 lfu-decay-time 配置项控制 counter 的递减速度,默认值 1表示如果N分钟没有访问,那么 counterN

总结

我们围绕 Redis是怎么解决缓存占满内存展开了Redis的内存回收策略,Redis的内存回收策略有两个方面,删除过期键策略和淘汰策略,但是不管是删除过期键策略还是淘汰策略目的都是来控制防止内存溢出。在淘汰策略中,Redis4.0实现了6种淘汰策略,4.0之后又增加了2种策略,所以Redis一共有8 种淘汰策略。其中最为主要的LRU和LFU算法策略。LFU是在LRU的基础上的策略,但是LFU并不是用来替换LRU;它们各自的数据筛选侧重点不同,前者LRU策略侧重数据时效性,而后者LFU侧重访问频次。

朋友们,到这就接近尾声了。感兴趣的朋友可以在3A服务器上部署环境尝试一下。

点赞
收藏
评论区
推荐文章
peter peter
3年前
Go-连接Redis-学习go-redis包
Redis介绍Redis是一个开源的内存数据结构存储,常用作数据库、缓存和消息代理。目前它支持的数据结构有诸如string、hash、list、set、zset、bitmap、hyperloglog、geospatialindex和stream。Redis内置了复制、Lua脚本、LRU清除、事务和不同级别的磁盘持久性,并通过RedisSentinel
Stella981 Stella981
2年前
Redis 集群(11)
为什么需要集群?1、性能Redis本身的QPS已经很高了,但是如果在一些并发量非常高的情况下,性能还是会受到影响。这个时候我们希望有更多的Redis服务来完成工作。2、扩展第二个是出于存储的考虑。因为Redis所有的数据都放在内存中,如果数据量大,很容易受到硬件的限制。升级硬件收效和成本比太低,所以我们需要有
Stella981 Stella981
2年前
Redis存储方式RDB和AOF介绍及持久化的重要性
更多精彩和干货和你分享前言redis可以干很多事情,我们这里的背景是将redis作为缓存服务器还讨论的。如果部署了一个redis,主从也好,cluster也罢,我将一些数据存储在它上面,如果没有持久化的话,redis只会存储到内存中,那如果遇到灾难性故障,就会丢失所有数据。这对企业级的应用来说,是个噩梦。如果我们能将数据持久化
Stella981 Stella981
2年前
Redis 基础使用 及 队列、订阅
Redis介绍  Redis是一个开源,先进的keyvalue存储,并用于构建高性能,可扩展的Web应用程序的完美解决方案。  Redis从它的许多竞争继承来的三个主要特点:Redis数据库完全在内存中,使用磁盘仅用于持久性。相比许多键值数据存储,Redis拥有一套较为丰富的数据类型。
Stella981 Stella981
2年前
Redis持久化的几种方式——深入解析RDB
Redis 的读写都是在内存中,所以它的性能较高,但在内存中的数据会随着服务器的重启而丢失,为了保证数据不丢失,我们需要将内存中的数据存储到磁盘,以便Redis重启时能够从磁盘中恢复原有的数据,而整个过程就叫做Redis持久化。!image.png(https://oscimg.oschina.net/oscnet/232e657dae2
Stella981 Stella981
2年前
Redis持久化机制(文末有福利)
        上一篇主要针对Redis的内存淘汰机制以及Redis容易引发的三大问题:缓存击穿、缓存穿透以及缓存雪崩进行了详细的讲解以及提供了业界常用的解决方案。本篇主要讲讲Redis的持久化机制,Redis受开发者欢迎的一大原因就是因为可持久化的特性。我们如何保证Redis宕机之后重启可以将数据进行恢复?所以一般情
Stella981 Stella981
2年前
Redis常见问题
1、什么是Redis?回答:Redis是一个基于内存的高性能keyvalue数据库;应用场景:1)会话缓存(SessionCache)2)全页缓存(FPC)3)队列4)排行榜/计数器5)发布/订阅2、使用Redis有哪些好处?回答:1)速度快,因为数据存在内存中,类似于HashMap,HashM
Stella981 Stella981
2年前
Redis—持久化
一、持久化简介Redis的数据全部存储在内存中,如果突然宕机,数据就会全部丢失,因此必须有一套机制来保证Redis的数据不会因为故障而丢失,这种机制就是Redis的持久化机制,它会将内存中的数据库状态保存到磁盘中。持久化发生了什么|从内存到磁盘
Stella981 Stella981
2年前
SpringBoot 2,用200行代码完成一个一二级分布式缓存
缓存系统的用来代替直接访问数据库,用来提升系统性能,减小数据库负载。早期缓存跟系统在一个虚拟机里,这样内存访问,速度最快。后来应用系统水平扩展,缓存作为一个独立系统存在,如redis,但是每次从缓存获取数据,都还是要通过网络访问才能获取,效率相对于早先从内存里获取,还是不够逆天快。如果一个应用,比如传统的企业应用,一次页面显示,要访问数次redis,那效果
Stella981 Stella981
2年前
Redis 内存管理策略
背景Redis很多时候都是在使用内存,数据一直写,但内存是有限的,如果Redis内存满了,那么我们的很多缓存操作都会超时、失败,接着可能会引发雪崩。那么当内存达到阀值Redis是怎么处理的呢?配置内存限制maxmemory我们可以通过在配置文件中配置maxmemory来限制内存的最大使用情况。如果maxmem