Redis内存淘汰策略

Redis内存淘汰策略

Redis 的五种数据结构各有特色,用对了才能发挥它的优势。很多人只用到了 String 和 Hash,却不知道 List、Set、ZSet 在特定场景下更合适。本文从应用场景出发,讲什么时候用什么类型。

一、内存管理基础

#

1.1 查看内存使用

# 查看内存信息
redis-cli INFO memory

# 关键指标:
# used_memory: 总内存使用(字节)
# used_memory_rss: 操作系统视角的内存使用
# used_memory_peak: 内存使用峰值
# maxmemory: 最大内存限制
# maxmemory_policy: 淘汰策略

#

1.2 配置最大内存

# redis.conf
maxmemory 4gb # 最大内存限制
maxmemory-policy allkeys-lru # 淘汰策略

#

1.3 内存达到上限的行为

当 used_memory > maxmemory 时:

1. 如果配置了淘汰策略:按策略淘汰key
2. 如果没有配置淘汰策略(noeviction):
- 读操作正常执行
- 写操作返回错误:OOM command not allowed

二、淘汰策略详解

#

2.1 策略分类

策略 说明 范围
noeviction 不淘汰,返回错误 所有
allkeys-lru 淘汰最近最少使用的key 所有key
allkeys-lfu 淘汰使用频率最低的key 所有key
allkeys-random 随机淘汰key 所有key
volatile-lru 淘汰设置了过期时间的key中最近最少使用的 有过期时间的
volatile-lfu 淘汰设置了过期时间的key中使用频率最低的 有过期时间的
volatile-random 随机淘汰设置了过期时间的key 有过期时间的
volatile-ttl 淘汰快要过期的key 有过期时间的

#

2.2 LRU策略

LRU(Least Recently Used):最近最少使用

Redis的近似LRU实现:

传统LRU:需要维护一个链表,每次访问移动节点,O(1)
Redis近似LRU:
- 每个对象记录最后访问时间(24bit,精度到分钟级别)
- 随机采样N个key,淘汰其中最久未访问的
- 采样数量由 maxmemory-samples 控制(默认5)

近似LRU vs 精确LRU:
- 内存占用更少(不需要维护链表)
- 性能更好(不需要移动节点)
- 精度接近(采样数越大越精确)

配置

maxmemory-policy allkeys-lru
maxmemory-samples 5 # 采样数量(1-10,越大越精确但越慢)

#

2.3 LFU策略

LFU(Least Frequently Used):最少使用频率

Redis 4.0+ 引入的LFU实现:

每个对象的LFU计数:
┌──────────────────┬──────────────────┐
│ 16位计数器 │ 8位衰减时间 │
│ (访问频率) │ (分钟精度) │
└──────────────────┴──────────────────┘

计数器增长逻辑:
- 每次访问时,计数器增加
- 增长概率随当前计数器值递减(值越大越难增长)
- 最大值为255

衰减逻辑:
- 每隔一定时间(衰减周期),计数器衰减
- 衰减周期 = lfu-decay-time(默认1分钟)
- 衰减量 = 当前计数器值 / 2

配置

maxmemory-policy allkeys-lfu
lfu-log-factor 10 # 计数器增长因子(越大计数器增长越慢)
lfu-decay-time 1 # 衰减周期(分钟)

LFU vs LRU

特性 LRU LFU
关注点 最近访问时间 访问频率
适合场景 热点数据变化快 有明确的热度差异
新数据 容易淘汰 需要时间积累计数
突发流量 可能淘汰热点 更稳定

#

2.4 TTL策略

volatile-ttl:优先淘汰即将过期的key

适用场景:
- 缓存数据都有过期时间
- 希望优先保留长久有效的数据
- 让快要过期的数据自然淘汰

注意:
- 只淘汰有过期时间的key
- 如果所有key都没有过期时间, behaves like noeviction

#

2.5 Random策略

allkeys-random / volatile-random:随机淘汰

适用场景:
- 所有key访问概率相同
- 对淘汰精度要求不高
- 追求极致性能(不需要计算LRU/LFU)

特点:
- 性能最好(不需要比较)
- 但可能淘汰热点数据

三、策略选择指南

#

3.1 选择决策树

是否需要淘汰数据?

├── 否 → noeviction

└── 是

├── 所有key都需要淘汰?
│ │
│ ├── 是 → allkeys-xxx
│ └── 否 → volatile-xxx

├── 数据有明显冷热差异?
│ │
│ ├── 是 → lru/lfu
│ └── 否 → random/ttl

└── 热点变化快?

├── 是 → lru
└── 否 → lfu

#

3.2 场景推荐

场景 推荐策略 原因
纯缓存 allkeys-lru 缓存所有数据,LRU最自然
持久化+缓存混合 volatile-lru 保护未设置过期的数据
有明显热点 allkeys-lfu 保留高频访问数据
数据均匀访问 allkeys-random 简单高效
缓存都有TTL volatile-ttl 让快过期的先淘汰
不允许丢数据 noeviction 内存满时返回错误

#

3.3 生产配置示例

# 场景1:纯缓存(电商商品)
maxmemory 16gb
maxmemory-policy allkeys-lru
maxmemory-samples 5

# 场景2:会话缓存(有明确过期时间)
maxmemory 4gb
maxmemory-policy volatile-lru

# 场景3:热点数据(排行榜、计数器)
maxmemory 8gb
maxmemory-policy allkeys-lfu
lfu-log-factor 10
lfu-decay-time 1

# 场景4:不允许淘汰(关键配置)
maxmemory 2gb
maxmemory-policy noeviction

四、内存优化

#

4.1 数据结构优化

# 1. 使用Hash存储对象(比String序列化省空间)
# String: {"name":"Alice","age":25}
# Hash: HSET user:1001 name Alice age 25

# 2. 控制Hash使用ziplist编码
hash-max-ziplist-entries 512
hash-max-ziplist-value 64

# 3. 控制Set使用intset编码
set-max-intset-entries 512

# 4. 控制ZSet使用ziplist编码
zset-max-ziplist-entries 128
zset-max-ziplist-value 64

#

4.2 数据压缩

# 开启RDB压缩
rdbcompression yes

# 开启AOF的RDB前缀(Redis 4.0+)
aof-use-rdb-preamble yes

#

4.3 监控内存使用

# 查看内存使用详情
redis-cli --bigkeys

# 查看内存碎片
redis-cli INFO memory | grep mem_fragmentation_ratio

# 内存碎片率 > 1.5 说明碎片严重,可以考虑重启

#

4.4 内存分析工具

# redis-rdb-tools: 分析RDB文件内存使用
rdb -c memory dump.rdb > memory.csv

# 查看哪些key占用最多内存
sort -t, -k4 -nr memory.csv | head -20

五、常见问题

#

5.1 内存突增导致OOM

# 现象:used_memory突然达到maxmemory,写操作失败

# 排查:
redis-cli INFO memory
# 查看 used_memory 和 maxmemory

# 解决:
# 1. 临时增加maxmemory
redis-cli CONFIG SET maxmemory 8gb

# 2. 检查是否有大key
redis-cli --bigkeys

# 3. 检查是否有内存泄漏
redis-cli INFO stats | grep evicted_keys

#

5.2 淘汰策略不生效

# 检查配置
redis-cli CONFIG GET maxmemory*

# 常见原因:
# 1. 没有设置maxmemory
# 2. 设置了noeviction策略
# 3. volatile策略但key没有过期时间

# 查看淘汰统计
redis-cli INFO stats | grep evicted

#

5.3 内存碎片率高

# 查看碎片率
redis-cli INFO memory | grep mem_fragmentation_ratio

# 如果 > 1.5,说明碎片严重
# 解决:
# 1. 重启Redis(最彻底)
# 2. 使用jemalloc(默认)比glibc malloc碎片少
# 3. Redis 4.0+ 可以使用内存整理
redis-cli MEMORY PURGE

六、总结

策略 淘汰范围 算法复杂度 适用场景
allkeys-lru 所有key O(N)采样 通用缓存
allkeys-lfu 所有key O(N)采样 热点数据
allkeys-random 所有key O(1) 均匀访问
volatile-lru 有过期时间的 O(N)采样 混合存储
volatile-lfu 有过期时间的 O(N)采样 热点+过期
volatile-ttl 有过期时间的 O(N)排序 TTL优化
volatile-random 有过期时间的 O(1) 简单场景
noeviction 不淘汰 - 不允许丢数据

内存管理的核心原则:

  1. 设置合理的maxmemory:不超过物理内存的75%
  2. 选择合适的淘汰策略:根据业务特点选择LRU/LFU/Random
  3. 监控内存使用:及时发现内存异常
  4. 优化数据结构:使用ziplist等紧凑编码
  5. 定期分析大key:避免单个key占用过多内存

核心要点

  1. String:简单的键值对,适合缓存、计数器

  2. Hash:存储对象属性,适合用户信息、配置

  3. List:有序列表,适合消息队列、最新列表

  4. Set:无序去重,适合共同好友、抽奖

  5. ZSet:有序集合,适合排行榜、积分系统

总结

选择合适的数据结构是使用 Redis 的关键。在实际项目中,根据业务需求选择合适的类型,可以提升性能和开发效率。


   转载规则


《Redis内存淘汰策略》 小乐 采用 知识共享署名 4.0 国际许可协议 进行许可。
  目录