Redis(7) —— Redis 中的五大对象

本文详细介绍了Redis中的五大对象:字符串、列表、哈希、集合和有序集合,及其各自的编码方式,包括int、raw、embstr、ziplist、linkedlist、hashtable等,探讨了不同编码在不同场景下的应用和优化策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Redis 中存在五种底层的数据结构:简单动态字符串(SDS)、双端链表、字段、压缩列表、整数集合
Redis 中由五种基本对象:字符串对象、列表对象、哈希对象、集合对象和有序集合对象
Redis 中的基本对象的回收基于引用计数法

1. 对象的类型和编码

Redis 中的对象都是由 RedisObject 结构表示,该结构保存着三个属性:type 属性、encoding 属性 和 ptr 属性

typedef struct RedisObject{
    //类型
    unsigned type:4;
    
    //编码
    unsigned encoding:4;
    
    //指向底层数据结构的指针
    void *ptr;
}

通过 TYPE 命令,可以查看 Reids 的值对象类型(五种对象中的一种)

通过 OBJECT ENCODING 命令可以查看数据库键的值对象的编码(key-value 中 value 的编码,而编码决定底层数据结构的种类)


对象的五种基本类型:

类型常量对象的名称TYPE 命令的输出
REDIS_STRING字符串对象“string”
REDIS_LIST列表对象“list”
REDIS_HASH哈希对象“hash”
REDIS_SET集合对象“set”
REDIS_ZEST有序集合对象“zset”


不同类型和编码的对象:


编码和底层实现(encoding 属性和 ptr 指针):

使用 OBJECT ENCODING 命令可以查看一个数据库键的值对象的编码,而这个编码决定了 ptr 指针指向的底层数据结构,也就是说 encoding 属性决定了 Redis 对象底层使用的数据结构。


通过 encoding 属性来决定对象的编码,而不是为 Redis 的对象指定一个底层实现,这样可以在不同的情形下使用不同的底层结构,这样提升了 Reids 的灵活性和效率。



2. 字符串对象以及其三种底层实现

字符串对象的编码可以是 int、raw、embstr

***int:***

如果一个字符串对象保存的是整数值,而且这个整数值可以用 long 表示,那么字符串对象会将整数值保存在 ptr 属性里面(将 void * 转换成 long),并且将 encoding 属性设置为 “int”;

redis> SET number 10086
OK

redis> OBJECT ENCODING number
"int"

raw:

如果一个字符串对象保存的是一个长度大于 39 字节的字符串值, 那么字符串对象将使用一个简单动态字符串(SDS)来保存这个字符串,并且将 encoding 属性设置为 raw。

redis> SET story "long long long long·······"
OK

redis> OBJECT ENCODING story
"raw"

embstr:

如果一个字符串对象保存的是一个长度小于等于 39 字节的字符串值,那么字符串对象会将 encoding 属性设置为 embstr。

redis> SET msg "hello"
OK

redis> OBJECT ENCODING msg
"embstr"

embstr 也是使用 SDS 来作为底层实现,不过在这上面做了一些优化:

分配一块连续的内存来保存 RedisObject 对象和 SDS 对象(RedisObject 对象和 SDS 对象的地址空间连在一起, 而且 embstr 类型的对象也连在一起)

这样做的好处:

在分配内存和回收内存的时候 embstr 只需要做一次内存的内配和回收,而 raw 需要做两次

所有的 embstr 对象都连在一起,所以这样能更好利用缓存来带来优势。



3. 列表对象及其两种实现

列表对象的编码可以是 ziplist 或者 linkedlist。

ziplist:

ziplist 编码的列表对象使用压缩列表作为底层实现,每个压缩列表的节点(entry)保存了一个列表元素。

使用条件:

  • 列表对象保存的所有字符元素的长度都小于 64 字节
  • 列表对象保存的元素小于等于 512 个
redis> RPUSH blah "hello" "world" "again"
OK

redis> OBJECT ENCODING blah
"ziplist"

linkedlist:
linkedlist 编码的列表使用双端链表作为底层实现,每个双端链表节点(node)都保存了一个字符串对象,而每个字符串对象都保存了一个列表元素。

使用条件:

  • 列表对象保存的字符中至少有一个长度大于等于 64 字节
  • 或者列表对象的元素大于 512 个
redis> RPUSH blah "hello" "world" "again"
(integer) 3

redis> OBJECT ENCODING blah
"ziplist"

# 将一个 65 字节的元素推入列表对象中
redis> RPUSH blah "wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww"
(integer) 4

# 编码已改变
redis> OBJECT ENCODING blah
"linkedlist"


4. 哈希对象及其两种实现

哈希对象编码可以是 ziplist 或者 hashtable

ziplist:

ziplist 编码的哈希对象会使用压缩列表作为底层实现,每当有新值对要加入到哈希对象时,程序会先将保存了键的压缩列表节点推入到压缩列表表尾,然后再将保存了值的压缩列表节点推入到压缩列表表尾

所以可以得到用 ziplist 作为哈希表实现的两个性质:

  • 保存了同一对键值的两个节点总是连在一起,键在前,值在后
  • 先加入压缩列表的键值对在靠近压缩列表的表头方向,后加入的靠近压缩列表的表尾方向

使用条件:

  • 哈希对象保存的所有键值对的键和值的字符串长度都小于 64 字节
  • 哈希对象保存的键值对数量小于 512 字节;

不能满足上述两个条件的将哈希对象将使用 hashtable 编码

redis> HSET book name "Mastering C++ in 21 days"
(integer) 1

redis> OBJECT ENCODING book
"ziplist"

# 像哈希对象添加一个新的键值对,键的长度为 66 字节
redis> HSET book long_long_long_long_long_long_long_long_long_long_long_description "content"
(integer) 1

# 编码已经改变
redis> OBJECT ENCODING book
"hashtable"

hashtable:

hashtable 编码的哈希对象使用字典作为底层实现,哈希对象中的每个键值对都使用一个哈希键值来保存:

  • 字典的每一个键都是一个字符串对象,对象中保存了键值对的键
  • 字典的每一个值都是一个字符串对象,对象中保存了键值对的值


5. 集合对象及其两种实现

集合对象的编码可以使 intset 或者 hashtable

intset:

intset 编码的集合对象使用整数集合作为底层实现,集合对象包含的所有元素都被保存在整数集合里面

使用条件:

  • 集合对象保存的所有元素都是整数值
  • 集合对象保存的元素数量不超过 512 个

当不能满足上述两个条件的时候将使用 hashtable 编码

redis> SADD numbers 1 3 5
(integer) 3

redis> OBJECT ENCODING numbers
"intset"

redis> SADD numbers "seven"
(integer) 1

redis> OBJECT ENCODING numbers
"hashtable"

hashtable:

hashtable 编码的集合对象使用字典作为底层实现,字典的每个键都是一个字符串对象,每个字符串对象包含了一个集合元素,而字典的值则全部被设置为 NULL


6. 有序集合对象及其两种实现

有序集合的编码可以是 ziplist 或者 skiplist

ziplist:

ziplist 编码的有序集合对象使用压缩列表作为底层实现。

每个集合元素使用两个紧挨在一起的压缩列表节点来保存,第一个节点保存成员(member)第二个节点保存元素的分值(score)


压缩列表内的集合元素按照分值从小到大进行排序,分值小的元素放置在靠近表头位置,分值大的元素放置在靠近表尾的位置。

使用条件:

  • 有序集合保存的所有元素成员的长度都小于 64 字节
  • 有序集合保存的元素数量小于 128 个

不满足上述两个条件则使用 skiplist 编码

redis> ZADD blah 1.0 www
(integer) 1

redis> OBJECT ENCODING blah
"ziplist"

# 向有序集合添加一个成员为 66 字节长的元素
redis> ZADD blash 2.0 oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo
(integer) 1

# 编码已改变
redis> OBJECT ENCODING blah
"skiplist"


skiplist:

skiplist 编码的有序集合对象使用 zset 结构作为底层实现,一个 zset 结构同事包含一个字典和一个跳跃表

字典用于存储对象到分值之间的映射。


跳跃表则按分值从大到小保存了所有集合元素,每个跳跃表节点都保存了一个元素

typedef struct zset{
    zskiplist *zsl;
    dict *dict;
}zset;


参考资料

[1].《Redis 设计与实现》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值