Redis的ZSet类型底层用到了跳表,来支持平均
需要注意的是,Redis使用跳表时,实际用了跳表和哈希表双份保存,来分别支持高效的范围查找和单点查询(哈希表不支持有序)。
什么是跳表
有序链表的增删改查平均时间复杂度都是O(n),跳表则是基于链表,建立多级索引,通过多级索引检索定位将增删改查时间复杂度降为
对于一个有2级索引的三层跳表,每级索引的索引个数都是基于下层元素个数的一半。
查询跳表
查询是会先从最高层,沿着链表查询,如果大于目标节点,就向下移动一层,直到找到目标元素或者到底最底层。这样类似平衡树但更加直观。空间复杂度为n/2+n/4+…+2=n-2, 即O(n)。
更新跳表
插入数据首先会查询需要插入的位置,时间复杂度
原理:足够随机的话,索引是均匀的,因此随机选取n/2的元素做以及索引,
randomLevel产生一个1-MAX_LEVEL(索引最高层数)的随机数,返回1表示不建立索引,其他数i则表示建立1-i-1级索引。
如何实现这个randomLevel函数?
产生随机数,如果不超过SKIPLIST_P(0.5),则levle+1,否则返回level,这样产生1的概率就是1/2。redis中这个SKIPLIST_P是0.25。
删除数据
删除时除了原始链表,索引中也需要删除,过程就是类似查找,查找索引过程中遇到对应的数据就删除,时间复杂度为
为什么用跳表,不用平衡树、红黑树、B+树?
主要是redis采用跳表实现zset是为了高效地进行区间查找,单个值定位其实会通过哈希表,尽管增删、以及有序性的维护用红黑树是一样的,但是按照区间查找这个操作跳表可以用O(logN)定位后遍历即可,更加高效。此外跳表实现比红黑树简单。
类似地,插入和删除来说,跳表比平衡树简单
为什么不用B+树
B+树主要是针对IO频繁场景设计的,Redis是内存数据库,不需要进行频繁IO,相对B+树来说更节省内存(索引少),维护简单。
主要是节约内存(相对B树)、有序区间操作比其他树好、更容易实现与调试