redis数据类型与底层数据结构-redis6与redis7的区别

redis数据类型与底层数据结构

文档

  1. redis单机安装
  2. redis常用的五种数据类型
  3. springboot整合redis-RedisTemplate单机模式
  4. 布隆过滤器 -Bloom Filter

官方文档

  1. 官网操作命令指南页面:https://redis.io/docs/latest/commands/?name=get&group=string
  2. Redis cluster specification

说明

  1. redis7版本:7.0.0
  2. 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 字节时使用。它将 redisObjectSDS 分配在同一块连续内存中,只需一次内存分配,减少内存碎片。
    • raw :当字符串长度 > 44 字节时使用。redisObjectSDS 分开分配内存,适合存储超长字符串。
  • List / Hash / ZSet 等集合类型的演进(ziplist 到 listpack):
    • 在 Redis 6 及以前,当集合元素较少且值较小时,会使用 ziplist(压缩列表)来节省内存。但 ziplist 存在致命的级联更新(Cascade Update)缺陷:当某个节点数据扩容超过 254 字节时,会导致后续所有节点的长度记录字段重新分配空间,引发连锁反应,阻塞主线程造成性能卡顿。
    • Redis 7 的革命性升级 :用 listpack(紧凑列表)全面替代 ziplistlistpack 不再记录上一个节点的长度,而是记录当前节点的长度,节点之间互不影响,彻底消除了级联更新问题,大幅提升了高并发写入时的性能和稳定性。
  • Set 类型的特殊处理:
    • 当集合中全为整数且数量较少时,使用 intset(整数集合)以有序数组的形式存储,极大节省内存。
SDS 与传统 C 字符串的区别

在 Redis 中,为了追求极致的性能和内存利用率,并没有直接使用 C 语言原生的字符串,而是自己设计了 SDS(Simple Dynamic String,简单动态字符串) 。同时,针对不同的数据场景,Redis 在底层设计了 int、embstr、raw 三种灵活的编码方式。

传统的 C 语言字符串本质上是一个以空字符 \0 结尾的字符数组,这种设计在 Redis 这种高性能内存数据库中存在诸多缺陷。SDS 在此基础上进行了深度优化,主要区别如下:

  1. 获取长度的时间复杂度
    • C 字符串:没有记录自身长度,获取长度必须遍历整个数组,时间复杂度为 O(n) 。
    • SDS :内部维护了 len 属性来记录已使用的字节数,获取长度只需直接读取该属性,时间复杂度为 O(1)。
  2. 杜绝缓冲区溢出
    • C 字符串:在进行字符串拼接或修改时,如果忘记重新分配足够的空间,极易发生内存溢出,覆盖相邻内存的数据。
    • SDS:API 是安全的。在执行修改操作前,SDS 会首先检查当前空间是否满足需求,如果不满足会自动进行动态扩展,完全杜绝了缓冲区溢出的问题。
  3. 减少内存重分配次数
    • C 字符串:每次修改字符串长度,都必然需要执行内存重分配。
    • SDS :采用了空间预分配惰性空间释放 策略。例如,当修改后长度小于 1MB 时,SDS 会分配与当前长度相同的额外未使用空间(free 属性);当长度大于等于 1MB 时,每次多预留 1MB。这使得后续的追加操作大概率不需要重新分配内存,极大提升了性能。
  4. 二进制安全
    • C 字符串 :以 \0 作为结束标识,因此字符串内部不能包含 \0,只能保存文本数据。
    • SDS :使用 len 属性来确定字符串的实际长度,不依赖空字符终止符。因此 SDS 能够存储包括图片、序列化对象在内的任意二进制数据。
底层实现示例及查询验证命令
String 类型 (Redis 6、7)
  • int(Redis 6、7):存储纯整数

    shell 复制代码
    127.0.0.1:6379> SET my_int 12345
    OK
    127.0.0.1:6379> OBJECT ENCODING my_int
    "int"
  • embstr(Redis 6、7):存储长度 ≤ 44 字节的短字符串

    shell 复制代码
    127.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 字节的长字符串

    shell 复制代码
    127.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 字节

    shell 复制代码
    127.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 字节

    shell 复制代码
    127.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 字节

    shell 复制代码
    127.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)

    shell 复制代码
    127.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

    shell 复制代码
    127.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

    shell 复制代码
    127.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 字节

    shell 复制代码
    127.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 字节

    shell 复制代码
    127.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 字节

    shell 复制代码
    127.0.0.1:6379> ZADD my_zset 2.0 "1234567890123456789012345678901234567890123456789012345678901234a"
    (integer) 1
    127.0.0.1:6379> OBJECT ENCODING my_zset
    "skiplist"

参考资料

  1. https://www.bilibili.com/video/BV13R4y1v7sP

注意事项

  1. 部分内容由AI生成
  2. 如有不对,欢迎指正!!!