1. 哈希对象
哈希对象的编码可以是ziplist或者hashtable;
ziplist编码:每当有新的键值对要加入时,会先键推入到压缩列表表尾,然后再将值推入到列表表尾,因此:同一键值对的两个节点总是紧挨在一起,保存键的节点在前,保存值的节点在后;先添加到哈希对象中的键值对会被放到压缩列表的表头方向,后添加的键值对会被放在列表的表尾方向;


hashtable编码:使用字典作为底层实现,每个键值对都使用一个字典键值对来保存:

1.1 编码转换
满足以下两个条件时,哈希对象使用ziplist编码:
- 哈希对象保存的所有键值对的键和值的字符串长度都小于64字节;
- 哈希对象保存的键值对数量小于512个;
当然这两个上限值是可以修改的。
1.2 哈希命令的实现

2. 集合对象
集合对象的编码可以是intset或者hashtable。
intset编码的集合示例:

hashtable编码的集合示例:

2.1 编码的转换
当集合对象同时满足以下两个条件时,对象使用intset编码:
- 集合对象保存的所有元素都是整数值;
- 集合对象保存的元素数量不超过512个;
第二个条件的上限值可以修改。
对于intset编码的集合,如果上面两个条件任意一个不满足时,就会执行对象的编码转换操作,原本保存在整数集合中的所有元素都会被转移并保存到字典里面。
2.2 集合命令的实现

3. 有序集合对象
有序集合的编码可以是ziplist或者skiplist。
当ziplist作为有序集合的底层实现时,每个元素使用两个紧挨在一起的压缩列表节点保存,第一个节点保存元素的成员,而第二个元素则保存元素的分值;集合元素按照分值从小到大进行排序,分值较小的元素在靠近表头的方向。
当我们执行ZADD命令时,服务器会创建一个有序集合对象作为price的值;示例如下:


skiplist编码的有序集合对象使用zset结构作为底层实现,一个zset结构同时包含一个字典和一个跳跃表。zset结构中的zsl跳跃表按分值从小到大保存了所有集合元素,每个跳跃表节点都保存了一个集合元素:跳跃表节点的object属性保存了元素的成员,而跳跃表节点的score属性则保存了元素的分值,通过这个跳跃表,程序可以对有序集合进行范围型操作,比如zrank,zrange等命令。
除此之外,zset结构中的dict字典为有序集合创建了一个从成员到分值的映射,字典中的每个键值对都保存了一个集合元素:字典的键保存了元素的成员,而字典的值则保存了元素的分值。通过这个字典,程序可以用O(1)复杂度查找给定元素的分值,zscore命令就是根据这一特性实现的。
有序集合每个元素的成员都是一个字符串对象,而每个元素的分值都是一个double类型的浮点数。值得一提的是,虽然zset结构同时使用跳跃表和字典来保存有序集合元素,但这两种数据结构都会通过指针来共享相同元素的成员和分值,所以同时使用跳跃表和字典来保存集合元素不会产生任何重复成员或者分值,也不会因此而浪费额外的内存。
skiplist编码的有序集合示例:


需要注意:上图为了展示方便,字典和跳跃表重复展示了各个元素的成员和分值,但在实际中,字典和跳跃表会共享元素的成员和分值。
为什么有序集合需要同时使用跳跃表和字典来实现?
理论上,有序集合可以单独使用字典或者跳跃表的其中一种数据结构来实现,但无论是单独使用字典还是跳跃表,在性能上都会有所降低。举个例子,如果值使用字典来实现有序集合,那么虽然O(1)复杂度查找成员分值这一特性会被保留,但是,因为字典时无序的,每次执行范围型操作,比如zrank,zrange等命令时,程序都需要对字典保存的所有元素进行排序,完成这种排序需要至少O(NlogN)时间复杂度,以及额外的O(N)内存空间。如果只使用跳跃表,范围型操作的有点会被保留,但根据成员查找分值这一操作的复杂度会上升。
3.1 编码的转换
当有序集合可以同时满足以下两个条件时,对象使用ziplist编码,反之则使用skiplist编码:
- 有序集合保存的元素数量小于128个;
- 有序集合保存的所有元素成员的长度都小于64字节;
元素数量和成员长度的上限值是可以修改的。
3.2 有序集合命令的实现

1万+

被折叠的 条评论
为什么被折叠?



