Redis对象基础数据类型底层编码
字符串对象
字符串的 编码可以是 int,raw,或者embstr
- 如果一个 字符串对象保存的 是整数值,并且 这个 整数值可以用long类型来 表示,那么字符串对象会将整数值保存在字符串对象结构的ptr属性里面(将void*转换为long),并且将字符串对象的 编码设置成int
- 如果字符串保存的是一个 字符串值,并且这个 字符串值的 长度大于44字节,那么字符串对象 将使用一个简单动态字符串来保存这个字符串值,并将 对象的编码格式设置为raw
- embstr编码是专门用于保存短字符串的一种优化编码方式,这种编码和raw编码一样,都使用redisObject结构和sdshdr结构来表示字符串对象,但raw编码会调用两次内存分配函数来分别创建redisObject结构和sdshdr结构,而embstr编码则通过调用一次内存分配函数来分配一块连续的空间,空间中依次包含redisObject和sdshdr两个结构
- 可以用long double类型表示的浮点数在Redis中也是作为字符串值来保存的可以用long double类型表示的浮点数在Redis中也是作为字符串值来保存的
- embstr编码的字符串对象在执行修改命令之后,总会变成一个raw编码的字符串对象
列表对象
- 列表对象的编码可以是ziplist或者linkedlist。在 redis 3.2 版本之后,列表的底层统一采用 quicklist 实现。
我们仍然可以把 quicklist 看作是一个双向列表,但是列表的每个节点都是一个 ziplist,其实就是 linkedlist 和 ziplist 的结合。 - 当列表对象可以同时满足以下两个条件时,列表对象使用ziplist编码:
❑列表对象保存的所有字符串元素的长度都小于64字节;
❑列表对象保存的元素数量小于512个;不能满足这两个条件的列表对象需要使用linkedlist编码。
哈希对象
哈希对象的编码可以是ziplist或者hashtable。
- ziplist编码的哈希对象使用压缩列表作为底层实现,每当有新的键值对要加入到哈希对象时,程序会先将保存了键的压缩列表节点推入到压缩列表表尾,然后再将保存了值的压缩列表节点推入到压缩列表表尾,
- hashtable编码的哈希对象使用字典作为底层实现,哈希对象中的每个键值对都使用一个字典键值对来保存:
❑字典的每个键都是一个字符串对象,对象中保存了键值对的键;
❑字典的每个值都是一个字符串对象,对象中保存了键值对的值。 - 当哈希对象可以同时满足以下两个条件时,哈希对象使用ziplist编码:
❑哈希对象保存的所有键值对的键和值的字符串长度都小于64字节;
❑哈希对象保存的键值对数量小于512个;不能满足这两个条件的哈希对象需要使用hashtable编码。
集合对象
集合对象的编码可以是intset或者hashtable。
- intset编码的集合对象使用整数集合作为底层实现,集合对象包含的所有元素都被保存在整数集合里面。
- hashtable编码的集合对象使用字典作为底层实现,字典的每个键都是一个字符串对象,每个字符串对象包含了一个集合元素,而字典的值则全部被设置为NULL。
- 当集合对象可以同时满足以下两个条件时,对象使用intset编码:
❑集合对象保存的所有元素都是整数值;
❑集合对象保存的元素数量不超过512个。
不能满足这两个条件的集合对象需要使用hashtable编码。 - 只要我们向这个只包含整数元素的集合对象添加一个字符串元素,集合对象的编码转移操作就会被执行:
有序集合对象
有序集合的编码可以是ziplist或者skiplist。
- 当有序集合对象可以同时满足以下两个条件时,对象使用ziplist编码:❑有序集合保存的元素数量小于128个;
❑有序集合保存的所有元素成员的长度都小于64字节;
不能满足以上两个条件的有序集合对象将使用skiplist编码。 - ziplist编码的压缩列表对象使用压缩列表作为底层实现,每个集合元素使用两个紧挨在一起的压缩列表节点来保存,第一个节点保存元素的成员(member),而第二个元素则保存元素的分值(score)。
压缩列表内的集合元素按分值从小到大进行排序,分值较小的元素被放置在靠近表头的方向,而分值较大的元素则被放置在靠近表尾的方向。 - skiplist编码的有序集合对象使用zset结构作为底层实现,一个zset结构同时包含一个字典和一个跳跃表
- 跳跃表采用两种的原因是基于性能考虑
如果我们只使用字典来实现有序集合,那么虽然以O(1)复杂度查找成员的分值这一特性会被保留,但是,因为字典以无序的方式来保存集合元素,所以每次在执行范围型操作——比如ZRANK、ZRANGE等命令时,程序都需要对字典保存的所有元素进行排序,完成这种排序需要至少O(NlogN)时间复杂度,以及额外的O(N)内存空间(因为要创建一个数组来保存排序后的元素)。
另一方面,如果我们只使用跳跃表来实现有序集合,那么跳跃表执行范围型操作的所有优点都会被保留,但因为没有了字典,所以根据成员查找分值这一操作的复杂度将从O(1)上升为O(logN)。因为以上原因,为了让有序集合的查找和范围型操作都尽可能快地执行,Redis选择了同时使用字典和跳跃表两种数据结构来实现有序集合。