Redis并没有直接使用这些基础数据结构实现键值对的数据库,而是基于这些数据结构创建一个对象系统。包括字符串对象,列表对象,哈希对象,集合对象和有序集合对象。
Redis中每个对象都由一个redisObject结构表示,该结构如下:
typedef struct redisObject {
unsigned type:4;//类型,记录了对象的类型, 这个类型包括下面表格8-1
unsigned encoding:4;//编码
void *ptr; //指向底层实现数据结构的指针
int refcount; //对象的引用计数
unsigned leu:22;//对象最后一次被命令程序访问的时间
}robj;
对一个数据库键执行 TYPE 命令可以来查看键对应值的类型,对应的列表如下:
编码决定了使用什么样的底层数据结构,下图对应的编码:
用OBJECT ENCODING命令查看数据库值对象的编码。
1.字符串对象
编码可以是int,raw和enbstr
当字符串保存的是整数值,例如: SET number 10086 那么对象结构如下:
如果是大于32个字节的字符串,SET story “Long, long ago there lived a king …” , 对象的类型是raw, 对象结构如下:
如果字符串长度小于32字节,字符串对象将使用embstr编码方式保存,他是专门用于保存短字符串的一种优化编码方式。embstr仅会调用一次内存分配函数来分配连续内存空间。raw是两次。因此在释放空间的时候也只需一次。
用long double类型表示的浮点数在Redis中也是作为字符串保存的。程序会现将这个浮点数转换成字符串值。字符串值保存各种不同对象值所用的编码:
embstr编码的字符串对象是制度的,对embstr字符串对象执行的修改命令程序会先转化成raw类型,然后执行修改。
2.列表对象
编码可以是zippiest或linked list,zippiest是压缩的对于 RPUSH number 1 “three” 5, ziplist的存储格式如下:
对于linked list编码的使用双端队列底层实现,每个节点保存了一个字符串对象,其结构如下所示:
当满足下面条件时列表对象使用ziplist编码(1).列表对象保存的所有字符串长度小于64字节.(2).列表对象保存的元素数量小于512个
3.哈希对象
哈希对象的编码可以是zippiest或者hashtable, ziplist使用压缩列表实现,对于ziplist实现举个例子:
HSET profile name “Tom"
HSET profile age 25
HSET profile career “Programmer"
使用了ziplist的哈希对象如下:
使用了hashtable编码的结构如下:
当同时满足下面两个条件时使用ziplist编码格式存储哈希对象:(1).哈希对象保存的所有键值对的键和值的字符串长度都小于64字节。(2).哈希对象保存的键值对数量小于512个。
4.集合对象
集合对象的编码可以是:intset或者hash table. intset编码的对象使用的是整数集合作为底层实现。
当集合对象同时满足下面两个条件时,对象使用intset编码:(1).集合对象保存的都是整数值。(2).集合对象保存的元素不超过512个。
5.有序集合对象
可以使用ziplist或者skiplist编码。使用ziplist的时候每个几何元素使用两个紧挨在一起的压缩列表节点来保存,第一个节点保存元素 成员,第二个节点保存分数。并且压缩列表集合元素是从小到大排序的。
ZADD price 8.5 apple 5.0 banana 6.0 cherry
如果是skiplist编码的有序集合对象,使用zset结构作为底层实现:
typedef struct set {
zskiplist *zsl; //
dict *dict;//保存成员到分值的映射
}zset;
对于使用zset结构的存储结构图如下:
当同时满足下面两个条件时,对象使用ziplist编码:(1).有序集合保存元素数量小于128.(2).有序集合保存的所有元素成员长度小于64字节
6.类型检查与命令多态
有些命令可以适用于任意类型。例如: DEL, EXPIRE, RENAME, TYPE, OBJECT, 有些只适用于指定类型:
- SET, GET, APPEND, STRLEN等适用于字符串键执行
- HDEL, HSET, HGET, HLEN等适用于哈希键执行
- RPUSH, LPOP, LINSERT, LLEN等适用于列表键
- SADD, SPOP, SINTER, SCARD等适用于集合键
- ZADD, ZCARD, ZRANK, ZSCORE等适用于有序集合键
在执行Redis命令之前会先检查对象是否支持对应的命令,命令检查是通过redisObject中的type实现的。
7.对象的共享
通过引用计数来实现,类似于智能指针,这样可以节省空间
8.对象的空转时长