文章目录
- 一、淘汰策略概览
-
- [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)
如果需要毫秒级精度 的过期设置,可使用 pexpire
和 pttl
(查看剩余毫秒数):
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 对每个对象(键的值)用 robj
(redisObject
)封装,结构体表示(源码简化):
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-*
可用)想淘汰【快过期】的键 → 选TTL
(volatile-ttl
)。 - 想"随机淘汰",不纠结访问情况,选
random
(如volatile-random
/allkeys-random
)。
3. 考虑"写操作是否能报错"
- 能接受"内存满时写操作报错",可选
noeviction
(需确保业务能处理写错误)。 - 不能接受"写操作报错",必须选其他淘汰策略,确保内存有空间容纳新数据。
举例:
- 缓存场景 :热点数据需保留,非热点数据可淘汰。适合选
allkeys-lru
或allkeys-lfu
。 - 业务数据场景 :只有"临时数据"可过期,"核心数据"需长期存在。适合给临时数据设过期时间,再选
volatile-lru
或volatile-lfu
。