Redis 内部结构
Redis 内部使用一个 redisObject 对象来表示所有的 key 和 value。
- type :代表一个 value 对象具体是何种数据类型。
- encoding :是不同数据类型在 redis 内部的存储方式,比如:type=string 代表 value 存储的是一个普通字符串,那么对应的 encoding 可以是 raw 或者是 int,如果是 int 则代表实际 redis 内部是按数值型类存储和表示这个字符串的,当然前提是这个字符串本身可以用数值表示,比如:”123” “456”这样的字符串。
- vm 字段:只有打开了 Redis 的虚拟内存功能,此字段才会真正的分配内存,该功能默认是关闭状态的。 Redis 使用 redisObject 来表示所有的 key/value 数据是比较浪费内存的,当然这些内存管理成本的付出主要也是为了给 Redis 不同数据类型提供一个统一的管理接口,实际作者也提供了多种方法帮助我们尽量节省内存使用。
redisObject 的 type/encoding 二层结构是理解 Redis 内存行为的钥匙,但原文停留在字段介绍。本文补全三块:type→encoding 的完整映射与转换条件、全局哈希表与渐进式 rehash、以及内存优化的实际手段(object encoding 命令验证)。
type 与 encoding 的完整映射
| type | encoding | 转换条件 |
|---|---|---|
| string | int | 值是能 long 表示的整数 |
| string | embstr | ≤44 字节短串(对象头+数据一次分配,只读) |
| string | raw | 长串或修改后的 embstr |
| list | listpack(ziplist) | 元素少且小(list-max-listpack-size) |
| list | quicklist | 超出阈值(listpack 组成的双向链表) |
| hash | listpack | 字段数<128 且值<64B(hash-max-listpack-entries/value) |
| hash | hashtable | 超出阈值 |
| set | intset | 全整数且量小(set-max-intset-entries) |
| set | listpack | 小集合非全整数(新版本) |
| set | hashtable | 超出阈值 |
| zset | listpack | 成员少且小 |
| zset | skiplist+hashtable | 超出阈值 |
要点:encoding 会随数据增长自动升级且不可逆(listpack→hashtable 单向);小集合用紧凑结构(listpack 连续内存、无指针开销)是 Redis 省内存的核心手段。OBJECT ENCODING key 可验证当前编码。
全局结构:字典 + 渐进式 rehash
- 所有键空间存于一个哈希表(dict,两个 dichtab)
- 扩容/缩容不一次性 rehash(会阻塞单线程),而是渐进式 rehash:新旧表并存,每次 CRUD 顺带迁移若干桶,后台定时任务辅助搬迁——把 O(N) 的 rehash 摊平到每次操作
- 过期键:惰性删除(访问时检查)+ 定期删除(每 100ms 抽样清理)双策略,平衡内存与 CPU
redisObject 的开销与优化
每个 redisObject 头约 16 字节(type/encoding/lru/refcount/ptr),小值场景开销占比高。优化手段:
- 尽量命中紧凑编码:控制 hash/set/zset 的元素数与值大小在 listpack 阈值内(阈值可调但权衡 CPU)
- 键名简短:键也是对象,长键名浪费可观(亿级键时 GB 级)
- 整数化:能存整数不存字符串(int encoding 直接存值于 ptr,零额外分配)
- 共享对象:0-9999 整数对象共享池(embstr/int 场景复用)
- 32bit vs 64bit:指针 4 vs 8 字节,内存敏感场景的历史选项(今天少用)
单线程与结构的配合
redisObject 的统一接口让单线程命令分发可以对五种 type 一视同仁;紧凑编码减少内存占用即减少缓存缺失与 swap 风险——内部结构的全部设计都服务于”单线程+全内存”这两个前提:结构要省内存(内存是瓶颈)、操作要 O(1)/O(logN)(CPU 时间片珍贵)、rehash 要渐进(不能阻塞)。
验证与排障工具
OBJECT ENCODING key/MEMORY USAGE key:单键编码与内存redis-cli --bigkeys/--memkeys:大键扫描(大 key 是删除阻塞与迁移卡顿的元凶)INFO memory:used_memory vs RSS、碎片率 mem_fragmentation_ratio(>1.5 考虑重启整理或 activedefrag)
Redis 内部结构的主线:redisObject 统一抽象 + type/encoding 二层映射(小数据紧凑编码省内存)+ 全局字典渐进 rehash(单线程不阻塞)+ 双策略过期删除。记住”encoding 自动升级不可逆”与”大 key 是万恶之源”两条,内存类问题的排查就有了起点。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 非鱼小站!
评论
WalineDisqus











