返回首页

Redis 内存优化完全指南 — 数据结构/TTL/淘汰策略/Bigkey/碎片治理

📅 创建于 2026-05-12 🔄 更新于 2026-05-12 📝 1002 字

Redis 内存优化完全指南

Redis 的内存不只是 value 本身,还包括 key、RedisObject、字典结构、过期字典、客户端缓冲区、复制缓冲区和内存碎片。 优化核心:用合适的数据结构表达数据,用合理的 TTL 管理生命周期,用正确的淘汰策略保护热点数据,再通过 bigkey、碎片率和缓冲区监控持续治理。

一、Redis 内存占用分析

Redis 的内存由以下几部分组成:

类别 说明 举例
key 本身 每个 key 的字符串 user:profile:10001
value 本身 存储的数据 JSON 字符串、Hash field、List element
元数据开销 RedisObject、字典节点、哈希表 每个 key-value 约 ~64 字节额外开销
过期字典 TTL 维护 大量过期 key 增加内存
客户端缓冲区 每个客户端连接的输入/输出缓冲 慢客户端/大批量输出时膨胀
复制积压缓冲区 主从复制 repl-backlog-size 决定
内存分配器碎片 申请/释放不匹配导致 碎片率 > 1.5 需要关注
COW 额外内存 fork 子进程时写时复制 RDB/AOF rewrite 期间临时升高

所以不要简单理解成:Redis 里存了 1GB 的业务数据,就只占 1GB 内存。


二、核心优化思路

少存:不该放 Redis 的数据不要放
短存:能设置过期时间的 key 尽量设置 TTL
精存:key、value、序列化格式尽量减少冗余
合理存:根据场景选择合适的数据结构
可淘汰:配置合理的 maxmemory 和淘汰策略
可排查:通过监控和命令定位 bigkey、碎片和异常缓冲区

三、Key 设计优化

3.1 控制 Key 长度

不推荐:

user:profile:detail:2026:mobile:login:region:china:id:10001

更合理:

u:p:10001          # 但不要失去可读性
user:profile:10001 # 保留业务含义,去掉重复废话

3.2 Key 命名规范

格式:业务:对象:ID

user:profile:10001
product:detail:20001
order:status:30001

好处:可读性好、方便按前缀扫描和统计。

3.3 大量无 TTL 的缓存 Key

缓存类 key 必须设置过期时间,否则 Redis 只增不减:

  • 防止历史数据长期占用内存
  • 降低人工清理成本
  • 给淘汰策略留出空间

注意: TTL 不要全部设成同一时间,加随机偏移防雪崩。

基础 TTL + 随机偏移,如 30min + 0~5min 随机

四、Value 与序列化优化

4.1 避免大对象无脑 JSON 化

JSON 存在的问题:

  • 字段名重复占空间
  • 字符串格式比二进制大
  • 类名/类型信息额外冗余
  • 只更新一个字段时整体读写

例如 {"id":10001,"name":"Tom","age":18,"city":"Shanghai"} — 字段名 id/name/age/city 都是重复开销。

4.2 Spring RedisTemplate 注意点

方式 优点 缺点
RedisTemplate 直接存对象,方便 可能写入额外类型信息(@class),占用更多内存
StringRedisTemplate 数据更干净 序列化/反序列化需自己控制

建议: 内存敏感场景下,用 MEMORY USAGE key 实测真实占用,对比不同序列化方式。

4.3 小对象可以考虑 Hash

# String 方式
SET user:10001 '{"name":"Tom","age":18}'

# Hash 方式
HSET user:10001 name Tom age 18 city Shanghai

Hash 优势:可单独修改字段;小 Hash 底层使用紧凑编码(ziplist)。

但 Hash 不是永远更省 — field 名太长、field 太多、value 特别大时也可能膨胀。结合 MEMORY USAGE 实测。

4.4 能用整数不要存字符串

SET user:age:10001 18            # 整数,紧凑编码
# 优于:
SET user:age:10001 "eighteen-years-old"  # 长字符串

计数器、库存、点赞数、访问量优先用数字类型。


五、数据结构选型

数据类型 适合场景 内存优化要点
String 简单 key-value、计数器、分布式锁、小 JSON 控制 value 大小,避免大 JSON 形成 bigkey
Hash 用户信息、商品信息、配置对象(字段少) 小 Hash 紧凑编码,field 名称别太长
List 队列、消息列表、最新记录 控制长度 + 定期 LTRIM
Set 去重集合、标签集合 判断存在可考虑布隆过滤器
Sorted Set 排行榜、延迟队列 只保留 TopN,定期清理低分数据
Bitmap 签到、在线状态、布尔状态(ID 连续) 比 Set 省大量空间,但偏移量不能离散
HyperLogLog UV 统计 固定小空间,但有误差,不能记录具体谁

5.1 Bitmap 案例:用户签到

# Set 方式(存 100 万用户签到需存 100 万个 ID)
SADD sign:20260424 10001

# Bitmap 方式(按 bit 位记录)
SETBIT sign:20260424 10001 1

Bitmap 代价:偏移量不能过于离散;适合 ID 比较连续的场景。

5.2 HyperLogLog 案例:UV 统计

PFADD page:uv:20260424 user:10001 user:10002
PFCOUNT page:uv:20260424

固定小空间,不精确(有误差),不能记录具体访客身份。


六、过期与淘汰策略

6.1 过期策略

Redis 删除过期 key 依赖两类机制:

机制 说明
惰性删除 访问 key 时发现过期→删除
定期删除 Redis 周期性抽样检查并删除

key 到期 ≠ 立刻从内存消失。大量过期 key 未及时被访问,可能短时间继续占用内存。

6.2 淘汰策略(maxmemory-policy

maxmemory 达到上限时:

策略 含义 适用场景
noeviction 不淘汰,写入时报错 Redis 当数据库或强依赖存储
allkeys-lru 淘汰最近最少使用的 key 通用缓存场景
volatile-lru 淘汰有 TTL 的 key 中最近最少使用 只淘汰缓存 key
allkeys-lfu 淘汰低频访问 key 热点明显且访问频率稳定
volatile-lfu 淘汰有 TTL 的 key 中低频访问 缓存 key 有 TTL 且保留高频
volatile-ttl 优先淘汰更快过期的 key TTL 敏感场景
allkeys-random 随机淘汰 要求低的简单缓存
volatile-random 随机淘汰有 TTL 的 key 只淘汰临时缓存

缓存型 Redis 推荐: allkeys-lruallkeys-lfu

存储型 Redis: noeviction + 扩容/拆分/持久化保障


七、Bigkey 优化

7.1 什么是 Bigkey

类型 标准
String value > 10 KB
List > 10000 个元素
Hash > 10000 个 field
Set / ZSet > 10000 个 member

7.2 Bigkey 的危害

不只是占内存,还会导致:

  • 网络传输慢
  • 序列化/反序列化慢
  • 删除时可能阻塞 Redis(单线程)
  • 迁移/备份/主从同步成本高
  • 集群模式数据倾斜
  • 单个命令处理时间变长,影响其他请求

7.3 优化方法

  • 拆分 key(按业务维度或用户 ID 拆分)
  • 控制集合长度(LTRIMZREMRANGEBYRANK
  • 分页读取,不使用一次性全量命令
  • 删除大 key 用 UNLINK(异步释放)
  • 大 Hash 拆成多个小 Hash
# 拆分示例:一个大 Hash user:all
# 按分片拆分:
user:profile:0   → 前 1000 个用户
user:profile:1   → 接下来 1000 个
# 或按用户 ID 拆分:
user:profile:10001
user:profile:10002

八、内存碎片优化

8.1 碎片指标

redis-cli INFO memory
# 重点关注:
# used_memory          — Redis 认为自己实际使用的内存
# used_memory_rss      — 操作系统分配给 Redis 进程的物理内存
# mem_fragmentation_ratio — 碎片率 = used_memory_rss / used_memory

mem_fragmentation_ratio > 1.5 表示可能存在明显碎片。

8.2 碎片产生原因

  • key 频繁创建和删除
  • value 大小变化很频繁
  • 大量过期删除集中发生
  • bigkey 删除或更新

8.3 碎片处理方案

# 开启主动碎片整理
redis-cli CONFIG SET activedefrag yes

其他措施:

  • 避免 value 大小频繁剧烈变化
  • 大 key 用 UNLINK 异步释放
  • 低峰期执行内存整理或重启切换
  • 扩容/分片减少单实例压力

注意: 碎片整理消耗 CPU,需同时观察延迟和 CPU 使用率。


九、持久化与复制带来的内存问题

9.1 fork 与 Copy On Write

Redis 做 RDB/AOF rewrite 时 fork 子进程,父进程继续处理写请求。被修改的内存页会复制一份 → 短期内内存升高。

生产建议: Redis 内存不能长期打满,预留余量给持久化、复制和碎片。

9.2 AOF 重写压力

AOF rewrite 期间写入流量高 → 额外缓冲 → 内存/磁盘 IO 升高 → rewrite 时间变长 → 主从延迟变大。

9.3 客户端输出缓冲区

如果客户端消费太慢,Redis 输出缓冲区膨胀:

redis-cli CLIENT LIST
# 关注:qbuf、qbuf-free、obl、oll、omem 等字段

排查:客户端是否消费慢、是否一次性读取 bigkey、网络是否异常。

客户端缓冲区详细治理参见 redis-connection-management

持久化机制原理参见 redis-persistence-strategy(RDB/AOF/混合持久化)。


十、常用排查命令

# 查看整体内存
redis-cli INFO memory

# 查看单个 key 内存(带采样)
redis-cli MEMORY USAGE key SAMPLES 5

# 查看对象底层编码
redis-cli OBJECT ENCODING key

# 内存诊断
redis-cli MEMORY STATS
redis-cli MEMORY DOCTOR

# 扫描 bigkey(低峰期执行)
redis-cli --bigkeys
redis-cli --memkeys

十一、场景案例

电商商品缓存

  • 商品详情按商品 ID 拆 key
  • 热点商品设较长 TTL 或逻辑过期
  • 普通商品 TTL 加随机值
  • 大字段(图文详情)不全部放 Redis
  • 排行榜 ZSet 只保留 TopN

用户签到

  • ID 连续 → Bitmap
  • ID 离散大 → 考虑 Bloom Filter 或 Set

热门文章阅读量

INCR article:read:10001
# 计数类用数字 String,不要把整个文章对象反复读写

十二、常见问题排查思路

问题 优先排查
内存持续上涨 无 TTL key 过多?写入 > 过期/淘汰速度?bigkey?慢客户端缓冲区?持久化额外内存?
突然 OOM maxmemory 过高未留余量?淘汰策略 noeviction?短时大量大 value?AOF rewrite 高峰?
命中率下降但内存高 低价值 key 占位?淘汰策略不合理?TTL 设置不合理?bigkey 挤压热点?
删除 key 后内存未降 key 过小占比不明显?RSS 未回收?碎片高?缓冲区占用?业务仍在写入?

关联页面

页面 关联点
redis-persistence-strategy RDB/AOF/混合持久化(fork/COW 内存影响)
redis-connection-management 客户端输出缓冲区治理、连接打满
redis-ha-replication-sentinel 高可用架构(淘汰策略场景参考)
redis-backup-recovery 备份恢复(内存与持久化配合)
server-performance-four-dimensions 系统级内存监控
linux-memory-management-deep-dive Linux 内存管理深潜(cgroup v2/回收/OOM)