skiplist->称之为跳表,作为redis中有序集合(sort set)的数据结构.我们也可以理解为有序的链表(sort linkedlist)
首先,先来回顾一下链表
上图只是一个最简单的单向链表,
在java中LinkedList就是链表的实现(具体说就是一个双向链表)
相对于arraylist:
- 优势就是删除和新增的时候,不需要节点移位,只需要将next指针指向下一个就可以了
- 缺点:就是查找必须通过遍历所有的节点查找,运气好第一个节点就可以查到,如果刚好查找最后一个元素,则要遍历N次,即时间复杂度是O(n)
后来为了优化链表的查找:提出了二分查找法
二分查找法:对应的链表必须是有序的(我们可以采用插入排序的方式将元素在插入的时候就排好序)
查找过程:
将查找的元素和处于middle的元素比较
如果小于,则获取0到middle之间的middle比较
如果大于,则获取middle到n之间middle比较
以此类推,最终比较的次数就是logn,所以时间复杂度是:O(log(n))
skiplist 跳表
让我们来了解redis怎么存储有序列表的
redis的有序列表的使用:
是根据score (可以理解为分数,也就是排序点)
命令:
zadd key score memeber
zrangebyscore key min max 根据分数范围查找
添加两个
zadd rank 1 baidu 2 alipay
skiplist知识点:
- level : redis中存储一个score-memeber时候,会随机生成一个level,代表这个数据存在哪个层级中(类似于分片的概念)(因为是C语言写的,具体最大level不知道,我们可以当做0-256之间也行)
- 生成随机算法的是:
//我这里将大概意思转换成伪代码,具体c语言中会有一点点不同(不去深究,我们只是了解结构的设计思想)
int getLevel(){
int level = 1
int random = new Random().next();
while( random&oxffff < zklist_p&oxffff){
level+=1;
}
return level<max_level?level:max_level;
}
- 我们可以把每一个存储的对象当做一个Entry(跟hashmap中存储类似),Entry的结构是:
level[] : 因为每一个数据存储,就会随机生成一个level值,这个level数组存储的就是从1-level的所有值(level有多少,skiplist会当做这个memeber在每层都存在,查找的时候就会当做这个member存每层都存在)
skiplist存储
我们假设存储3个元素 score-member:
1-aa
2-bb
3-cc
我们假设三次随机生成的level是:1 2 3
存储方式:
- 先随机生成level值
- 在当前层级level,用插入排序方式,有序的插入到所在level层级中,用next指向下一个同级元素
*随机出来的level,就代表这个score/member所属哪个层级
*每一层都是链表连接
skiplist查找
会从list的最大level开始查找,如果该level层级的header->tail 找不到对应的元素,则向下个层级level查找,
如果上图,假设我们要查找score=3元素
在一般的有序链表中,我们就需要遍历3次,才能找到
如果用二分查找法:即log3, 肯定是>1的对吧,但是n越大,次数越大
但是如果用skiplist的从最高level向下查找的话:
如果我们存储3的时候,正好随机生成的level=3的话,这样,只要1次就可以查找到对吧,在大数据量的时候,就是这样提高查找效率,
总结:通过每层level分区数据,通过一层一层level往下跳的方式查找,就通过这样设计思想提高检索效率,
可以把这种当做LinkedHashMap<Level,LinkedList<?>> 这样的数据结构存储