上一篇文章我们介绍了 Redis 的六种数据结构:SDS、双端链表、字典、跳表、压缩列表、整数集合。Redis 基于这些数据结构创建了对象系统,包含五大类型的对象:字符串对象、列表对象、哈希对象、集合对象、有序集合对象。
redis 对象
对象的定义:
typedef struct redisObject {
unsigned type:4; // 类型,即五大类型,标记本对象是什么类型的对象
unsigned encoding:4; // 编码方式,即类型的底层实现,即数据结构
void *ptr; // 指向底层数据结构实现的指针
int refcount; // 引用计数,用于垃圾回收
unsigned lru:22; // 最近一次被访问时间,每次使用都会被更新
// ...
} robj;
上方
type:4
表示 type 占用4个位。这是 C 语言中的“位段”的表示。
用TYPE xxx
命令可查看一个对象的类型,即 type 字段。
用OBJECT ENCODING xxx
命令查看一个对象的编码方式,即 encoding 字段。
用OBJECT IDLETIME xxx
命令查看一个对象的空转时长(多久没被访问了 curTime - lru)。这个命令本身不会更新 lru。
用OBJECT REFCOUNT xxx
命令查看一个值的引用次数
对象类型与编码
每种类型的对象都至少有两种不同的编码(数据结构)实现。

- 字符串类型: int 只存数值类型;embstr 用于短字符串(小于等于39字节);raw 即 SDS 用于长字符串。
- 列表对象:列表对象中元素数量少于 512 个,且字符串长度均小于 64字节时,用 ziplist;否则用 linkedlist。
- 哈希对象:当键值对的数量小于 512 个,且键和值的字符串均小于64字节时,用 ziplist;否则用 hashtable。
- 集合对象:当元素数量不超过 512 个,且所有元素都是整数时,用 intset;否则用 hashtable。(没用 ziplist)
- 有序集合对象:当元素数量小于 128个,且成员长度均小于 64 字节时,用 ziplist;否则用 skiplist(+字典)。
Redis 会总动进行编码转换。
embstr 编码是专门用于保存短字符串的一种优化编码方式,它SDS 用了两次内存分配函数来分配空间,而 embstr 只调用一次,分配连续的空间来存储 redisObject 和 sdshdr。释放空间时同样少一次。哈希用列表实现时会先存键再存值,两节点紧凑挨在一起。在表尾添加。
有序集合中用 skiplist 时是和字典一起使用的,因为 skiplist 是有序的,按 score 操作块;而字典存的是无序的,按 member 操作需要用字典来实现。
类型的多态
我们主要到,既然对象有不同的类型,但有一些命令可以用于不同的对象,如 GET、TTL、EXPIRE 等,有些则只能用于特定的类型。
用于特定类型的命令,会先查 redisObject.type 字段验证具体类型。
这些通用类型的命令是通过类型的多态实现的。即在不同类型的对象上用了不同的方法来实现某一命令,使用命令时只需根据对象的类型判断使用什么函数来做操作就可以了。这就是多态。
内存回收
类型对象上存有一个计数字段 refcount,对象每进行一次引用则计数+1,每取消一次引用,则计数-1。通过这一机制,实现在合适的时候进行内存回收。这是引用计数的方式实现回收内存。
对象共享
Redis 中会默认对 0~9999 的数值型数据进行内存共享。即这种对象在内存中只有一个,在不同的使用的地方只需指向它即可。
可用修改 redis.h/REDIS_SHARED_INTERGERS 来修改可共享内存的值的数量
当键名为共享对象的值时,也会使用共享对象(引用计数+1)
问:Redis 为什么只对整数值的对象做共享?
答:使用共享对象时,需要验证对象是否和共享对象一致,而数据越复杂复杂度就越高。对整型值的验证操作复杂度为O(1);对字符串的验证操作复杂度为O(N);对多个值的对象,如列表或哈希做验证时复杂度为O(N²)。为了节省CPU,做出了这样的限制。
对象的最近访问时间
redisObject.lru
属性记录了上次访问的时间。通过 OBJECT IDLETIME xxx
命令可查看多久没访问过此元素了(curTime - lru),这个命令本身不会更新 lru。
书上将这个词解释成空转时长,但 lru 不是记录的时间长度,而是个时刻,所以我直接用“最近访问时间”表达。
网友评论