百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

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

nanshan 2025-03-01 14:50 17 浏览 0 评论

前言

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

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

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

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

那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-lru、allkeys-random、allkeys-lfu

会在设置过期时间数据中淘汰的:volatile-lru、volatile-random、volatile-ttl、volatile-lfu

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

Redis淘汰策略详解

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

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

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

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

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

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

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

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

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

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

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

LRU(Least Recently Used)

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

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



你看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原来的24bit的lru字段拆分成Idt值和 counter 值两部分。其中Idt 是lru字段的前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取不同值,不同访问次数,计数器值的变化情况。



从表中可以看到,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分钟没有访问,那么 counter 减 N。

总结

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

相关推荐

使用nginx配置域名及禁止直接通过IP访问网站

前段时间刚搭建好这个网站,一直没有关注一个问题,那就是IP地址也可以访问我的网站,今天就专门研究了一下nginx配置问题,争取把这个问题研究透彻。1.nginx配置域名及禁止直接通过IP访问先来看n...

如何在 Linux 中使用 PID 号查找进程名称?

在Linux的复杂世界中,进程是系统运行的核心,每个进程都由一个唯一的「进程ID」(PID)标识。无论是系统管理员在排查失控进程,还是开发者在调试应用程序,知道如何将PID映射到对应的进程名称都是一项...

Linux服务器硬件信息查询与日常运维命令总结

1.服务器硬件信息查询1.1CPU信息查询命令功能描述示例lscpu显示CPU架构、核心数、线程数等lscpucat/proc/cpuinfo详细CPU信息(型号、缓存、频率)cat/proc/c...

Ubuntu 操作系统常用命令详解(ubuntu常用的50个命令)

UbuntuLinux是一款流行的开源操作系统,广泛应用于服务器、开发、学习等场景。命令行是Ubuntu的灵魂,也是高效、稳定管理系统的利器。本文按照各大常用领域,详细总结Ubuntu必学...

从 0 到 1:打造基于 Linux 的私有 API 网关平台

在当今微服务架构盛行的时代,API网关作为服务入口和安全屏障,其重要性日益凸显。你是否想过,不依赖商业方案,完全基于开源组件,在Linux上构建一个属于自己的私有API网关平台?今天就带你...

Nginx搭建简单直播服务器(nginx 直播服务器搭建)

前言使用Nginx+Nginx-rtmp-module在Ubuntu中搭建简单的rtmp推流直播服务器。服务器环境Ubuntu16.04相关概念RTMP:RTMP协议是RealTi...

Linux连不上网?远程卡?这篇网络管理指南你不能错过!

大家好!今天咱们聊个所有Linux用户都躲不开的“老大难”——网络管理。我猜你肯定遇到过这些崩溃时刻:新装的Linux系统连不上Wi-Fi,急得直拍桌子;远程服务器SSH连不上,提示“Connecti...

7天从0到上线!手把手教你用Python Flask打造爆款Web服务

一、为什么全网开发者都在疯学Flask?在当今Web开发的战场,Flask就像一把“瑞士军刀”——轻量级架构让新手3天速成,灵活扩展能力又能支撑百万级用户项目!对比Django的“重型装甲”,Flas...

nginx配置文件详解(nginx反向代理配置详解)

Nginx是一个强大的免费开源的HTTP服务器和反向代理服务器。在Web开发项目中,nginx常用作为静态文件服务器处理静态文件,并负责将动态请求转发至应用服务器(如Django,Flask,et...

30 分钟搞定 Docker 安装与 Nginx 部署,轻松搭建高效 Web 服务

在云计算时代,利用容器技术快速部署应用已成为开发者必备技能。本文将手把手教你在阿里云轻量应用服务器上,通过Docker高效部署Nginx并发布静态网站,全程可视化操作,新手也能轻松上手!一、准...

Nginx 配置实战:从摸鱼到部署,手把手教你搞定生产级配置

各位摸鱼搭子们!今天咱不聊代码里的NullPointerException,改聊点「摸鱼必备生存技能」——Nginx配置!先灵魂拷问一下:写了一堆接口却不会部署?服务器被恶意请求打崩过?静态资源加载...

如何使用 Daphne + Nginx + supervisor部署 Django

前言:从Django3.0开始支持ASGI应用程序运行,使Django完全具有异步功能。Django目前已经更新到5.0,对异步支持也越来越好。但是,异步功能将仅对在ASGI下运行的应用程序可用...

Docker命令最全详解(39个最常用命令)

Docker是云原生的核心,也是大厂的必备技能,下面我就全面来详解Docker核心命令@mikechen本文作者:陈睿|mikechen文章来源:mikechen.cc一、Docker基本命令doc...

ubuntu中如何查看是否已经安装了nginx

在Ubuntu系统中,可以通过以下几种方法检查是否已安装Nginx:方法1:使用dpkg命令(适用于Debian/Ubuntu)bashdpkg-l|grepnginx输出...

OVN 概念与实践(德育概念的泛化在理论和实践中有什么弊端?)

今天我们来讲解OVN的概念和基础实践,要理解本篇博客的内容,需要前置学习:Linux网络设备-Bridge&VethPairLinux网络设备-Bridge详解OVS+Fa...

取消回复欢迎 发表评论: