Zset 有两种编码格式 , 一种是ziplist(双向链表的形式), skiplist(跳跃表的形式),Zset会根据相应的规则来选择编码格式,
一、ziplist (压缩列表)
1.条件 :
- 元素个数小于128个
- 元素的长度小于64
ziplist采用的是双向的链表的结构实现, 且是一个特殊的双向链表,为啥特殊呢,因为链表的设计采用了连续的内存空间。
2.结构:
<zlbytes> <zltail> <zllen> <entry> <entry> ... <entry> <zlend>
zbytes :
整个记录了ziplist占用的字节数 (32位)
ztail :
最后一个数据实体的元素的的偏移量(32位) 这就使得我们能更加快速的找到最后一个元素,它能以O(1)的时间复杂度在表的两端提供push和pop操作
zlen :
数据实体的元素的个数 (16位),当数据量个数超出2^16-1 时,zlen会将16位全部置为1,
这时zlen 不再表示当前ziplist的数据元素个数, 此时要想知道元素个数就只能通过遍历的方式去得到。
zlentry:
ziplist中的数据实体元素
zlend :
ziplist的结束标志位 固定位数值255
不足:
因为内存地址是需要连续的,所以大量的数据的时候不适合,如果是大量的数据的话就会需要大量的连续的内存地址,这可能也是是为啥呀限制,数据超过128后要转出为跳跃表
3.zentry得结构组成
prerawlen :
前一个数据实体的长度,通过当前地址-prerwalen可以快速的找到前一个数据
len:
当前实体的数据长度
endcoding :
编码格式
data :
实际的数据 <value,score>
二、skiplist (跳跃表)
条件:
当不满足ziplist的条件的时候就会使用skiplist的方式,即不满足(元素个数小于128,数据长度小于64)
skiplist 结构其实由一个 score 跳跃链表 + 一个字典构成
字典中记录了每个score —>对应的value 的地址 ,只要通过跳跃表查到对应的score 就可以 在字典 中根据score 快速找到对应的value
结构:
与标准的跳跃表不同, 每次插入数据的时候会随机的产生一个层数,因为如果是严格的跳跃表的话,相邻两节点的层高之比固定为2:1, 这样的话就会存在在加入数据的时候,为了保证层高固定比例而必须去调整后面的节点,就会影响查找的效率
产生随机层数的源码:
#define ZSKIPLIST_MAXLEVEL 32
#define ZSKIPLIST_P 0.25
int zslRandomLevel(void) {
int level = 1;
while ((random()&0xFFFF) < (ZSKIPLIST_P * 0xFFFF))
level += 1;
return (level<ZSKIPLIST_MAXLEVEL) ? level : ZSKIPLIST_MAXLEVEL;
}
每一个节点平均的指针数:=1/(1-p)