Redis对象系统中包含字符串对象、列表对象、哈希对象、集合对象、有序集合对象。
实现了基于引用计数的内存回收机制。
一,对象的类型与编码
Redis使用对象来表示数据库中的键和值。
/*
* Redis 对象
*/
typedef struct redisObject {
// 类型
unsigned type:4;
// 编码方式
unsigned encoding:4;
// LRU 时间(相对于 server.lruclock)
unsigned lru:22;
// 引用计数
int refcount;
// 指向底层实现数据结构的指针
void *ptr;
} robj;
type表示了该对象的对象类型:
- REDIS_STRING 字符串对象
- REDIS_LIST 列表对象
- REDIS_HASH 哈希对象
- REDIS_SET 集合对象
- REDIS_ZSET 有序集合对象
举例:
(1)字符串对象
字符串对象的编码可以是int、raw、embstr
如果字符串对象保存的是整数值,并且这个整数值是可以用long类型来表示,字符串的编码设置为int。
如果值是字符串对象,且长度大于39字节,那么编码为raw
如果值是字符串对象,且长度小于等于39字节,那么编码为embstr
embstr的创建只需分配一次内存,而raw为两次,分别创建redisObject结构和sdshdr结构。相对地,embstr释放内存的次数也由两次变为一次。embstr的objet和sds放在一起,更好地利用缓存带来的优势。redis并未提供任何修改embstr的方式,即embstr是只读的形式。对embstr的修改实际上是先转换为raw再进行修改。
字符串对象是Redis五种类型的对象中唯一一种会被其他四种对象嵌套的对象。
(2)列表对象
列表对象的编码可以是ziplist或者linkedlist。
当列表对象同时满足下面两个条件时,则使用ziplist:
- 所有字符串元素的长度都小于64字节
- 元素数量小于512
ziplist是一种压缩列表,它的好处是更能节省内存空间,因为它所存储的内容都是在连续的内存区域当中的。当列表对象元素不大,每个元素也不大的时候,就采用ziplist存储。但当数据量过大时就ziplist就不是那么好用了。因为为了保证他存储内容在内存中的连续性,插入的复杂度是O(N),即每次插入都会重新进行realloc。如下图所示,对象结构中ptr所指向的就是一个ziplist。整个ziplist只需要malloc一次,它们在内存中是一块连续的区域。
linkedlist是一种双向链表。它的结构比较简单,节点中存放pre和next两个指针,还有节点相关的信息。当每增加一个node的时候,就需要重新malloc一块内存。
(3)哈希对象
哈希对象的底层实现可以是ziplist或者hashtable。
当列表对象同时满足下面两个条件时,则使用ziplist:
- 所有键值对的键和值的字符串度都小于64字节
- 键值对数量小于512
(4)集合对象
集合对象的编码可以是intset或者hashtable。
满足下面两个条件,使用intset:
- 所以有元素都是整数值
- 元素数量不超过512个
(5)有序集合对象
有序集合的编码可能两种,一种是ziplist,另一种是skiplist与dict的结合。
为什么有序集合需要使用字典和跳表来实现?
因为字典可以实现O(1)访问,但是数据无序范围查找需要O(NlogN)时间;跳表是有序的,可以实现范围查找,但是查找键值操作时间为O(logN)。
当有序集合对象同时满足下面两个条件时,则使用ziplist:
- 所有元素的字符串度都小于64字节
- 元素数量小于128
二,类型检查与命令多态
Redis的命令分为两类:
(1)可以对任何类型的键执行:DEL、EXPIRE、RENAME、TYPE、OBJECT命令
(2)只能对特殊对象的键执行:
- 类型检查的实现
在执行一个类型特定的命令之前,Redis会先检查输入键的类型是否正确,然后再决定是否执行命令。
- 多态命令的实现
我们对一个键执行LLEN命令,服务器除了要确保执行命令的是列表键之外,还需要根据键值对象所使用的的编码来选择正确的LLEN命令实现。
实际上我们将DEL、EXPIRE、TYPE等命令也称为多态命令。这些命令和LLEN等命令的区别在于,前者是基于类型的多态———一个命令可以同时用于处理多种不同类型的键,而后者是基于编码的多态——一个命令可以同时处理多种不同的编码。
三,内存回收
四,对象共享
除了用于实现引用计数内存回收机制之外,对象的引用计数属性还带有对象共享的作用。
Redis会共享值为0-9999的字符串对象,目前服务器只对包含整数值的字符串对象进行共享。
五,对象的空转时长
redisObject结构包含的最后一个属性为lru,该属性记录了对象最后一次被命令程序访问的时间。