Redis的基本数据类型
Redis使用对象来表示数据库中的键和值。当我们在Redis中创建一个键值对的时候,最少要创建两个对象。其中的键,也就是key,一定是字符串对象。其中的值,也就是value,可以是:字符串对象(String),列表对象
(list),哈希对象(hash),集合对象(set),有序集合对象(Sort Set);
其中每个对象都是由一个redisObject结构表示,这个结构里面由三个字段属性,分别是:type(类型),encoding (编码方式) ,ptr(指向编码方式的底层实现).
字符串对象(String)
字符串对象的编码可以是int,raw或者embstr。
1.如果字符串对象保存的是整数值,那么对应的字符串对象编码encoding为int,ptr指针指向的底层数据结构为整数.
2.如果字符串对象保存的是字符串值,并且字符串值长度大于39字节, 那么对应的字符串对象编码encoding为raw,ptr指针指向底层数据结构为SDS(简单动态字符串)。
3.如果字符串对象保存的是字符串值,并且字符串值长度小于39字节,那么对应的字符串对象编码encoding为
embstr,ptr指针指向底层数据结构为SDS(简单动态字符串);
编码的转换:
1,对于int编码的字符串对象来说,如果执行一些命令(比如说添加字符串),使得这个对象存的不再是整数,而是字符串时,字符串的编码将自动转为raw
2.对于embstr编码的字符串对象来说,Redis没有为embstr编码的字符串对象编写任何修改程序,所以当embstr编码的字符串对象进行修改的时候,字符串编码自动转为raw。
列表对象(list)
列表对象的编码可以是ZipList或者LinkedList
1,ZipList编码的列表使用压缩列表作为底层实现。
2.LinkedList编码的列表使用双端链表作为底层实现。每个双端链表节点保存了一个字符串对象,每个字符串对象里面又保存了一个列表元素。
3.字符串对象可以嵌套在其他四种对象里面。
编码的转换:
当列表对象同时满足以下两个条件的时候,列表对象使用ZipList编码。不满足以下条件的的列表对象,需要使用LinkedList编码。
1.列表对象保存的所有字符串元素长度都小于64字节
2.列表对象保存的元素数量小于512个
哈希对象(hash)
哈希对象的编码可以是ZipList或者hashtable
1.ZipList编码的哈希对象使用压缩列表作为底层实现,先加入的键值对放在靠近表头的地方,后加入的放在靠近表尾的地方。
2.hashtable编码的哈希对象使用字典(字典使用哈希表作为底层实现)作为底层实现。字典中的每一个键,每一个值都是字符串对象,在字符串对象里面分别保存了键和值。
编码的转换:
当列表对象同时满足以下两个条件的时候,哈希对象使用ZipList编码。不满足以下条件的的哈希对象,需要使用hashtable编码。
1.哈希对象保存的所有键值对的键和值的字符串长度都小于64字节
2.哈希对象保存的键值对数量小于512个
集合对象(Set)
集合对象的编码可以是intSet或者hashtable
1.intSet编码的集合对象使用整数集合作为底层实现。
2.hashtable编码的集合对象使用字典作为底层实现。每个键都是一个字符串对象,每个字符串对象包含了一个集合元素,而字典的值都是null.
编码的转换:
当集合对象同时满足以下两个条件的时候,集合对象使用intSet编码。不满足以下条件的的集合对象,需要使用hashtable编码。
1.集合对象保存的所有元素都是整数值,
2。集合对象保存的元素不超过512个。
有序集合对象(Sort Set)
有序集合对象的编码方式可以是ZipList或者skipList
1.ZipList编码的有序集合对象使用的是压缩列表作为底层实现。每个集合元素使用两个紧挨一起的压缩列表的节点来保存,第一个节点保存成员,第二个节点保存分数,压缩列表内的集合按照分数从小到大排序. 分数小的靠近表头,分数大的靠近表位
2.SkipList编码的有序集合对象使用zSet结构作为底层实现。其中Zset结构同时包含一个字典和一个跳跃表.
跳跃表是有序的,按照分数进行了从小到大的排序,通过跳跃表可以对有序集合进行范围型操作,效率较高。而字典为有序集合创建了一个成员到分值的映射,可以通过O(1)的时间复杂度查找给定成员的分数。
问:有序集合使用了两个数据结构来保存。会造成成员和分数重复,浪费额外内存的现象吗?
有序集合使用这两种数据结构来实现,字典和跳跃表会通过指针来共享相同的成员和分值,所以同时使用跳跃表和字典不会产生任何重复成员和分值,也不会浪费内存
问:为什么有序集合需要同时使用跳跃表和字典来实现呢?
在理论上,有序集合可以单独使用字典或者跳跃表其中一种数据结构来实现。但是无论单独使用哪个,在性能上比同时使用它们两个的效率都会有所降低。因为跳跃表是有序的,可以根据分数进行范围查找,效率较高,但是根据成员去查分数的效率较低,时间复杂度为O(logN)。而字典范围查找,需要对保存的元素进行排序,至少需要O(NlogN)的时间复杂度和O(N)的内存空间(因为要创建一个数组来保存排序后的元素),效率比较低,但是字典可以O(1)的时间复杂度根据成员查分数。因为以上原因,为了让有序集合的查找和范围型操作都尽可能的快速执行,Redis选择同时使用了字典和跳跃表两种数据结构来实现有序集合.
编码的转换:
当有序集合对象同时满足以下两个条件的时候,集合对象使用ZipList编码。不满足以下条件的的有序集合对象,需要使用SkipList编码。