Redis 内存管理

Redis 给缓存数据设置过期时间有什么用?

一般情况下,我们设置保存的缓存数据的时候都会设置一个过期时间。为什么呢?

内存是有限且珍贵的,如果不对缓存数据设置过期时间,那内存占用就会一直增长,最终可能会导致 OOM 问题。通过设置合理的过期时间,Redis 会自动删除暂时不需要的数据,为新的缓存数据腾出空间。

Redis 自带了给缓存数据设置过期时间的功能,比如:

127.0.0.1:6379> expire key 60 # 数据在 60s 后过期
(integer) 1
127.0.0.1:6379> setex key 60 value # 数据在 60s 后过期 (setex:[set] + [ex]pire)
OK
127.0.0.1:6379> ttl key # 查看数据还有多久过期
(integer) 56

注意 ⚠️:Redis 中除了字符串类型有自己独有设置过期时间的命令 setex 外,其他方法都需要依靠 expire 命令来设置过期时间 。另外, persist 命令可以移除一个键的过期时间。

过期时间除了有助于缓解内存的消耗,还有什么其他用么?

很多时候,我们的业务场景就是需要某个数据只在某一时间段内存在,比如我们的短信验证码可能只在 1 分钟内有效,用户登录的 Token 可能只在 1 天内有效。

如果使用传统的数据库来处理的话,一般都是自己判断过期,这样更麻烦并且性能要差很多。

Redis 是如何判断数据是否过期的呢?

Redis 通过一个叫做过期字典(可以看作是 hash 表)来保存数据过期的时间。过期字典的键指向 Redis 数据库中的某个 key(键),过期字典的值是一个 long long 类型的整数,这个整数保存了 key 所指向的数据库键的过期时间(毫秒精度的 UNIX 时间戳)。

过期字典是存储在 redisDb 这个结构里的:

typedef struct redisDb {...dict *dict;     //数据库键空间,保存着数据库中所有键值对dict *expires   // 过期字典,保存着键的过期时间...
} redisDb;

在查询一个 key 的时候,Redis 首先检查该 key 是否存在于过期字典中(时间复杂度为 O(1)),如果不在就直接返回,在的话需要判断一下这个 key 是否过期,过期直接删除 key 然后返回 null。

Redis 过期 key 删除策略了解么?

如果假设你设置了一批 key 只能存活 1 分钟,那么 1 分钟后,Redis 是怎么对这批 key 进行删除的呢?

常用的过期数据的删除策略就下面这几种:

  1. 惰性删除:只会在取出/查询 key 的时候才对数据进行过期检查。这种方式对 CPU 最友好,但是可能会造成太多过期 key 没有被删除。
  2. 定期删除:周期性地随机从设置了过期时间的 key 中抽查一批,然后逐个检查这些 key 是否过期,过期就删除 key。相比于惰性删除,定期删除对内存更友好,对 CPU 不太友好。
  3. 延迟队列:把设置过期时间的 key 放到一个延迟队列里,到期之后就删除 key。这种方式可以保证每个过期 key 都能被删除,但维护延迟队列太麻烦,队列本身也要占用资源。
  4. 定时删除:每个设置了过期时间的 key 都会在设置的时间到达时立即被删除。这种方法可以确保内存中不会有过期的键,但是它对 CPU 的压力最大,因为它需要为每个键都设置一个定时器。

Redis 采用的那种删除策略呢?

Redis 采用的是 定期删除+惰性/懒汉式删除 结合的策略,这也是大部分缓存框架的选择。定期删除对内存更加友好,惰性删除对 CPU 更加友好。两者各有千秋,结合起来使用既能兼顾 CPU 友好,又能兼顾内存友好。

下面是我们详细介绍一下 Redis 中的定期删除具体是如何做的。

Redis 的定期删除过程是随机的(

#define ACTIVE_EXPIRE_CYCLE_FAST_DURATION 1000 /* Microseconds. */
#define ACTIVE_EXPIRE_CYCLE_SLOW_TIME_PERC 25 /* Max % of CPU to use. */
#define ACTIVE_EXPIRE_CYCLE_ACCEPTABLE_STALE 10 /* % of stale keys after whichwe do extra efforts. */

周期性地随机从设置了过期时间的 key 中抽查一批),所以并不保证所有过期键都会被立即删除。这也就解释了为什么有的 key 过期了,并没有被删除。并且,Redis 底层会通过限制删除操作执行的时长和频率来减少删除操作对 CPU 时间的影响。

另外,定期删除还会受到执行时间和过期 key 的比例的影响:

  • 执行时间已经超过了阈值,那么就中断这一次定期删除循环,以避免使用过多的 CPU 时间。
  • 如果这一批过期的 key 比例超过一个比例,就会重复执行此删除流程,以更积极地清理过期 key。相应地,如果过期的 key 比例低于这个比例,就会中断这一次定期删除循环,避免做过多的工作而获得很少的内存回收。

Redis 7.2 版本的执行时间阈值是 25ms,过期 key 比例设定值是 10%

#define ACTIVE_EXPIRE_CYCLE_FAST_DURATION 1000 /* Microseconds. */
#define ACTIVE_EXPIRE_CYCLE_SLOW_TIME_PERC 25 /* Max % of CPU to use. */
#define ACTIVE_EXPIRE_CYCLE_ACCEPTABLE_STALE 10 /* % of stale keys after whichwe do extra efforts. */

每次随机抽查数量是多少?

expire.c中定义了每次随机抽查的数量,Redis 7.2 版本为 20 ,也就是说每次会随机选择 20 个设置了过期时间的 key 判断是否过期。

#define ACTIVE_EXPIRE_CYCLE_KEYS_PER_LOOP 20 /* Keys for each DB loop. */

如何控制定期删除的执行频率?

在 Redis 中,定期删除的频率是由 hz 参数控制的。hz 默认为 10,代表每秒执行 10 次,也就是每秒钟进行 10 次尝试来查找并删除过期的 key。

hz 的取值范围为 1~500。增大 hz 参数的值会提升定期删除的频率。如果你想要更频繁地执行定期删除任务,可以适当增加 hz 的值,但这会加 CPU 的使用率。根据 Redis 官方建议,hz 的值不建议超过 100,对于大部分用户使用默认的 10 就足够了。

下面是 hz 参数的官方注释,我翻译了其中的重要信息(Redis 7.2 版本)。

类似的参数还有一个 dynamic-hz,这个参数开启之后 Redis 就会在 hz 的基础上动态计算一个值。Redis 提供并默认启用了使用自适应 hz 值的能力,

这两个参数都在 Redis 配置文件 redis.conf中:

# 默认为 10
hz 10
# 默认开启
dynamic-hz yes

多提一嘴,除了定期删除过期 key 这个定期任务之外,还有一些其他定期任务例如关闭超时的客户端连接、更新统计信息,这些定期任务的执行频率也是通过 hz 参数决定。

为什么定期删除不是把所有过期 key 都删除呢?

这样会对性能造成太大的影响。如果我们 key 数量非常庞大的话,挨个遍历检查是非常耗时的,会严重影响性能。Redis 设计这种策略的目的是为了平衡内存和性能。

为什么 key 过期之后不立马把它删掉呢?这样不是会浪费很多内存空间吗?

因为不太好办到,或者说这种删除方式的成本太高了。假如我们使用延迟队列作为删除策略,这样存在下面这些问题:

  1. 队列本身的开销可能很大:key 多的情况下,一个延迟队列可能无法容纳。
  2. 维护延迟队列太麻烦:修改 key 的过期时间就需要调整期在延迟队列中的位置,并且,还需要引入并发控制。

大量 key 集中过期怎么办?

当 Redis 中存在大量 key 在同一时间点集中过期时,可能会导致以下问题:

  • 请求延迟增加: Redis 在处理过期 key 时需要消耗 CPU 资源,如果过期 key 数量庞大,会导致 Redis 实例的 CPU 占用率升高,进而影响其他请求的处理速度,造成延迟增加。
  • 内存占用过高: 过期的 key 虽然已经失效,但在 Redis 真正删除它们之前,仍然会占用内存空间。如果过期 key 没有及时清理,可能会导致内存占用过高,甚至引发内存溢出。

为了避免这些问题,可以采取以下方案:

  1. 尽量避免 key 集中过期: 在设置键的过期时间时尽量随机一点。
  2. 开启 lazy free 机制: 修改 redis.conf 配置文件,将 lazyfree-lazy-expire 参数设置为 yes,即可开启 lazy free 机制。开启 lazy free 机制后,Redis 会在后台异步删除过期的 key,不会阻塞主线程的运行,从而降低对 Redis 性能的影响。

Redis 内存淘汰策略了解么?

相关问题:MySQL 里有 2000w 数据,Redis 中只存 20w 的数据,如何保证 Redis 中的数据都是热点数据?

Redis 的内存淘汰策略只有在运行内存达到了配置的最大内存阈值时才会触发,这个阈值是通过redis.confmaxmemory参数来定义的。64 位操作系统下,maxmemory 默认为 0 ,表示不限制内存大小。32 位操作系统下,默认的最大内存值是 3GB。

你可以使用命令 config get maxmemory 来查看 maxmemory的值。

config get maxmemory
maxmemory
0

Redis 提供了 6 种内存淘汰策略:

  1. volatile-lru(least recently used):从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰。
  2. volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰。
  3. volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰。
  4. allkeys-lru(least recently used):从数据集(server.db[i].dict)中移除最近最少使用的数据淘汰。

  5. allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰。
  6. no-eviction(默认内存淘汰策略):禁止驱逐数据,当内存不足以容纳新写入数据时,新写入操作会报错。

4.0 版本后增加以下两种:

  1. volatile-lfu(least frequently used):从已设置过期时间的数据集(server.db[i].expires)中挑选最不经常使用的数据淘汰。
  2. allkeys-lfu(least frequently used):从数据集(server.db[i].dict)中移除最不经常使用的数据淘汰。

allkeys-xxx 表示从所有的键值中淘汰数据,而 volatile-xxx 表示从设置了过期时间的键值中淘汰数据。

config.c中定义了内存淘汰策略的枚举数组:

configEnum maxmemory_policy_enum[] = {{"volatile-lru", MAXMEMORY_VOLATILE_LRU},{"volatile-lfu", MAXMEMORY_VOLATILE_LFU},{"volatile-random",MAXMEMORY_VOLATILE_RANDOM},{"volatile-ttl",MAXMEMORY_VOLATILE_TTL},{"allkeys-lru",MAXMEMORY_ALLKEYS_LRU},{"allkeys-lfu",MAXMEMORY_ALLKEYS_LFU},{"allkeys-random",MAXMEMORY_ALLKEYS_RANDOM},{"noeviction",MAXMEMORY_NO_EVICTION},{NULL, 0}
};

你可以使用 config get maxmemory-policy 命令来查看当前 Redis 的内存淘汰策略。

> config get maxmemory-policy
maxmemory-policy
noeviction

可以通过config set maxmemory-policy 内存淘汰策略 命令修改内存淘汰策略,立即生效,但这种方式重启 Redis 之后就失效了。修改 redis.conf 中的 maxmemory-policy 参数不会因为重启而失效,不过,需要重启之后修改才能生效。

maxmemory-policy noeviction

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/487401.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Day2——需求分析与设计

教师端签到应用软件的需求分析; 产品经理如何写好产品需求文档(附模板) 需求分析是软件开发过程中的关键步骤,它确保了开发的软件能够满足用户的需求。以下是进行需求分析的具体步骤: 1. 确定分析目标 明确教师端签到…

Python爬虫——HTML中Xpath定位

Xpath是一种路径查询语言。利用一个路径表达式从html文档中找到我们需要的数据位置,进而将其写入到本地或者数据库中。 学习Xpath爬虫,我们首先学习一下python中lxml库 关于库 lxml 终端下载Xpath需要用到的模块 pip install lxml 关于HTML 超文本标…

AI如何让PPT制作变得轻松与智能?用一键生成ppt!

谁还愿意把时间浪费在PPT的设计和内容排版上?尤其是对于那些需要频繁制作演示文稿的人来说,一份看起来专业的PPT往往会让人陷入“做与不做”的困境。但随着科技的飞速发展,传统的PPT制作方法正逐渐被更为高效的工具所取代,尤其是智…

树莓派 PICO RP2040 MACOS 使用

文章参考: Developing in C on the RP2040: macOS | Wellys Dev 这里会提示报错:ln: /bin/picotool: Operation not permitted 参考:Mac ln命令报错:Operation not permitted_ln operation not permitted-CSDN博客 放在 usr/lo…

React第十七章(useRef)

useRef 当你在React中需要处理DOM元素或需要在组件渲染之间保持持久性数据时,便可以使用useRef。 import { useRef } from react; const refValue useRef(initialValue) refValue.current // 访问ref的值 类似于vue的ref,Vue的ref是.value,其次就是vu…

xtu oj 制药

文章目录 回顾前言代码思路 回顾 AB III问题 H: 三角数问题 G: 3个数等式 数组下标查询,降低时间复杂度1405 问题 E: 世界杯xtu 数码串xtu oj 神经网络xtu oj 1167 逆序数(大数据)xtu oj 原根xtu oj 不定方程的正整数解xtu oj 最多的可变换字…

【Linux】VMware 安装 Ubuntu18.04.2

ISO镜像安装步骤 选择语言 English 选择键盘布局 English 选择系统 Ubuntu 虚拟机网卡地址,默认即可 代理地址,默认空即可 镜像地址,修改成阿里云地址 选择第二项,LVM 磁盘扩容技术 第一块硬盘名sda,默认…

Java项目实战II基于微信小程序的跑腿系统(开发文档+数据库+源码)

目录 一、前言 二、技术介绍 三、系统实现 四、核心代码 五、源码获取 全栈码农以及毕业设计实战开发,CSDN平台Java领域新星创作者,专注于大学生项目实战开发、讲解和毕业答疑辅导。获取源码联系方式请查看文末 一、前言 在快节奏的现代生活中&…

透彻理解并解决Mockito模拟框架的单元测试无法运行的问题

本篇的实例基于Maven IDE (VS Code) 运行 在VS Code 运行的时候, 不需要在pom.xml 中添加任何插件就可以在测试类中看到如下的绿色按钮,单击就可以运行使用Mockito 注解 ExtendWith(MockitoExtension.class) 或是 Mockito 代码方式的测试。 不使用注…

CUDA学习笔记(1):安装和起步-1

CUDA安装和起步 写在前面硬件的准备学习的目的Pytorch学习CUDA方向 CUDA相关的书寻找CUDA的示例 安装Vsual Studio 2019下载CUDA和安装首先检查,自己的电脑是否已装过CUDA(读作:kuda)查看cuda-samples README.md,要求的…

获取pdf文件文字图片内容以及在PDF中插入图片

该功能用到如下jar包: //获取图片数据public void huoqu() throws IOException {File file new File("C:\\Users\\xiaomage\\Desktop\\123.pdf");PDDocument document Loader.loadPDF(file);/* PDFTextStripper stripper new PDFTextStripper();String…

【C#】NET 9中LINQ的新特性-CountBy

前言 在 .NET 中,使用 LINQ 对元素进行分组并计算它们的出现次数时,需要通过两个步步骤。首先,使用 GroupBy方法根据特定键对元素进行分类。然后,再计算每个组元素包含个数。而随着 .NET 9 版本发布,引入了一些新特性。其中 LINQ 引入了一种新的方法 CountBy,本文一起来了…

若依集成更好用的easyexcel

背景 若依使用的是apach poi并在此基础上进行封装apach poi的原生的api是很复杂的,若依简化了了此操作apach poi的上传速率和下载速率都是没有优化的,依赖于文件大小的限制在此前提下,如果没法满足客户的需求(超大型文件的上传&am…

阿里内部正式开源“Spring Cloud Alibaba (全彩小册)”

年轻的毕业生们满怀希望与忐忑,去寻找、竞争一个工作机会。已经在职的开发同学,也想通过社会招聘或者内推的时机争取到更好的待遇、更大的平台。 然而,面试人群众多,技术市场却相对冷淡,面试的同学们不得不面临着 1 个…

Android问题记录 - Inconsistent JVM-target compatibility detected for tasks

文章目录 前言开发环境问题描述问题分析解决方案补充内容最后 前言 前段时间升级Android Studio后修复了一堆问题,详情请看:Android问题记录 - 适配Android Studio Ladybug/Java 21/AGP 8.0(持续更新)。我以为问题已经全部解决了…

分布式专题(1)之Redis持久化、主从与哨兵架构详解

一、Redis持久化 1.1 RDB快照(snapshot) 在默认的情况下,Redis将内存数据快照保存名字为:dump.rdb的二进制文件中,当然你在配置文件redis.conf中修改对应的二进制文件名。 redis开启RDB快照,可以在redis中…

Y20030053 JSP+SSM+MYSQL+LW+旅游系统的设计与实现 源码 配置 文档 全套资料

旅游系统的设计与实现 1.摘要2.开发目的和意义3.系统功能设计4.系统界面截图5.源码获取 1.摘要 摘 要 随着旅游业的蓬勃发展和人们对休闲度假需求的不断增加,旅游业管理面临着越来越多的挑战。为了提高管理效率、优化客户体验并增强市场竞争力,本文介绍…

TypeScript进阶

Typescript进阶 基础知识 JavaScript 的核心特点就是灵活,但随着项目规模的增大,灵活反而增加开发者的心智负担。例如在代码中一个变量可以被赋予字符串、布尔、数字、甚至是函数,这样就充满了不确定性。而且这些不确定性可能需要在代码运行…

github操作学习笔记

git开源的分布式版本控制系统: 每次修改文件提交后,都会自动创建一个项目版本 查看git版本看有没有安装成功:git --version 把默认编辑器设置成vim:git config --global core.editor "vim" 1、设置昵称和邮箱&#xff…

QT 多级嵌套结构体,遍历成员--半自动。<模板+宏定义>QTreeWidget树结构显示

Qt的QTreeWidget来显示嵌套结构体的成员&#xff0c;并以树形结构展示。 #include <QApplication> #include <QTreeWidget> #include <QTreeWidgetItem> #include <QString> #include <cstdint>// 假设这些是你的结构体定义 struct BaseMeterPa…