Redis 对象
Redis 对象
Redis 基于数据结构定义了一个对象系统、包括字符串对象、列表对象、哈希对象、集合对象、有序集合对象。
1 结构
对于一个键值对,Redis 会用两个字符串对象来分别表示 key
和 value
。
Redis 中每个对象都由 redisObject
表示:
typedef struct redisObject {
// 类型
unsigned type;
// 编码
unsigned encoding;
// 指向底层实现数据结构的指针
void *ptr;
}
1.1 类型
记录了对象的类型:

键总是一个字符串对象,而值可以是所有类型中的一个。
1.2 编码
编码定义了对象具体使用了什么数据结构作为底层的实现,每种类型的对象可以由不同的数据结构实现。

Redis 可以根据不同的使用场景决定一个对象设置不同的编码,提升效率、节约内存。
2 字符串对象
字符串对象编码包括int、 embstr、raw
三种:
Int
: 字符串保存的是一个整数值,且可以用 long
表示。
Raw
: 如果字符串的长度大于32字节,使用一个简单的动态字符串sds保存这个值。
Embstr
: 如果小于32字节使用 embstr
类型,embstr
将 redisObject
和 sdshdr
结构放在一起,一次分配内存中一段联系的空间保存数据。
2.1 命令

3 列表对象
列表对象的编码可以是 ziplist
或者 linkedlist
。
对于一个列表 numbers
, 执行RPUSH numbers 1 "three" 5
后,如果是用ziplist编码,最终的结构为:

如果是用 linkedlist
编码结构为:

对于 linkedlist
的每一个节点的,都包含了字符串对象。
3.1 编码转换
使用 ziplist
条件:
- 列表中所有字符串长度都小于64字节。
- 列表元素数量小于512。
3.2 列表命令

3.3 消息队列服务
Redis List
经常被用于消息队列服务。假设消费者程序在从队列中取出消息后立刻崩溃,但由于该消息已经被取出且没有被正常处理,那么可以认为该消息已经丢失,由此可能会导致业务数据丢失,或业务状态不一致等现象发生。
为了避免这种情况,Redis 提供了 RPOPLPUSH
命令,消费者程序会原子性的从主消息队列中取出消息并将其插入到备份队列中,直到消费者程序完成正常的处理逻辑后再将该消息从备份队列中删除。同时还可以提供一个守护进程,当发现备份队列中的消息过期时,可以重新将其再放回到主消息队列中,以便其它的消费者程序继续处理。
4 哈希对象
哈希对象的编码可以是 ziplist
或者 hashtable
。
ziplist编码的哈希对象使用压缩列表作为底层实现,每当有键值对加入哈希对象时,会先将键的节点加到列表表尾,然后将值的节点加入到列表表尾。

hashtable
编码使用字典作为底层实现,键值都使用字符串对象。

4.1 编码转换
使用 ziplist
编码的条件:
- 键值的字符串长度都小于64字节。
- 键值对数量小于512。
4.2 命令

5 集合对象
集合对象的编码可以是 intset、hashtable
。
intset
编码的集合对象使用整数集合作为底层实现。
hashtable
编码的集合对象底层使用字典实现,字典的每一个键都是一个字符串对象,值全部被设置为 NULL
。
5.1 编码的转换
使用intset编码的条件:
- 集合对象的所有元素都是整数值。
- 集合对象保存的元素数量不超过 512 个。


6 有序集合
有序集合的编码是 ziplist
或 skiplist
。
ziplist
编码使用压缩列表作为底层实现,每个集合元素使用两个挨在一起的节点保存,分别是元素的成员和对应的分值。根据分值按从小到大进行排序。
skiplist
编码的有序集合使用zset作为底层实现,一个 zset
包含一个字典和一个跳跃表。
typedef struct zset {
zskiplist *zsl;
dict *dict;
} zset;
通过跳跃表可以对有序集合快速进行范围查找。而通过字典结构可以用 O(1)
的复杂度查找给定成员的分值,两个数据结构会共享数据指针。

6.1 编码转换
使用 ziplist
的条件:
- 数量小于128个。
- 所有元素成员的长度小于64字节。
6.2 命令实现方法

7 内存回收
在 redisObject
中定义了一个引用计数的字段,当引用计数值为0时,对象锁占用的内存就会被回收。
8 空转时长
redisObject
包含一个 lru
属性,记录了对象最后一次被命令程序访问的时间。主要用在对象回收时计算未访问时长。