您的位置:首页 > 数据库 > Redis

结合redis设计与实现的redis源码学习-8.0-object(对象)

2017-10-15 23:42 633 查看
重点来了!

在前面的几个章节里,我们陆续学习了redis用到的所有主要数据结构,但是redis并没有直接使用这些数据结构来实现键值对数据库,而是基于这些数据结构创建了一个对象系统,包含字符串对象,列表对象,哈希对象,集合对象和有序集合对象这五种类型的对象,美中对象都用到了至少一中前面的数据结构。

通过这五种不同的对象,redis可以在执行命令之前根据对象的类型来判断一个对象是否可以执行给定的命令。使用对象还有一个好处,我们可以针对不同的使用场景,为对象设置多种不同的数据结构实现,从而优化对象在不同场景下的使用效率。

除此之外,redis还实现了基于引用计数器的内存回收机制和对象共享机制;

最后,redis的对象带有访问时间记录信息,这个信息可以用于计算数据库键的空转时长,在服务器使用了maxmemory的时候,空转时长较大的那些键可能会优先被服务器删除。

一、对象的类型与编码

redis使用对象来表示数据库中的键和值,每当我们在redis的数据库中新创建一个键值对时,我们至少会创建两个对象,一个对象用作键值对的键,另一个对象用作键值对的值。

redis中的每个对象都由一个redisObject结构表示,该结构定义在server.h中,其中和保存数据有关的分别是type属性,encoding属性和ptr属性:

typedef struct redisObject {
unsigned type:4;//类型
unsigned encoding:4;//编码
unsigned lru:LRU_BITS; /* lru time (relative to server.lruclock) */
int refcount;
void *ptr;//指向底层数据结构的指针
} robj;


1、类型

对象的type属性记录了对象的类型,这个属性的值标识了stirng,list,hash,set,zset集中对象。对于redis数据库保存的键值对来说,键总是一个字符串对象,而值则可以是以上五种的其中一种。

使用type命令查看键值对的值类型。

2、编码和底层实现

对象的ptr指针指向对象的底层实现结构,而这些数据结构由encoding属性决定。

使用object encoding命令可以查看一个数据库键的值对象的编码;

二、字符串对象

字符串对象的编码可以是int、raw、或者embstr。

int:如果一个字符串对象保存的是整数值,并且这个整数值可以用long类型来表示,那么字符串对象会将整数值保存在字符串对象结构的ptr属性中,并将字符串对象的编码设置为int。

raw:如果字符串对象保存的是一个字符串值,并且这个字符串值得长度大于39字节,那么字符串对象将使用一个SDS来
b547
保存着个字符串值,并将对象编码设置为raw。

embstr:如果字符串对象保存的是一个字符串值,并且这个字符串值得长度小于等于39字节,那么字符串将使用embstr来保存这个字符串值。

embstr是专门用来保存短字符串的一种优化编码方式,这种编码和raw编码一样,都是用redisObject和sdshdr来保存字符串对象,但raw会分别调用两次内存分配这两个结构,而embstr则通过调用一次内存分配函数来分配一块连续的空间,空间中一次包含这两个结构。

可以用long double类型表示的浮点数在redis中也是作为字符串值来保存的,如果我们要保存一个浮点数到字符串对象里,那么程序会先将这个浮点数转换成字符串,然后再保存转换所得的字符串值。在有需要的时候,程序会将保存在字符串对象里的字符串值转换回浮点数值,执行某些操作后再转回字符串值,继续保存在字符串对象里。

编码转换:int编码的字符串对象和embstr编码的字符串对象在条件满足的情况下,会被转换为raw编码的字符串对象。

三、列表对象

列表对象的编码可以是ziplist或者linkedlist。

编码转换:当列表对象同时满足以下两个条件是,列表对象使用ziplist编码:

1、列表对象保存的所有字符串元素的长度都小于64字节;

2、列表对象保存的元素数量小于512个;

不能满足这两个条件的列表对象需要使用linkedlist编码。这两个条件的上限是可以修改的。

四、哈希对象

哈希对象的编码可以是ziplist或者hashtable。

如果是ziplist的哈希对象,总是键在前,值在后排列的。

编码转换:当哈希对象可以同时满足以下两个条件时,哈希对象使用ziplist编码:

1、哈希对象保存的所有键值对的键和值得字符串长度都小于64字节;

2、哈希对象保存的键值对小雨512个;

不能满足这两个条件的哈希对象需要使用hashtable编码。这两个条件的上限是可以修改的。

五、集合对象

集合对象的编码可以是intset或者hashtable。

使用hashtable编码时,字典的每个键都是字符串对象,每个字符串对象包含了一个集合元素,而字典的值则全部被设置为NULL;

编码转换:当集合对象可以同时满足以下两个条件时,集合对象使用intset编码:

1、集合对象保存的所有元素都是整数值;

2、集合对象保存的元素数量不超过512个。

不能满足这两个条件的哈希对象需要使用hashtable编码。这两个条件的上限是可以修改的。

六、有序集合对象

有序集合对象使用ziplist或者skiplist编码。

redis为了同时满足查找和有序的效率,使用dict字典为有序集合创建了一个从成员到分值的映射,字典中的每个键值对都保存了一个集合元素。

编码转换:当有序集合对象可以同时满足以下两个条件时,集合对象使用ziplist编码:

1、有序集合保存的所有元素数量小于128个;

2、有序集合保存的所有元素的长度都小于64字节。

不能满足这两个条件的哈希对象需要使用skiplist编码。这两个条件的上限是可以修改的。

七、类型检查与命令多态

redis中用于操作的命令基本上分为两种类型:

1、可以对任何类型的键执行的如:DEL,EXPIRE,TYPE,OBJECT命令等;

2、只能对特定类型的键执行的如:SET,GET,APPEND,STRLEN对字符串;

1、类型检查

redis在执行特定的命令前,会先检查输入键的类型是否正确,然后再决定是否执行给定的命令。

类型检查是通过redisObject结构的type属性来实现的。

2、多态命令的实现

一个命令可以处理不用的类型,命令会在输入后判断数据类型,然后决定用哪种方式操作。

八、内存回收

redis在自己的对象系统中构建了一个引用计数计数实现的内存回收机制。

每个对象的引用计数信息由reidsObject结构的refcount属性记录。

九、对象共享

对象的引用计数属性还带有对象共享的作用。

目前,redis会在初始化服务其实,创建一万个字符串对象,这希尔对象包含了从0到9999的所有整数值,当服务器需要用到这些字符串对象时,服务器就会使用这些共享对象,而不是创建新对象。

为了保证操作效率,redis支队包含整数值的字符串对象进行共享。

十、对象的空转时长

redisObject结构包含的lru属性记录了对象最后一次被命令程序访问的时间;

如果服务器打开了maxmemory选项,并且服务器用于回收内存的算法为volatile-lru或者allkeys-lru,那么当服务器占用的内存数超过了maxmemory选项所设置的上限值时,空转时长较高的那部分键会被优先释放,从而回收内存。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐