文章目录
- 一、对象
- 二、字符串对象
- 三、列表对象
- 四、哈希对象
- 五、集合对象
- 六、有序集合对象
一、对象
前面学习了SDS、双端列表,字典,压缩列表,整数集合等。但是Redis并没有直接使用这些数据结构来实现键值对数据库,而是使用这些数据结构来创建一个对象系统,这个系统包含字符串对象,列表对象,哈希对象,集合对象和有序集合对象。
通过这五种不同类型的对象,redis在执行命令前,根据对象的类型来判断一下对象是否可以执行给定的命令,另一个好处是我们可以通过不同的场合从而设置多种不同的数据结构实现,从而优化对象在不同场景的执行效率。
redis的对象系统还使用了基于引用技术的内存回收机制,当程序不使用某个对象时,这个对象占用的内存就会被自动释放;另外,redis还通过引用技术实现了对象共享机制,这一机制可以在适当的条件下,通过让多个数据库键共享一个对象来节约内存。
redis使用对象来表示数据库中的键和值,每次当我们在redis的数据库新创建一个键值对的时候,我们至少会创建两个对象。一个作为键,一个作为值。
对象的数据结构如下
typedef struct redisObject {
// 类型
unsigned type:4;
// 编码
unsigned encoding:4;
// 对象最后一次被访问的时间
unsigned lru:REDIS_LRU_BITS; /* lru time (relative to server.lruclock) */
// 引用计数
int refcount;
// 指向实际值的指针
void *ptr;
} robj;对象的类型如下
#define REDIS_STRING 0 //字符串队形
#define REDIS_LIST 1 //列表对象
#define REDIS_SET 2 //集合对象
#define REDIS_ZSET 3 //有序集合对象
#define REDIS_HASH 4 //哈希对象对象的ptr指针指向对象的底层数据结构,它由encoding属性决定。
encoding 属性如下
#define REDIS_ENCODING_RAW 0 //SDS
#define REDIS_ENCODING_INT 1 //long类型的整数
#define REDIS_ENCODING_HT 2 //字典
#define REDIS_ENCODING_ZIPMAP 3 //zipmap
#define REDIS_ENCODING_LINKEDLIST 4 //双端链表
#define REDIS_ENCODING_INTSET 6 //整数集合
#define REDIS_ENCODING_SKIPLIST 7 //跳跃表
#define REDIS_ENCODING_EMBSTR 8 //embstr编码的简单动态字符串二、字符串对象
字符串对象的编码可以是int,raw或者是embstr
如果字符串对象保存的是整数值,并且可以用long表示,那么字符串对象会将整数值保存在字符串对象结构的ptr中。如果保存的是大于39字节的字符串,那么就使用SDS来保存,如果小于39字节,那么字符串将使用embstr编码的方式来保存。
embstr是专门用于保存短字符串的一种优化编码方式,这种编码和raw编码一样,都使用redisObject结构和sdshdr结构。raw编码会调用两次内存分配函数,而embstr只会调用一次。
使用embstr有如下好处:
- 内存分配只需要一次
- 内存释放只需要一次
- 编码的数据在一块连续的内存里,可以利用缓存的优势。
int 编码的字符串和embstr编码的字符串在某些情况下可以转为raw编码。
对于Int编码的字符串,对对象执行一些命令,使得它保存的不是整数值,而是一个字符串的值,那么它将变为raw。
对于embstr,我们没有修改的程序,所以从某种意义上是只读的,所以当修改时会将其变为embstr
三、列表对象
列表对象的编码可以是ziplist或者linkedlist
ziplist编码的列表对象使用压缩列表作为底层实现。linkedlist编码的列表使用双端列表作为底层实现,每个双端列表节点都保存了一个字符串对象,而每个字符串对象都保存了一个列表元素。
当满足如下两个条件,列表对象使用ziplist对象编码:
- 列表对象保存的所有字符串元素的长度都小于64字节
- 保存的元素小于512个
不能满足的则使用linkedlist
四、哈希对象
哈希对象的编码可以ziplist或者hashtable
ziplist编码的哈希对象使用压缩链表作为底层实现,每当有新的键值对加入到哈希对象时,程序会先将保存了键的压缩列表推入到压缩列表表尾。
hashtable编码的哈希对象使用字典作为底层实现,哈希对象中的每个键值对都使用一个字典键值对来保存:
- 字典的每个键都是一个字符串对象
- 字典的每个值都是一个字符串对象
当满足以下两个条件时,哈希对象使用ziplist编码:
- 哈希对象的所有键值对的键和值的字符串的长度都小于64字节
- 哈希对象保存的键值对数量小于512个
五、集合对象
集合对象的编码可以是intset或者hashtable
intset编码的集合对象使用整数集合作为底层实现就,集合对象的所有元素都被保存在整数集合里
hashtable 编码的集合对象使用字典作为底层实现,字典的每个键都是一个字符串对象,每个字符串对象都包含了一个集合元素,而字典的值则全被设置为NULL。
当满足:
*集合对象里保存的所有元素值都是整数
*对象保存的元素数量不超过512个
不满足时就会变为hashtable
六、有序集合对象
有序集合的编码是ziplist或者是skiplist
ziplist编码的有序集合对象使用压缩列表作为底层实现,每个集合元素使用两个紧挨在一起的压缩列表节点来保存,第一个节点保存元素的成员,而第二个元素则保存元素的分值。
skiplist编码的有序集合对象使用zset作为底层实现
typedef struct zset {
// 字典,键为成员,值为分值
// 用于支持 O(1) 复杂度的按成员取分值操作
dict *dict;
// 跳跃表,按分值排序成员
// 用于支持平均复杂度为 O(log N) 的按分值定位成员操作
// 以及范围操作
zskiplist *zsl;
} zset;当有序集合对象同时满足:
- 成员的长度小于64字节
- 有序集合元素数量小于128个
使用ziplist编码,否则使用skiplist编码
参考《Redis设计与实现》
















