Redis 内存淘汰策略

文章目录

  • 一、淘汰策略概览
    • [1.1 Redis 键的过期设置](#1.1 Redis 键的过期设置)
    • [1.2 redisObject 与 LRU/LFU 基础](#1.2 redisObject 与 LRU/LFU 基础)
    • [1.3 淘汰策略的配置项](#1.3 淘汰策略的配置项)
      • [1.3.1 maxmemory:设置内存上限](#1.3.1 maxmemory:设置内存上限)
      • [1.3.2 maxmemory-policy:选择淘汰策略](#1.3.2 maxmemory-policy:选择淘汰策略)
  • 二、具体的淘汰策略类型
    • [2.1 仅淘汰"设置了过期时间"的键(volatile)](#2.1 仅淘汰“设置了过期时间”的键(volatile))
    • [2.2 淘汰"所有键"(allkeys)](#2.2 淘汰“所有键”(allkeys))
    • [2.3 禁止淘汰(noeviction)](#2.3 禁止淘汰(noeviction))
  • 三、淘汰策略的选择逻辑

一、淘汰策略概览

Redis 作为内存数据库 ,所有数据默认存储在内存中,因此内存空间的管理至关重要。当内存不足时,需要通过"淘汰策略"删除部分键值对,为新数据腾出空间;同时,Redis 也支持为键设置过期时间,让键自动删除。

1.1 Redis 键的过期设置

我们可以为 Redis 的最外层键设置"过期时间",让键值对在指定时间后自动删除。核心命令有两个:

  • expire key seconds:设置键在 seconds(秒)后过期。
  • pexpire key milliseconds:设置键在 milliseconds(毫秒)后过期。

注意 :过期是作用于最外层的 key(例如整个 List、Hash、Set、String),不能对 List 的单个元素或 Hash 的单个 field 做淘汰或单独过期。

示例(以 student 键为例):

bash 复制代码
# 1. 设置 student 20 秒后过期
127.0.0.1:6379> expire student 20
(integer) 1  # 返回 1 表示设置成功

# 2. 查看剩余过期时间(秒),值会逐渐减少
127.0.0.1:6379> ttl student
(integer) 14
...
127.0.0.1:6379> ttl student
(integer) -2  # 返回 -2 表示键已过期并被删除

# 3. 此时获取 student,返回 nil(键已不存在)
127.0.0.1:6379> get student
(nil)

如果需要毫秒级精度 的过期设置,可使用 pexpirepttl(查看剩余毫秒数):

bash 复制代码
127.0.0.1:6379> pexpire student 20000  # 设置 20000 毫秒(20 秒)后过期
(integer) 1
127.0.0.1:6379> pttl student           # 查看剩余过期时间(毫秒)
(integer) 10600
...
127.0.0.1:6379> pttl student
(integer) -2  # 键已过期删除
127.0.0.1:6379> get student
(nil)

关于 TTL/PTTL 的返回值:

  • 若 key 不存在通常返回 -2
  • 若 key 存在但没有设置过期则返回 -1

1.2 redisObject 与 LRU/LFU 基础

Redis 对每个对象(键的值)用 robjredisObject)封装,结构体表示(源码简化):

c 复制代码
typedef struct redisObject {
    unsigned type:4;      // 数据类型(如 string、list 等)
    unsigned encoding:4;  // 底层存储的编码方式
    unsigned lru:24;      // 记录"最近访问"相关信息,为 LRU/LFU 策略提供依据
    int refcount;         // 引用计数(用于内存回收)
    void *ptr;            // 指向实际数据结构的指针(如 List、Hash 的底层实现)
} robj;

其中,lru 字段是 LRU/LFU 策略的核心:它会存储键的"最近使用时间"或"使用频次",Redis 正是通过这个字段判断"哪些键更应该被淘汰"。

lru 字段并不是精确的时间戳,而是用于近似 实现 LRU 的 24 位计时/访问信息(LRU_BITS)。Redis 以紧凑方式保存访问时间,节省内存,并用外部时钟/逻辑时间来计算"空闲时间(idletime)"或最近访问程度。

1.3 淘汰策略的配置项

当 Redis 内存不足时,需通过以下配置启用/调整淘汰策略:

1.3.1 maxmemory:设置内存上限

格式:maxmemory <bytes>

作用:配置 Redis 可使用的最大内存空间

在配置文件或运行时设置:maxmemory <bytes>。告诉 Redis 在使用的内存超过该值时开始触发淘汰策略或拒绝写入(取决于 maxmemory-policy)。

建议配置逻辑 :一般设为物理内存的 50% 左右,原因:

  • Redis 内部的散列表(如 Hash 底层)扩容时,会临时需要额外内存(扩容是"渐进式迁移",但仍需预留空间)。
  • 执行 RDB 持久化时,会 fork 子进程生成内存快照,子进程也需要占用内存(采用"写时复制"机制,但仍需考虑内存余量)。

maxmemory-samples(采样数):

当 Redis 需要选择要淘汰的 key 时,不会扫描全库,而是随机采样若干候选 key,然后在这些样本中按策略选出一个目标。maxmemory-samples 控制每次采样的数量(默认一般是 5)。

更大的采样数能更好地近似全局最优淘汰(例如更可能找到真正最久未使用的 key),但也稍微增加决策开销。工程上根据延迟/命中率权衡参数。

1.3.2 maxmemory-policy:选择淘汰策略

格式:maxmemory-policy <policy>

作用:配置"内存达到 maxmemory 时,Redis 如何淘汰键"。

默认策略是 noeviction,不淘汰任何键,当达到 maxmemory 后新的写入操作会失败并返回错误。

在理解具体策略前,先明确两个范围前缀淘汰算法

  • 范围前缀
    • volatile-*:仅在【设置了过期时间的键】中选淘汰对象。
    • allkeys-*:在【所有键(无论是否设过期时间)】中选淘汰对象。
  • 淘汰算法
    • LRU(Least Recently Used):最近最少使用,淘汰"最长时间没被访问"的键。
    • LFU(Least Frequently Used):最近最少使用频次,淘汰"一段时间内使用次数最少"的键(通过随机采样比较)。
    • TTL(Time To Live):剩余生存时间,淘汰"剩余过期时间最短"的键(仅对 volatile-* 有效)。
    • random:随机,随机选一个键淘汰。

二、具体的淘汰策略类型

Redis 提供了 8 种淘汰策略,可分为三大类

  • volatile-*:只在设置了过期时间的 key 中淘汰)、
  • allkeys-*:在所有 key 中淘汰
  • noeviction:禁止淘汰,写操作报错

2.1 仅淘汰"设置了过期时间"的键(volatile)

这类策略只对"有过期时间的键"进行淘汰,适合"部分数据可过期,部分数据需长期保留"的场景。

策略名称 说明
volatile-lru 在设置了过期时间的键中,淘汰最长时间没有被使用的键。
volatile-lfu 在设置了过期时间的键中,淘汰使用次数最少的键(通过随机采样比较)。
volatile-ttl 在设置了过期时间的键中,淘汰剩余过期时间最短(最近要过期)的键。
volatile-random 在设置了过期时间的键中,随机淘汰一个键。

2.2 淘汰"所有键"(allkeys)

这类策略会在"所有键"中选择淘汰对象,适合"数据整体可根据访问情况淘汰"的场景(如缓存场景:热点数据保留,非热点数据淘汰)。

策略名称 说明
allkeys-lru 在所有键中,淘汰最长时间没有被使用的键。
allkeys-lfu 在所有键中,淘汰使用次数最少的键。
allkeys-random 在所有键中,随机淘汰一个键。

2.3 禁止淘汰(noeviction)

策略名称 说明
noeviction 不淘汰任何键 。当内存不足时,写操作(如 set、hset 等)会直接报错,但读操作仍可正常执行。
这种策略适合"数据必须全部保留,绝不允许丢失"的场景,但要承担"写操作失败"的风险。

三、淘汰策略的选择逻辑

淘汰流程

当Redis内存使用超过 maxmemory 且淘汰策略允许淘汰时,会先依据 maxmemory-policy 确定候选 key 的范围:可能是所有 key,也可能仅限带过期时间的 key。

接着随机抽取maxmemory-samples个候选key,再按照策略对样本进行比较:

  • LRU比较空闲时间,淘汰空闲时间最长的 key;
  • LFU比较使用频率,淘汰访问频率最低的 key;
  • TTL比较剩余过期时间,优先淘汰剩余生存时间最短的 key;
  • Random直接随机选,随机删除样本中的某个 key。
    删除选中的key后检测内存是否低于阈值,如果仍超限则重复上述过程。

这种"随机采样+基于样本近似决策"的方式,是Redis在内存开销与淘汰效果之间做的工程折中------既避免了维护精确全局结构的高成本,又能在多数场景下保证较好的淘汰效果。

选择淘汰策略时,可按以下思路决策:

1. 确定"淘汰范围"

  • 场景 1 :只有"部分键可过期","核心键需长期保留",选 volatile-* 系列策略。
  • 场景 2 :所有键都可根据"访问情况/随机性"淘汰,选 allkeys-* 系列策略。

2. 确定"淘汰算法"

  • 想淘汰【最久没被访问】的键,选 LRU(如 volatile-lru / allkeys-lru)。
  • 想淘汰【使用次数最少】的键,选 LFU(如 volatile-lfu / allkeys-lfu)。
  • (仅 volatile-* 可用)想淘汰【快过期】的键 → 选 TTLvolatile-ttl)。
  • 想"随机淘汰",不纠结访问情况,选 random(如 volatile-random / allkeys-random)。

3. 考虑"写操作是否能报错"

  • 能接受"内存满时写操作报错",可选 noeviction(需确保业务能处理写错误)。
  • 不能接受"写操作报错",必须选其他淘汰策略,确保内存有空间容纳新数据。

举例

  • 缓存场景 :热点数据需保留,非热点数据可淘汰。适合选 allkeys-lruallkeys-lfu
  • 业务数据场景 :只有"临时数据"可过期,"核心数据"需长期存在。适合给临时数据设过期时间,再选 volatile-lruvolatile-lfu
相关推荐
偶尔贪玩的骑士3 小时前
Kioptrix Level 1渗透测试
linux·开发语言·网络安全·php
胖头鱼的鱼缸(尹海文)4 小时前
数据库管理-第376期 Oracle AI DB 23.26新特性一览(20251016)
数据库·人工智能·oracle
それども4 小时前
忽略Lombok构建警告
java·开发语言·jvm
麦聪聊数据4 小时前
浅谈SQL审核(一):SQL审核实现方式与常见工具的选择
数据库·sql
ajassi20004 小时前
开源 Linux 服务器与中间件(七)数据库--MySQL
linux·服务器·数据库·ubuntu·开源
qiuiuiu4134 小时前
正点原子RK3568学习日志12-注册字符设备
linux·开发语言·单片机·学习·ubuntu
韩立学长4 小时前
【开题答辩实录分享】以《自然灾害隐患点管理信息系统》为例进行答辩实录分享
数据库·spring boot
liu****4 小时前
20.哈希
开发语言·数据结构·c++·算法·哈希算法
MetaverseMan4 小时前
Java Spring 框架的`@Autowired` 注解 以及依赖注入分析
java·开发语言·spring