2. Redis 内存模型:内存结构、内存消耗分析、内存碎片

Redis 作为内存数据库,其内存管理直接决定了服务的稳定性与性能。本章深入剖析 Redis 的内存结构、消耗来源以及碎片成因,为后续的淘汰策略与缓存命中率优化打下坚实基础。

2.1 Redis 内存结构全景

Redis 的内存占用并非仅由键值对数据构成,而是由多个独立区域共同组成。理解这些区域是精准分析内存消耗的前提。

Redis 内存结构全景图 数据区 (Data) 键值对对象 String / List / Set / ZSet / Hash 过期字典 (expires) 主字典 (dict) 元数据区 (Metadata) RedisObject 结构体 SDS 字符串头 编码类型标记 引用计数 缓冲区 (Buffer) 客户端输入/输出缓冲区 AOF 重写缓冲区 复制积压缓冲区 Lua 脚本缓存 持久化与碎片区 RDB 快照内存映射 AOF 重写子进程 COW 页 内存碎片 (jemalloc 分配粒度) Slab 内部碎片 运行时与模块区 主线程事件循环 模块加载的私有数据 Lua 运行时环境 网络连接对象
关键认知:Redis 的内存占用 = 数据区 + 元数据区 + 缓冲区 + 持久化区 + 碎片区。仅关注键值对大小会严重低估实际内存消耗。

2.2 内存消耗深度分析

每个 Redis 键值对在内存中由三部分组成:键的 SDS、值的 RedisObject、值的实际数据。以下以最常用的 String 类型为例进行拆解。

2.2.1 单键值对内存模型

组件 结构 内存开销(64位系统) 说明
键 SDS (Simple Dynamic String) sizeof(sdshdr) + len + 1 ≈ 19 + len sdshdr 头 19 字节,含 len、free、flags
值对象 RedisObject 16 字节 type、encoding、lru、refcount、ptr
值数据 embstr / raw ≤44 字节用 embstr,>44 用 raw embstr 与 RedisObject 连续分配
字典条目 dictEntry 24 字节 key、value、next 指针
合计 — ≈ 59 + len(键) + len(值) 不含内存碎片
实战案例:存储一个键 "user:1001"(10 字节),值 "active"(6 字节),理论内存 ≈ 59 + 10 + 6 = 75 字节。但实际占用可能达到 96~128 字节,多出的部分就是内存碎片。

2.2.2 不同数据结构的额外开销

数据类型 内部编码 额外元数据开销 适用场景
List quicklist 每个 ziplist 节点 6~12 字节 消息队列、时间线
Hash ziplist / hashtable ziplist 头 6 字节;hashtable 每个 entry 24 字节 对象属性存储
Set intset / hashtable intset 头 8 字节;hashtable 同 Hash 标签、去重
ZSet ziplist / skiplist skiplist 节点 24~32 字节 + 字典 24 字节 排行榜、延时队列

2.3 内存碎片:成因与度量

内存碎片是 Redis 内存消耗中不可忽视的部分。Redis 使用 jemalloc 作为默认内存分配器,其分配粒度导致内部碎片;频繁的键值对增删则产生外部碎片。

2.3.1 碎片产生机制

内存碎片产生过程 初始分配:连续内存块 (A:32B | B:64B | C:48B | D:128B | E:32B) 释放 B(64B) 和 D(128B):产生两个空闲空洞 碎片状态:A:32B | 空洞:64B | C:48B | 空洞:128B | E:32B 新请求 100B:两个空洞均不满足,触发 jemalloc 重新分配或扩展

2.3.2 碎片率指标

Redis 通过 INFO memory 命令暴露关键碎片指标:

# 查看内存碎片率
127.0.0.1:6379> INFO memory
# Memory
used_memory: 1048576000        # 实际分配的内存(含碎片)
used_memory_rss: 1258291200    # 操作系统看到的内存(RSS)
mem_fragmentation_ratio: 1.20  # 碎片率 = RSS / used_memory
mem_allocator: jemalloc-5.3.0
碎片率范围 状态 建议操作
1.0 ~ 1.1 ✅ 健康 无需处理
1.1 ~ 1.5 ⚠️ 轻度碎片 监控趋势,考虑定期重启
1.5 ~ 2.0 🔴 中度碎片 计划维护窗口,执行 MEMORY PURGE
> 2.0 🚨 严重碎片 立即重启或迁移数据
< 1.0 ❌ 异常(swap 使用中) 检查系统内存压力,增加物理内存
注意:碎片率 < 1.0 通常意味着操作系统开始使用 swap 交换分区,此时 Redis 性能会急剧下降。这是比高碎片率更危险的状态。

2.4 内存消耗分析实战方法

在生产环境中,推荐使用以下工具链进行内存分析:

  1. INFO memory — 快速查看整体内存状态和碎片率。
  2. MEMORY STATS — 获取更详细的内存分类统计。
  3. MEMORY USAGE <key> — 精确计算单个键的内存占用。
  4. redis-cli --bigkeys — 扫描大键,定位内存热点。
  5. RDB 分析工具 (rdb-tools) — 离线分析 RDB 文件,生成内存报告。
# 使用 MEMORY USAGE 分析单个键
127.0.0.1:6379> MEMORY USAGE user:1001
(integer) 96

# 使用 --bigkeys 扫描大键
$ redis-cli --bigkeys
# Scanning the entire keyspace to find biggest keys as well as
# average sizes per key type.  You can use -i 0.1 to sleep 0.1 sec
# per 100 SCAN commands (not usually needed).

[00.00%] Biggest string found so far '"user:1001"' with 96 bytes
[00.00%] Biggest list   found so far '"queue:orders"' with 15234 items
最佳实践:在微服务架构中,建议为每个 Redis 实例配置 maxmemory 限制,并设置 maxmemory-policy 淘汰策略。同时,通过 activedefrag yes 开启自动碎片整理,减少手动维护成本。

2.5 本章小结

  • Redis 内存由 数据区、元数据区、缓冲区、持久化区、碎片区 五部分组成。
  • 单个键值对的实际内存开销远大于数据本身,元数据和字典条目占大头。
  • 内存碎片率 mem_fragmentation_ratio 是衡量内存健康度的核心指标。
  • 使用 MEMORY USAGE、--bigkeys、rdb-tools 等工具进行精准分析。
  • 碎片率 > 1.5 时应主动干预,< 1.0 时需警惕 swap 风险。