set就是一个特殊的hash
不知道hash是什么的请看上一章,hash类似java里的hashmap的数据结构。
set就类似java里的hashset。就是一种特殊的字典表。
set在进行add操作的时候进行去重工作,只存key,value都是null。
zset面试官的最爱
zset在set的去重基础上,增加了一项排序功能。
还记得set的value都是null么,zset在value存score(分数),用这个分数来进行排序输出。
我们知道zset既有hash的功能,也有排序的功能。hash简单,还是可以用数组链表结构来实现,那么排序的功能该怎么办呢。必须有序排列吧,但是hash数组是随机的,才是好的hash数组。
那就说明。负责排序部分功能的,需要另一种数据结构出马。那就是跳表(skiplist)。
说明zset里面有2种数据结构,是个复合类型的数据。
跳表的基本结构
别忘了对象头
struct RedisObject {
int4 type;//4bit,对象类型,那最多最多16种对象咯
int4 encoding;//存储形式
int24 lru;//Least Recently Used,最近最少使用信息
int32 refcount;//暂未知,之后补上,todo
void *ptr;//指向字典数据
}
struct zsl{
zslnode* header;
int maxLevel;//跳表最高层数
map<String,zslnode*> ht;//hash结构,存储了链表的节点
}
struct zslnode{
string value;//header里的value值是空的
double score;//header里,score为Double.MIN_VALUE
zslnode*[] forwards;
zslnode* backword;
}
那么数据是存在跳表这个结构呢,还是存在hash这个结构呢?
由zsl的结构可知,是在跳表这个结构里的最后一层存储数据的,因为hash这个结构存储的是跳表节点的值。
如何构造跳表的索引
1、给定一个有序的链表。
2、选择连表中最大和最小的元素,然后从其他元素中按照一定算法(随机)随即选出一些元素,将这些元素组成有序链表。这个新的链表称为一层,原链表称为其下一层。
3、为刚选出的每个元素添加一个指针域,这个指针指向下一层中值同自己相等的元素。Top指针指向该层首元素
4、重复2、3步,直到不再能选择出除最大最小元素以外的元素。
跳表的查询
重点是查找最后一个比自己小的数,然后往下一层的索引去找。
跳表的插入,删除
如果节点已经存在,那么只是更新值。
插入的话,给这个节点计算一个层级。与ZSKIPLIST_P有关,就是不断轮询一个while,如果随机的数小于ZSKIPLIST_P的值,就将level+1,并继续随机,level初始值为1。
然后就将所有涉及到的索引都插入这个新的节点。
删除同理。
结语
跳表理解起来比较容易,但是真让写数据结构,还是得思考一下的。重点是理解,上面那几层,是索引,是新构造的节点了。就跟B+树的索引结构类似。