【Redis】Redis 的过期策略以及内存淘汰机制详解
Redis 的过期策略以及内存淘汰机制详解
- 1. Redis 的过期策略
- 1.1 如何设置 key 的过期时间?
- 1.2 key 设置且到了过期时间后,该 key 保存的数据还占据内存么?
- 1.3 Redis 如何删除过期的数据
- 1.3.1 定期删除
- 1.3.2 惰性删除
- 2. Redis 的内存淘汰机制
- 2.1 Redis 最大内存淘汰机制有哪些?
- 2.2 如何设置 Redis 最大内存?
- 2.3 如何设置 Redis 内存淘汰机制?
- 3. LRU 和 LFU 算法
- 3.1 概念
- 3.1.1 LRU(Least Recently Used)算法
- 3.1.2 LFU(Least Frequently Used)算法
- 3.2 区别
- 3.3 实现
- 3.3.1 LRU
- 3.3.2 LFU
- 4. 其他场景对过期 key 的处理
- 4.1 快照生成 RDB 文件时
- 4.2 服务重启载入 RDB 文件时
- 4.3 AOF 文件写入时
- 4.4 重写 AOF 文件时
- 4.5 主从同步时
1. Redis 的过期策略
Redis 在存储数据时,如果指定了过期时间,缓存数据到了过期时间就会失效,那么 Redis 是如何处理这些失效的缓存数据呢?这就用到了 Redis 的过期策略 - 定期删除 + 惰性删除。下面我们带着问题一起来学习 Redis 的过期策略。
1.1 如何设置 key 的过期时间?
- Redis 在指定 expire 时间后自动移除给定的键
设置 key 秒级精度的过期时间
EXPIRE key seconds
# 设置 key 毫秒级精度的过期时间
PEXPIRE key milliseconds
- Redis 在指定 unix 时间来临之后自动移除给定的键
设置 key 秒级精度的过期时间戳(unix timestamp)
EXPIREAT key seconds_timestamp
# 设置 key 毫秒级精度的过期时间戳(unix timestamp) 以毫秒计
PEXPIREAT key milliseconds_timestamp
1.2 key 设置且到了过期时间后,该 key 保存的数据还占据内存么?
当 key 过期后,该 key 保存的数据还是会占据内存的。
因为每当我们设置一个 key 的过期时间时,Redis 会将该键带上过期时间存放到一个过期字典中。当 key 过期后,如果没有触发 Redis 的删除策略的话,过期后的数据依然会保存在内存中的,这时候即便这个 key 已经过期,我们还是能够获取到这个 key 的数据。
1.3 Redis 如何删除过期的数据
Redis 使用:“定期删除 + 惰性删除” 策略删除过期数据。
1.3.1 定期删除
Redis 默认每隔 100ms 就随机抽取部分设置了过期时间的 key,检测这些 key 是否过期,如果过期了就将其删除。
- 100ms 是怎么来的?
定期任务是 Redis 服务正常运行的保障,它的执行频率由 hz 参数的值指定,默认为10,即每秒执行10次。
hz 10
5.0 之前的 Redis 版本,hz 参数一旦设定之后就是固定的了。hz 默认是 10。这也是官方建议的配置。如果改大,表示在 Redis 空闲时会用更多的 CPU 去执行这些任务。官方并不建议这样做。但是,如果连接数特别多,在这种情况下应该给与更多的 CPU 时间执行后台任务。
Redis 5.0之后,有了 dynamic-hz 参数,默认就是打开。当连接数很多时,自动加倍 hz,以便处理更多的连接。
dynamic-hz yes
- 为什么是随机抽取部分 key,而不是全部 key?
因为如果 Redis 里面有大量 key 都设置了过期时间,全部都去检测一遍的话 CPU 负载就会很高,会浪费大量的时间在检测上面,甚至直接导致 Redis 挂掉。所有只会抽取一部分而不会全部检查。
随机抽取部分检测,部分是多少?是由 redis.conf 文件中的 maxmemory-samples 属性决定的,默认为 5。
# The default of 5 produces good enough results. 10 Approximates very closely
# true LRU but costs more CPU. 3 is faster but not very accurate.
#
# maxmemory-samples 5
正因为定期删除只是随机抽取部分 key 来检测,这样的话就会出现大量已经过期的 key 并没有被删除,这就是为什么有时候大量的 key 明明已经过了失效时间,但是 Redis 的内存还是被大量占用的原因 ,为了解决这个问题,Redis 又引入了"惰性删除策略"。
1.3.2 惰性删除
惰性删除不是去主动删除,而是在你要获取某个 key 的时候,Redis 会先去检测一下这个 key 是否已经过期,如果没有过期则返回给你,如果已经过期了,那么 Redis 会删除这个 key,不会返回给你。
“定期删除 + 惰性删除” 就能保证过期的 key 最终一定会被删掉 ,但是只能保证最终一定会被删除,要是定期删除遗漏的大量过期 key,我们在很长的一段时间内也没有再访问这些 key,那么这些过期 key 不就一直会存在于内存中吗?不就会一直占着我们的内存吗?这样不还是会导致 Redis 内存耗尽吗?由于存在这样的问题,所以 Redis 又引入了"内存淘汰机制"来解决。
2. Redis 的内存淘汰机制
2.1 Redis 最大内存淘汰机制有哪些?
-
volatile-lru:当内存不足执行写入操作时,在设
置了过期时间的键空间中,移除最近最少(最长时间)使用的 key。 -
allkeys-lru:当内存不足执行写入操作时,在
整个键空间中,移除最近最少(最长时间)使用的 key。(这个是最常用的) -
volatile-lfu:当内存不足执行写入操作时,在
设置了过期时间的键空间中,移除最不经常(最少次)使用的key。 -
allkeys-lfu:当内存不足执行写入操作时,在
整个键空间中,移除最不经常(最少次)使用的key。 -
volatile-random -> 当内存不足执行写入操作时,在
设置了过期时间的键空间中,随机移除某个 key。 -
allkeys-random -> 当内存不足执行写入操作时,在
整个键空间中,随机移除某个 key。 -
volatile-ttl -> 当内存不足执行写入操作时,在
设置了过期时间的键空间中,优先移除过期时间最早(剩余存活时间最短)的 key。 -
noeviction:不删除任何 key, 只是在内存不足写操作时返回一个错误。(默认选项,一般不会选用)
2.2 如何设置 Redis 最大内存?
redis.conf 配置文件中的 maxmemory 属性限定了 Redis 最大内存使用量,当占用内存大于 maxmemory 的配置值时会执行内存淘汰机制。
# maxmemory <bytes>
当达到设置的内存使用限制时,Redis 将根据选择的内存淘汰机制(maxmemory-policy)删除 key。
2.3 如何设置 Redis 内存淘汰机制?
内存淘汰机制由 redis.conf 配置文件中的 maxmemory-policy 属性设置,没有配置时默认为 noeviction:不删除任何 策略
# maxmemory-policy noeviction
3. LRU 和 LFU 算法
3.1 概念
3.1.1 LRU(Least Recently Used)算法
@百度百科
LRU:最近最少使用淘汰算法(Least Recently Used)。LRU 是淘汰最近最久未使用的数据。
3.1.2 LFU(Least Frequently Used)算法
@百度百科
LFU:最不经常使用淘汰算法(Least Frequently Used)。LFU是淘汰一段时间内,使用次数最少的数据。
3.2 区别
LRU 关键是看最后一次被使用到发生替换的时间长短,时间越长,就会被淘汰;而 LFU 关键是看一定时间段内被使用的频率(次数),使用频率越低,就会被淘汰。
LRU 算法适合:较大的文件比如游戏客户端(最近加载的地图文件);
LFU 算法适合:较小的文件和教零碎的文件比如系统文件、应用程序文件。
LRU 消耗 CPU 资源较少,LFU 消耗 CPU 资源较多。
3.3 实现
3.3.1 LRU
这里用 leetcode 中一道面试题:LRU 缓存 来设计和构建一个 “最近最少使用” 缓存。

-
关键实现:
-
LRUCache(int capacity):容量,大于容量选择最久未使用资源淘汰。 -
get(key):如果密钥 (key) 存在于缓存中,则获取密钥的值(总是正数),否则返回 -1。 -
put(key, value):如果密钥不存在,则写入其数据值。当缓存容量达到上限时,它应该在写入新数据之前删除最近最少使用的数据值,从而为新的数据值留出空间。
-
public class LRUCache {private class Node{Node prev;Node next;int key;int value;public Node(int key, int value) {this.key = key;this.value = value;this.prev = null;this.next = null;}}private int capacity;private HashMap<Integer, Node> hs = new HashMap<Integer, Node>();private Node head = new Node(-1, -1);private Node tail = new Node(-1, -1);// @param capacity, an integerpublic LRUCache(int capacity) {// write your code herethis.capacity = capacity;tail.prev = head;head.next = tail;}// @return an integerpublic int get(int key) {// write your code hereif( !hs.containsKey(key)) {return -1;}// remove currentNode current = hs.get(key);current.prev.next = current.next;current.next.prev = current.prev;// move current to tailmove_to_tail(current);return hs.get(key).value;}// @param key, an integer// @param value, an integer// @return nothingpublic void set(int key, int value) {// write your code hereif( get(key) != -1) {hs.get(key).value = value;return;}if (hs.size() == capacity) {hs.remove(head.next.key);head.next = head.next.next;head.next.prev = head;}Node insert = new Node(key, value);hs.put(key, insert);move_to_tail(insert);}private void move_to_tail(Node current) {current.prev = tail.prev;tail.prev = current;current.prev.next = current;current.next = tail;}public static void main(String[] as) throws Exception {LRUCache cache = new LRUCache(3);cache.set(2, 2);cache.set(1, 1);System.out.println(cache.get(2));System.out.println(cache.get(1));System.out.println(cache.get(2));cache.set(3, 3);cache.set(4, 4);System.out.println(cache.get(3));System.out.println(cache.get(2));System.out.println(cache.get(1));System.out.println(cache.get(4));}
}
3.3.2 LFU
这里用 leetcode 中一道面试题:最不经常使用 LFU 缓存 算法设计并实现数据结构。

-
关键实现
-
LFUCache(int capacity)- 用数据结构的容量 capacity 初始化对象。 -
int get(int key)- 如果键 key 存在于缓存中,则获取键的值,否则返回 -1 。 -
void put(int key, int value)- 如果键 key 已存在,则变更其值;如果键不存在,请插入键值对。当缓存达到其容量 capacity 时,则应该在插入新项之前,移除最不经常使用的项。在此问题中,当存在平局(即两个或更多个键具有相同使用频率)时,应该去除 最近最久未使用 的键。
-
public class LFUCache {private static final int DEFAULT_MAX_SIZE = 3;private int capacity = DEFAULT_MAX_SIZE;//保存缓存的访问频率和时间private final Map<Integer, HitRate> cache = new HashMap<Integer, HitRate>();//保存缓存的KVprivate final Map<Integer, Integer> KV = new HashMap<Integer, Integer>();// @param capacity, an integerpublic LFUCache(int capacity) {this.capacity = capacity;}// @param key, an integer// @param value, an integer// @return nothingpublic void set(int key, int value) {Integer v = KV.get(key);if (v == null) {if (cache.size() == capacity) {Integer k = getKickedKey();KV.remove(k);cache.remove(k);}cache.put(key, new HitRate(key, 1, System.nanoTime()));} else { //若是key相同只增加频率,更新时间,并不进行置换HitRate hitRate = cache.get(key);hitRate.hitCount += 1;hitRate.lastTime = System.nanoTime();}KV.put(key, value);}public int get(int key) {Integer v = KV.get(key);if (v != null) {HitRate hitRate = cache.get(key);hitRate.hitCount += 1;hitRate.lastTime = System.nanoTime();return v;}return -1;}// @return 要被置换的keyprivate Integer getKickedKey() {HitRate min = Collections.min(cache.values());return min.key;}class HitRate implements Comparable<HitRate> {Integer key;Integer hitCount; // 命中次数Long lastTime; // 上次命中时间public HitRate(Integer key, Integer hitCount, Long lastTime) {this.key = key;this.hitCount = hitCount;this.lastTime = lastTime;}public int compareTo(HitRate o) {int hr = hitCount.compareTo(o.hitCount);return hr != 0 ? hr : lastTime.compareTo(o.lastTime);}}public static void main(String[] as) throws Exception {LFUCache cache = new LFUCache(3);cache.set(2, 2);cache.set(1, 1);System.out.println(cache.get(2));System.out.println(cache.get(1));System.out.println(cache.get(2));cache.set(3, 3);cache.set(4, 4);System.out.println(cache.get(3));System.out.println(cache.get(2));System.out.println(cache.get(1));System.out.println(cache.get(4));}
}
4. 其他场景对过期 key 的处理
4.1 快照生成 RDB 文件时
过期的 key 不会被保存在 RDB 文件中。
4.2 服务重启载入 RDB 文件时
Master 载入 RDB 时,文件中的未过期的键会被正常载入,过期键则会被忽略。Slave 载入 RDB 时,文件中的所有键都会被载入,当主从同步时,再和 Master 保持一致。
4.3 AOF 文件写入时
因为 AOF 保存的是执行过的 Redis 命令,所以如果 Redis 还没有执行 del,AOF 文件中也不会保存 del 操作,当过期 key 被删除时,DEL 命令也会被同步到 AOF 文件中去。
4.4 重写 AOF 文件时
执行 BGREWRITEAOF 时 ,过期的 key 不会被记录到 AOF 文件中。
4.5 主从同步时
Master 删除 过期 Key 之后,会向所有 Slave 服务器发送一个 DEL 命令,Slave 收到通知之后,会删除这些 Key。
Slave 在读取过期键时,不会做判断删除操作,而是继续返回该键对应的值,只有当 Master 发送 DEL 通知,Slave 才会删除过期键,这是统一、中心化的键删除策略,保证主从服务器的数据一致性。
相关文章:
【Redis】Redis 的过期策略以及内存淘汰机制详解
Redis 的过期策略以及内存淘汰机制详解1. Redis 的过期策略1.1 如何设置 key 的过期时间?1.2 key 设置且到了过期时间后,该 key 保存的数据还占据内存么?1.3 Redis 如何删除过期的数据1.3.1 定期删除1.3.2 惰性删除2. Redis 的内存淘汰机制2.…...
边缘云是什么?
涂鸦边缘云服务 旨在解决物联网边缘位置的连接需求和提高设备自主管理能力。并与涂鸦 IoT 云服务和 IoT 终端形成云边端三位一体的端到端产品架构。使用涂鸦边缘云,能极大降低设备响应延时、降低网络带宽压力、提高算力分发能力,并构建以下技术优势&…...
Java常用数据结构
Java常用数据结构 Java中有几种常用的数据结构,主要分为Collection和map两个主要接口(接口只提供方法,并不提供实现),而程序中最终使用的数据结构是继承自这些接口的数据结构类。 一、几个常用类的区别 1.…...
【Java基础 下】 026 -- 集合进阶(不可变集合、Stream流、方法引用)
目录 一、不可变集合 1、创建不可变集合的应用场景 2、创建不可变集合的书写格式 ①、不可变的List集合 ②、不可变的Set集合 ③、不可变的Map集合 3、小结 二、Stream流 1、体验Stream流的作用 2、Stream流的思想 3、Stream流的使用步骤 ①、单列集合获取Stream流 ②、双列集合…...
SAP 跨工厂或特定工厂的物料状态设置
在物料主数据的Basic data 1 View和MRP1 View可分别设置“跨工厂物料状态(X-plant matl status)”和“特定工厂的物料状态(Plant-sp.matl status)”。 通过对物料状态的设置,可实现对物料使用范围的限制。 例:在采购中不可用;在库存管理中不…...
jupyter的安装步骤
1.安装python文件 首先去官网python去下载python的安装包,点击donwload,选择合适的系统。这里我是windown系统,点击进去,如图找到有installer的去下载。不建议下载最新版本的,会有兼容问题。 2.安装python 点击第二个选项是自己配…...
Optional使用详解
Optional使用详解 文章目录Optional使用详解1.构造函数2.Optional.of(T value)作用使用源码(只想知道怎么用的可以略过)Optional.ofNullable(T value)作用使用源码.orElse(T other)作用使用源码.orElseGet(Supplier<? extends T> other)作用使用源…...
如何实现文件高速传输,推荐镭速高速文件传输解决方案
随着互联网的发展,文件传输越来越频繁,如何实现文件高速传输已经越来越成为企业发展过程中需要解决的问题, 在当今的业务中,随着与客户和供应商以及内部系统的所有通信的数据量不断增加,对高速文件传输解决方案的需求…...
SpringBoot整合Mybatis+人大金仓(kingbase8)
陈老老老板🦸👨💻本文专栏:国产数据库-人大金仓(kingbase8)(主要讲一些人大金仓数据库相关的内容)👨💻本文简述:本文讲一下Mybatis框架整合人…...
TPM 2.0实例探索2 —— LUKS磁盘加密(3)
接前文:TPM 2.0实例探索2 —— LUKS磁盘加密(2) 本文大部分内容参考: Code Sample: Protecting secret data and keys using Intel Platform... 二、LUKS磁盘加密实例 3. 将密码存储于TPM的LUKS 由于自动挂载需要在运行时提供一…...
嵌入式Debian主机可接HDMI显示
1、ARM是何物 ARM是一种体系架构。它使用 32 位处理器核心,采用 RISC(Reduced Instruction Set Computer,精简指令集计算机)架构,核心的运算效率高,占用空间小,功耗低,应用于便携式…...
驱动程序开发:基于ICM20608六轴传感器 --- 使用Regmap API 的 SPI 读取数据 之 IIO驱动
目录一、IIO 子系统简介二、IIO子系统使用的一些相关的结构体、函数等1、iio_dev 结构体 ①modes:是选择iio驱动设备支持的工作模式,模式分别有如下: ②dev:其是一个设备结构体。 ②channels:为 IIO 设备通道…...
专利撰写 为什么要申请专利 申请专利对个人有什么利益关系 专利申请实例 如何申请专利 专利申请办理流程
专利撰写 专利是对发明者或创造者所创造的发明或设计提供一定期限的独占权的法律保护。撰写专利需要考虑到多方面的因素,包括发明或设计的技术性、可行性、独创性、保密性等等。以下是一些关于专利撰写的常见问题和注意事项:专利类型:专利包括…...
yolov5/6/7系列模型训练日志结果数据对比分析可视化
早在之前使用yolov3和yolov4这类项目的时候可视化分析大都是自己去做的,到了yolov5的时候,变成了一个工具包了,作者全部集成进去了,这里我们以一个具体的结果为例,如下:整个训练过程产生的指标等数据都会自…...
ppppp2-23
#!/bin/sh USBFILE/etc/ppp/usbdevices LIST/etc/ppp/diallist function ec25_find_ttyname() { DEVNAME$1 FLAG0 USB_FIND_PATH/sys/bus/usb/devices for dir in $(ls $USB_FIND_PATH) do echo $(ls USBFINDPATH/USB_FIND_PATH/USBFINDPATH/dir) | grep ttyUSB > /dev…...
【GeoDjango框架解析——读取矢量数据写入postgis数据库】
系列文章目录 提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加 geodjango框架解析之读取矢量数据shp文件写入postgis数据库 提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录…...
注意啦!如何通过广告吸引客户直接下单?
2023年跨境电商越来越突出,据业内相关人士称,在未来几年与跨境电商相关的政策仍会继续倾斜甚至加大力度,因此各行各业都响应政策,在新政策落实之前致力于平台的转型升级,做新时代创新型的高质量发展,其实细…...
ThinkPHP ^6图片操作进阶
图片裁剪、缩略、水印不再是TP框架系统内置的功能,需要安装。 目录 安装 图片处理 1.创建图片对象 2.获取图片属性 3.裁剪图像 4.生成缩略图 6.保存图像 7.水印 安装 使用composer在项目根目录打开命令行执行: composer require topthink/think…...
深入理解JS作用域链与执行上下文
变量提升: 变量提升( hoisting )。 我可恨的 var 关键字: 你读完下面内容就会明白标题的含义,先来一段超级简单的代码: <script type"text/javascript">var str Hello JavaScript hoi…...
UnityEditor编辑器扩展代码实现Project搜索的实现功能和切换Component等
反射实现切换Gameobjecect-Comp之前介绍过Kinematic Character Controller这个插件这个插件很容易和另外一个插件混淆,两个作者头像比较相像,而且这个插件的作者不太喜欢露脸(他现在做Dot-CharacterControl去了),几乎网…...
变量 varablie 声明- Rust 变量 let mut 声明与 C/C++ 变量声明对比分析
一、变量声明设计:let 与 mut 的哲学解析 Rust 采用 let 声明变量并通过 mut 显式标记可变性,这种设计体现了语言的核心哲学。以下是深度解析: 1.1 设计理念剖析 安全优先原则:默认不可变强制开发者明确声明意图 let x 5; …...
CVPR 2025 MIMO: 支持视觉指代和像素grounding 的医学视觉语言模型
CVPR 2025 | MIMO:支持视觉指代和像素对齐的医学视觉语言模型 论文信息 标题:MIMO: A medical vision language model with visual referring multimodal input and pixel grounding multimodal output作者:Yanyuan Chen, Dexuan Xu, Yu Hu…...
rknn优化教程(二)
文章目录 1. 前述2. 三方库的封装2.1 xrepo中的库2.2 xrepo之外的库2.2.1 opencv2.2.2 rknnrt2.2.3 spdlog 3. rknn_engine库 1. 前述 OK,开始写第二篇的内容了。这篇博客主要能写一下: 如何给一些三方库按照xmake方式进行封装,供调用如何按…...
《Qt C++ 与 OpenCV:解锁视频播放程序设计的奥秘》
引言:探索视频播放程序设计之旅 在当今数字化时代,多媒体应用已渗透到我们生活的方方面面,从日常的视频娱乐到专业的视频监控、视频会议系统,视频播放程序作为多媒体应用的核心组成部分,扮演着至关重要的角色。无论是在个人电脑、移动设备还是智能电视等平台上,用户都期望…...
在HarmonyOS ArkTS ArkUI-X 5.0及以上版本中,手势开发全攻略:
在 HarmonyOS 应用开发中,手势交互是连接用户与设备的核心纽带。ArkTS 框架提供了丰富的手势处理能力,既支持点击、长按、拖拽等基础单一手势的精细控制,也能通过多种绑定策略解决父子组件的手势竞争问题。本文将结合官方开发文档,…...
YSYX学习记录(八)
C语言,练习0: 先创建一个文件夹,我用的是物理机: 安装build-essential 练习1: 我注释掉了 #include <stdio.h> 出现下面错误 在你的文本编辑器中打开ex1文件,随机修改或删除一部分,之后…...
让AI看见世界:MCP协议与服务器的工作原理
让AI看见世界:MCP协议与服务器的工作原理 MCP(Model Context Protocol)是一种创新的通信协议,旨在让大型语言模型能够安全、高效地与外部资源进行交互。在AI技术快速发展的今天,MCP正成为连接AI与现实世界的重要桥梁。…...
Android15默认授权浮窗权限
我们经常有那种需求,客户需要定制的apk集成在ROM中,并且默认授予其【显示在其他应用的上层】权限,也就是我们常说的浮窗权限,那么我们就可以通过以下方法在wms、ams等系统服务的systemReady()方法中调用即可实现预置应用默认授权浮…...
Java线上CPU飙高问题排查全指南
一、引言 在Java应用的线上运行环境中,CPU飙高是一个常见且棘手的性能问题。当系统出现CPU飙高时,通常会导致应用响应缓慢,甚至服务不可用,严重影响用户体验和业务运行。因此,掌握一套科学有效的CPU飙高问题排查方法&…...
Reasoning over Uncertain Text by Generative Large Language Models
https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829 1. 概述 文本中的不确定性在许多语境中传达,从日常对话到特定领域的文档(例如医学文档)(Heritage 2013;Landmark、Gulbrandsen 和 Svenevei…...
