ZSet跳表和压缩列表原理

【Redis】拼多多面试官问我zset底层是如何实现的,我反手就把跳表的数据结构画了出来

总结:
普通链表是一个节点一个节点按顺序的排序,而跳表是为每个节点随机出一个层数,每一层指向的节点可能会不同。
并且,越高层的链表节点数越少。这样查找的话,会从高层开始查找,一层一层往下找,最终在第一层找出其精准位置。
这样的查找方式可以跳过很多下层节点数,大大加快查找速度。
而对于插入和删除等,也只需要修改对应节点前后指针而已,这就比平衡树更有优势。

为什么采用跳表,而不使用哈希表或平衡树实现呢?
1、skiplist和各种平衡树(如AVL、红黑树等)的元素是有序排列的,而哈希表不是有序的。因此,在哈希表上只能做单个key的查找,不适宜做范围查找。所谓范围查找,指的是查找那些大小在指定的两个值之间的所有节点。
2、在做范围查找的时候,平衡树比skiplist操作要复杂。在平衡树上,我们找到指定范围的小值之后,还需要以中序遍历的顺序继续寻找其它不超过大值的节点。如果不对平衡树进行一定的改造,这里的中序遍历并不容易实现。而在skiplist上进行范围查找就非常简单,只需要在找到小值之后,对第1层链表进行若干步的遍历就可以实现。
3、平衡树的插入和删除操作可能引发子树的调整,逻辑复杂,而skiplist的插入和删除只需要修改相邻节点的指针,操作简单又快速。
4、从内存占用上来说,skiplist比平衡树更灵活一些。一般来说,平衡树每个节点包含2个指针(分别指向左右子树),而skiplist每个节点包含的指针数目平均为1/(1-p),具体取决于参数p的大小。如果像Redis里的实现一样,取p=1/4,那么平均每个节点包含1.33个指针,比平衡树更有优势。

REDIS - 跳跃表与压缩列表
【最完整系列】Redis-结构篇-压缩列表(压缩列表看这个)

Redis的配置文件中关于有序集合底层实现的两个配置。
1、zset-max-ziplist-entries 128:zset采用压缩列表时,元素个数最大值。默认值为128。
2、zset-max-ziplist-value 64:zset采用压缩列表时,每个元素的字符串长度最大值。默认值为64。

当满足以下任一条件时,Redis便会将zset的底层实现由压缩列表转为跳跃表。
1、zset中元素个数大于zset_max_ziplist_entries;
2、插入元素的字符串长度大于zset_max_ziplist_value。
zset在转为跳跃表之后,即使元素被逐渐删除,也不会重新转为压缩列表。

压缩列表ziplist本质上就是一个字节数组,是Redis为了节约内存而设计的一种线性数据结构,可以包含多个元素,每个元素可以是一个字节数组或一个整数。

因为ziplist是紧凑存储,没有冗余空间,意味着新插入元素,就需要扩展内存:
1、分配新的内存,将原数据拷贝到新内存; 
2、扩展原有内存;
所以ziplist 不适合存储大型字符串,存储的元素也不宜过多。

猜你喜欢

转载自blog.csdn.net/weixin_43871678/article/details/115163940