redis数据结构
redis数据结构
redis 中数据结构的底层存储形式是什么样的?例如 int 型整数会转成 sds 进行存储嘛?
Redis 有「类型(Type)」和「编码(Encoding)」两层设计,用户看到的是 String/List/Hash 等类型,但底层实际用的是不同的编码(如 int/embstr/raw/ziplist/intset 等),编码才决定了数据的真实存储形式。
前置基础:Redis 的对象系统 redisObject
Redis 用 redisObject 结构体统一管理所有对象,核心字段如下(简化版):
typedef struct redisObject {
// 1. 类型:4位,决定用户看到的类型(String/List/Hash/Set/ZSet)
unsigned type:4;
// 2. 编码:4位,决定数据的真实存储形式(int/embstr/raw/ziplist/intset/quicklist/zskiplist等)
unsigned encoding:4;
// 3. LRU时间:24位,用于LRU/LFU淘汰策略(记录最后一次访问时间)
unsigned lru:24;
// 4. 引用计数:用于内存管理(对象共享、GC)
int refcount;
// 5. 指针:指向实际数据(根据encoding指向不同结构)
void *ptr;
} robj;
健空间
redis 服务器的存储链路是分层设计的,核心层级如下:
redisServer(Redis 服务器)
└── redisDb[](数据库数组,默认 16 个,索引 0~15)
└── dict(键空间,存储该数据库所有 key-value)
└── dictht[2](两个哈希表,用于渐进式 rehash)
└── dictEntry[](哈希表节点数组)
└── dictEntry(单个键值对节点)
├── key(SDS,键)
├── v.val(redisObject*,值)
└── next(哈希冲突链指针)
这里需要注意 dictht 的 table 是 dictEntry[] 哈希表节点数组
数据结构
String 类型编码
Redis 字符串类型的编码选择是自动完成的,核心依据是「值的类型」和「值的长度」。
REDIS_ENCODING_INT(整数编码)和 REDIS_ENCODING_RAW(普通 SDS 编码),还有一个中间优化编码 REDIS_ENCODING_EMBSTR(优化的 SDS 编码)—— 这三个编码共同构成了 Redis 字符串的完整编码体系。
1、 三种字符串编码的定义
| 编码类型 | 宏定义 | 底层存储形式 | 核心特点 |
|---|---|---|---|
| 整数编码 | REDIS_ENCODING_INT | redisObject 的 ptr 字段直接存整数(不是指针!) | 无需额外内存,性能最高 |
| 优化 SDS 编码 | REDIS_ENCODING_EMBSTR | redisObject 和 SDS 是连续内存块(一次内存分配) | 减少内存碎片,适合短字符串 |
| 普通 SDS 编码 | REDIS_ENCODING_RAW | redisObject 和 SDS 是分开的内存块(两次内存分配) | 灵活,适合长字符串 |
2、三种字符串编码如何自动选择
1、优先判断:是否使用 REDIS_ENCODING_INT,条件:value 是整数,且必须在 long 类型 范围内。(范围:-2^63 ~ 2^63-1),主义 value 可以是字符串形式的整数,如 "123"。
127.0.0.1:6379> SET int_key 123
OK
127.0.0.1:6379> OBJECT ENCODING int_key
"int" # 满足条件,用 INT 编码
127.0.0.1:6379> SET neg_int_key -456
OK
127.0.0.1:6379> OBJECT ENCODING neg_int_key
"int" # 负整数也满足,用 INT 编码
不满足条件的情况(不用 INT):
- 情况 1:值不是整数(如
"hello"、"123abc"); - 情况 2:是整数,但超过
long范围(如"12345678901234567890",共 20 位,超过2^63-1)。
2、不满足 INT 时,判断用 EMBSTR 还是 RAW
如果值不满足 INT 编码的条件,Redis 会进一步判断字符串长度,选择 EMBSTR 或 RAW:
| 编码类型 | 选择条件 |
|---|---|
REDIS_ENCODING_EMBSTR | 字符串长度 ≤ 44 字节(Redis 5.0+);≤ 39 字节(Redis 5.0 之前) |
REDIS_ENCODING_RAW | 字符串长度 > 44 字节(Redis 5.0+);> 39 字节(Redis 5.0 之前) |
⚠️,这里的 44 不一定是对的,在 官方提供的 8.2.1 版本的 redis 上测试,发现阈值为 34 字节,也即
<=34 个字节使用 embstr 编码。但这并不影响实际性能,有一个公式:EMBSTR 最大字符串长度 = jemalloc 小内存块大小 redisObject 大小 SDS 头大小不同版本的 Redis 中,redisObject大小、SDS 头大小、jemalloc 的内存块大小都可能变化,导致阈值不同。Redis 默认使用 jemalloc 作为内存分配器,jemalloc 的小内存块大小是固定的(如 8、16、32、48、64 字节等)
为什么是 44 字节(Redis 5.0+)?
这是 Redis 的内存优化设计:
redisObject占 16 字节;EMBSTR用的是sdshdr8(SDS 的最小头结构),占 1 字节;- 字符串内容最多 44 字节;
- 总大小:
16 + 1 + 44 = 61 字节,刚好能放进 jemalloc 的 64 字节内存块(避免内存碎片)。
127.0.0.1:6379> SET embstr_key "hello world" # 长度 11 ≤ 44
OK
127.0.0.1:6379> OBJECT ENCODING embstr_key
"embstr" # 用 EMBSTR 编码
127.0.0.1:6379> SET raw_key "123456789012345678901234567890123456789012345" # 长度 45 > 44
OK
127.0.0.1:6379> OBJECT ENCODING raw_key
"raw" # 用 RAW 编码
127.0.0.1:6379> SET big_int_key 12345678901234567890 # 超过 long 范围
OK
127.0.0.1:6379> OBJECT ENCODING big_int_key
"embstr" # 不满足 INT,长度 ≤44,用 EMBSTR
3、编码转换规则
Redis 的字符串编码不是永久的**,以下情况会触发编码转换:
INT → RAW/EMBSTR 的转换
触发条件(满足任一):
- 条件 1:对
INT编码的 key 执行了需要字符串操作的命令(如APPEND、SETRANGE、GETRANGE等); - 条件 2:值被修改为非整数或超过 long 范围的整数。
127.0.0.1:6379> SET int_key 123
OK
127.0.0.1:6379> OBJECT ENCODING int_key
"int"
127.0.0.1:6379> APPEND int_key "abc" # 执行 APPEND,必须转成字符串
(integer) 6
127.0.0.1:6379> GET int_key
"123abc"
127.0.0.1:6379> OBJECT ENCODING int_key
"embstr" # 转成 EMBSTR(长度 ≤44)
EMBSTR → RAW 的转换
触发条件(满足任一):
- 条件 1:对
EMBSTR编码的 key 执行了修改命令(如APPEND、SETRANGE等)使长度 > 44 字节; - 条件 2:字符串长度被修改为 > 44 字节。
127.0.0.1:6379> SET embstr_key "hello"
OK
127.0.0.1:6379> OBJECT ENCODING embstr_key
"embstr"
127.0.0.1:6379> APPEND embstr_key " world very long string that exceeds 44 bytes..." # 长度超过 44
(integer) 50
127.0.0.1:6379> OBJECT ENCODING embstr_key
"raw" # 转成 RAW
4、字符串编码选择
1. 检查值是否是「可表示为 long 的整数」?
├─ 是 → 用 REDIS_ENCODING_INT
└─ 否 → 进入下一步
2. 检查字符串长度?
├─ ≤44 字节(Redis 5.0+)→ 用 REDIS_ENCODING_EMBSTR
└─ >44 字节 → 用 REDIS_ENCODING_RAW
3. 后续修改可能触发编码转换:
├─ INT → EMBSTR/RAW(执行字符串操作或值改变)
└─ EMBSTR → RAW(执行修改操作或长度超过阈值)
用 OBJECT ENCODING key 命令查看某个 key 的底层编码,直观验证选择逻辑