Redis——对象

1,前面学习了几种数据结构,Redis系统使用这些数据结构创建了一个对象系统。这样就可以根据对象的类型来判断一个对象是否可以执行给定的命令。使用对象的另一个好处是可以针对不同的使用场景,为对象设置多种不同的数据结构实现,从而优化对象在不同场景下的使用效率。
2,Redis中的每个对象都由一个redisObject结构表示,该结构中和保存数据有关的3个属性分别是type、encoding、ptr。

typedef struct redisObject{
    
    
	//类型
	unsigned type:4;
	//编码
	unsigned encoding:4;
	//指向底层实现数据结构的指针(void类型的属性)
	void *ptr;
	//
}robj;

1)type的取值有5个(REDIS_STRING、REDIS_LIST、REDIS_HASH、REDIS_SET、REDIS_ZSET有序集合对象)。
Redis的键值对来说,键总是一个字符串对象,而值则可以是上面的5种之一。
需要注意的是当我们对数据库键执行TYPE命令时,命令返回的结果为数据库键对应的值对象的类型,而不是键对象的类型。
2)对象ptr指针指向对象的底层实现数据结构,而这些数据结构由对象的encoding属性决定。
encoding属性记录了对象底层使用的是什么数据结构,并且每种类型的对象都至少使用了2种不同的编码(几种编码就说明可以用几种数据结构实现)。使用命令OBJECT ENCODING可以查看键值对中值的编码。
需要注意的是,Redis为了提供效率,允许将对象从一种编码转换成另一种编码来提高效率。
3,字符串对象
字符串对象的编码可以是int,raw或者embstr。
如果键值对的值是一个整数,并且可以使用long类型来表示,这个时候就会把编码设置为int。
如果键值对的值是一个字符串并且长度小于等于39,则把对象的编码设置为embstr。否则把对象设置为raw。需要注意的是,raw会调用两次内存分配函数来分别创建redisObject结构和sdshdr结构。而embstr编码则通过调用一次内存分配函数来分配一块连续的空间。使用embstr编码的字符串对象来保存短字符有以下好处:将内存分配次数从raw的2次降低为1次,释放的时候也是如此。
Redis没有为embstr编码的字符串对象编写任何相应的修改程序,所以embstr编码的字符串对象实际是只读的。当我们对embstr编码的字符串对象执行任何修改命令时,需要先将对象的编码从embstr转成raw。
4,列表对象
列表对象的编码可以是ziplist或者linkedlist。当列表对象可以同时满足以下两个条件时,列表对象使用ziplist编码:
1) 列表对象保存的所有字符串元素的长度都小于64
2)列表对象保存的元素数量小于512个,
不能满足这两个条件的列表对象需要使用linkedlist编码,以上两个条件的上限值是可以修改的。当使用ziplist编码所需的两个条件的任意一个不能被满足时,对象的编码转换操作就会被执行,原本保存在压缩列表里的所有列表元素都会被转移并保存到双端链表里面,对象的编码也会从ziplist变为linkedlist。
5,哈希对象
哈希对象的编码可以是ziplist或者hashtable。
ziplist编码的哈希对象使用压缩列表作为底层实现,每当有新的键值对要加入到哈希对象时,**程序会先将保存了键的压缩列表节点推入到压缩列表表尾,然后再将保存了值的压缩列表节点推入到压缩列表表尾。**这样就会造成先添加到哈希对象中的键值会被放在压缩列表的表头方向,后被放入的在表尾方向。
hashtable编码的哈希对象使用字典作为底层实现,哈希对象的每个键值对都使用一个字典键值对来保存:
字典的每个键都是一个字符串对象,对象中保存了键值对的键;字典的每个值都是一个字符串对象,对象中保存了键值对的值。
当哈希对象可以同时满足以下两个条件时,哈希对象使用ziplist编码:
1)哈希对象保存的所有键值对的键和值的字符串长度都小于64字节
2)哈希对象保存的键值对数量小于512;不能满足这两个条件的哈希对象需要使用hashtable编码。
6,集合对象
集合对象编码可以是intset或者hashtable。
intset编码的集合对象使用的是整数集合作为底层实现,而hashtable编码的集合使用字典作为底层实现。字典的每个键都是一个集合元素,字典的值设置为空。
当集合对象可以同时满足以下两个条件时,对象使用intset编码:
1)集合对象保存的所有元素都是整数值; 2)集合对象保存的元素数量不超过512
不满足这两个条件的集合对象需要使用hashtable编码。
7,有序集合对象
有序结合的编码可以是ziplist或者skiplist。
ziplist编码的有序集合对象使用压缩列表作为底层实现,每个集合元素使用两个紧挨在一起的压缩列表节点来保存,第一个节点保存元素的成员,而第二个元素则保存元素的分值。
skiplist编码的有序集合对象使用zset结构作为底层实现,一个zset结构同时包含一个字典和一个跳跃表。

typedef struct zset{
    
    
	zskiplist *zsl;
	dict *dict;
}zset;

字典中保存的是成员到分值的映射,用过这个数据结构可以更快的查找成员的分值。虽然zset结构同时使用跳跃表和字典来保存有序集合元素,但这两种数据结构都会通过指针来共享相同元素的成员和分值,因此不会产生任何重复成员或者分值。
需要注意的是,有序集合可以只使用跳跃表或者只使用字典来实现,但是没有同时使用两种数据结构效率高。
当有序结合可以同时满足以下两个条件时。对象可以使用ziplist编码:
1)有序集合保存的元素数量小于128个
2)有序集合保存的所有元素成员的长度小于64字节
如不能满足以上两个条件的有序集合对象将使用skiplist编码。
8,Redis中用于操作键的命令基本上可以分为两种类型。其一:可以对任何键执行的命令 其二:只能对特定的键执行的命令(其实很好理解,就是在结构中没有对应的方法)。所以Redis在执行命令前,要先对命令进行检查,如果可以,还需要通过编码类型找到对应的API。
9,尽管共享更复杂的对象可以节省更多的内存,但受到CPU时间的限制,Redis只对包含数值的字符串对象进行共享。因为一个共享对象保存的值越复杂,验证共享对象和目标对象是否相同所需的复杂度就越高。
10,redisObject中lru属性,记录了对象最后一次被命令程序访问的时间。这个的作用是可以用它来决定是否释放这块空间,具体策略要看具体的算法。

猜你喜欢

转载自blog.csdn.net/xiaoan08133192/article/details/115263371