redis数据类型与底层数据结构
文档
官方文档
说明
- redis7版本:7.0.0
- redis6版本:6.0.19
redis数据类型与底层数据结构
Redis 6 与 Redis 7 底层数据结构对比
| 数据类型 | Redis 6 底层实现 | Redis 7 底层实现 |
|---|---|---|
| String (字符串) | SDS (int / embstr / raw) | SDS (int / embstr / raw) |
| Hash (哈希) | ziplist + hashtable | listpack + hashtable |
| List (列表) | quicklist + ziplist | quicklist + listpack |
| Set (集合) | intset + hashtable | intset + listpack + hashtable |
| ZSet (有序集合) | ziplist + skiplist + dict | listpack + skiplist + dict |
- Redis 6 与 Redis 7 在底层数据结构上的最大区别在于:Redis 7 全面引入了
listpack(紧凑列表)来替代ziplist(压缩列表) ,以解决原有的性能瓶颈,ziplist的"连锁更新"卡顿问题。
存在不同底层数据结构的原因
- String 类型的三种编码:
- int :当值可以转换为
long型整数时使用,直接在指针中存储数值,节省内存。 - embstr :当字符串长度 ≤ 44 字节时使用。它将
redisObject和SDS分配在同一块连续内存中,只需一次内存分配,减少内存碎片。 - raw :当字符串长度 > 44 字节时使用。
redisObject和SDS分开分配内存,适合存储超长字符串。
- int :当值可以转换为
- List / Hash / ZSet 等集合类型的演进(ziplist 到 listpack):
- 在 Redis 6 及以前,当集合元素较少且值较小时,会使用
ziplist(压缩列表)来节省内存。但ziplist存在致命的级联更新(Cascade Update)缺陷:当某个节点数据扩容超过 254 字节时,会导致后续所有节点的长度记录字段重新分配空间,引发连锁反应,阻塞主线程造成性能卡顿。 - Redis 7 的革命性升级 :用
listpack(紧凑列表)全面替代ziplist。listpack不再记录上一个节点的长度,而是记录当前节点的长度,节点之间互不影响,彻底消除了级联更新问题,大幅提升了高并发写入时的性能和稳定性。
- 在 Redis 6 及以前,当集合元素较少且值较小时,会使用
- Set 类型的特殊处理:
- 当集合中全为整数且数量较少时,使用
intset(整数集合)以有序数组的形式存储,极大节省内存。
- 当集合中全为整数且数量较少时,使用
SDS 与传统 C 字符串的区别
在 Redis 中,为了追求极致的性能和内存利用率,并没有直接使用 C 语言原生的字符串,而是自己设计了 SDS(Simple Dynamic String,简单动态字符串) 。同时,针对不同的数据场景,Redis 在底层设计了 int、embstr、raw 三种灵活的编码方式。
传统的 C 语言字符串本质上是一个以空字符 \0 结尾的字符数组,这种设计在 Redis 这种高性能内存数据库中存在诸多缺陷。SDS 在此基础上进行了深度优化,主要区别如下:
- 获取长度的时间复杂度
- C 字符串:没有记录自身长度,获取长度必须遍历整个数组,时间复杂度为 O(n) 。
- SDS :内部维护了
len属性来记录已使用的字节数,获取长度只需直接读取该属性,时间复杂度为 O(1)。
- 杜绝缓冲区溢出
- C 字符串:在进行字符串拼接或修改时,如果忘记重新分配足够的空间,极易发生内存溢出,覆盖相邻内存的数据。
- SDS:API 是安全的。在执行修改操作前,SDS 会首先检查当前空间是否满足需求,如果不满足会自动进行动态扩展,完全杜绝了缓冲区溢出的问题。
- 减少内存重分配次数
- C 字符串:每次修改字符串长度,都必然需要执行内存重分配。
- SDS :采用了空间预分配 和惰性空间释放 策略。例如,当修改后长度小于 1MB 时,SDS 会分配与当前长度相同的额外未使用空间(
free属性);当长度大于等于 1MB 时,每次多预留 1MB。这使得后续的追加操作大概率不需要重新分配内存,极大提升了性能。
- 二进制安全
- C 字符串 :以
\0作为结束标识,因此字符串内部不能包含\0,只能保存文本数据。 - SDS :使用
len属性来确定字符串的实际长度,不依赖空字符终止符。因此 SDS 能够存储包括图片、序列化对象在内的任意二进制数据。
- C 字符串 :以
底层实现示例及查询验证命令
String 类型 (Redis 6、7)
-
int(Redis 6、7):存储纯整数
shell127.0.0.1:6379> SET my_int 12345 OK 127.0.0.1:6379> OBJECT ENCODING my_int "int" -
embstr(Redis 6、7):存储长度 ≤ 44 字节的短字符串
shell127.0.0.1:6379> SET my_short_str "hello_world" OK 127.0.0.1:6379> OBJECT ENCODING my_short_str "embstr" -
raw(Redis 6、7):存储长度 > 44 字节的长字符串
shell127.0.0.1:6379> SET my_long_str "This is a very long string that exceeds forty-four bytes limit" OK 127.0.0.1:6379> OBJECT ENCODING my_long_str "raw"
Hash 类型
-
listpack(Redis 7):元素数量 < 512 且单值 ≤ 64 字节
shell127.0.0.1:6379> HSET user:100 name "Alice" age "25" (integer) 2 127.0.0.1:6379> OBJECT ENCODING user:100 "listpack" -
ziplist (Redis 6):元素数量 < 512 且单值 ≤ 64 字节
shell127.0.0.1:16379> HSET user:100 name "Alice" age "25" (integer) 2 127.0.0.1:16379> OBJECT ENCODING user:100 "ziplist" -
hashtable(Redis 6、7):元素数量 ≥ 512 或单值 > 64 字节
shell127.0.0.1:6379> HSET user:100 description "This is a very long string value that is exactly sixty-five bytes long." (integer) 1 127.0.0.1:6379> OBJECT ENCODING user:100 "hashtable"
List 类型
-
quicklist(Redis 6、7)
shell127.0.0.1:6379> RPUSH my_list "item1" "item2" "item3" (integer) 3 127.0.0.1:6379> OBJECT ENCODING my_list "quicklist"
Set 类型
-
intset(Redis 6、7):所有元素均为整数,且元素数量 < 512
shell127.0.0.1:6379> SADD my_int_set 10 20 30 (integer) 3 127.0.0.1:6379> OBJECT ENCODING my_int_set "intset" -
hashtable(Redis 6、7):包含非整数元素,或元素数量 ≥ 512
shell127.0.0.1:6379> SADD my_str_set "apple" "banana" (integer) 2 127.0.0.1:6379> OBJECT ENCODING my_str_set "hashtable"
ZSet 类型
-
listpack (Redis 7):元素数量 ≤ 128 且单值 ≤ 64 字节
shell127.0.0.1:6379> ZADD my_zset 1.0 "member1" 2.0 "member2" (integer) 2 127.0.0.1:6379> OBJECT ENCODING my_zset "listpack" -
ziplist (Redis 6):元素数量 ≤ 128 且单值 ≤ 64 字节
shell127.0.0.1:16379> ZADD my_zset 1.0 "member1" 2.0 "member2" (integer) 2 127.0.0.1:16379> OBJECT ENCODING my_zset "ziplist" -
skiplist(Redis 6、7):元素数量 > 128 或单值 > 64 字节
shell127.0.0.1:6379> ZADD my_zset 2.0 "1234567890123456789012345678901234567890123456789012345678901234a" (integer) 1 127.0.0.1:6379> OBJECT ENCODING my_zset "skiplist"
参考资料
注意事项
- 部分内容由AI生成
- 如有不对,欢迎指正!!!