从跳表到Redis有序集合

一、Spark源码系列

Spark源码阅读系列停更了好久,因为一直没能达到想要的效果。写了一篇Spark物理计划生成,读下来味同嚼蜡,暂时不打算放出来了。最近偶然遇到《如何阅读一份源代码》,文中说“写代码是表达自己,读代码是在理解别人”,我想既然没有理解别人,为什么要写一篇水文欺骗自己呢?

二、跳表

最近开始看Kafka的书,书里面提到kafka的文件offset查找用到了“跳表”这种数据结构。跳表是一种随机化数据结构,查找、添加、删除操作都可以在对数期望时间下完成;当你设计一种存储结构,嫌数组O(n)的插入慢,又嫌链表的O(n)查找慢,可以尝试下跳表,既可以O(log n)的插入,又可以O(log n)的查找,唯一的代价是多出小于一倍的链表存储空间。

首先看一个Redis有序集合(Sorted Set)的例子, 向有序集合sc中插入三个元素x,y,z,排序评分为6, 10, 15;

localhost:~ renq$ redis-cli
127.0.0.1:6379> zadd sc 6 x 10 y 15 z
(integer) 3
127.0.0.1:6379> zrange sc 0 -1
1) "x"
2) "y"
3) "z"
127.0.0.1:6379> zrange sc 0 -1 WITHSCORES
1) "x"
2) "6"
3) "y"
4) "10"
5) "z"
6) "15"
127.0.0.1:6379>

一般地,跳表的结构如下图;每一层的节点随机向上形成索引,直到最上层只有一个索引节点。查找时从最上层索引开始,索引的存在加速了查找过程。

从跳表到Redis有序集合_第1张图片

Redis内部采用了改进的跳表结构存储上述信息,简化成下图所示;

具体Redis的实现,有序集合叫做zset, 由一个字典dict和一个跳表zsl组成。

typedef struct zset {
    // 字典
    dict *dict;
    // 跳表
    zskiplist *zsl;

} zset;

字典用来在O(1)的时间检查集合中指定元素是否存在,在O(1)时间内根据键取出给定的值;跳表用来在O(log n)的时间内查找指定键值对,进行范围查找。

Redis完整的zset结构如下所示。

三、典型应用:分类排行榜

做一个当天NBA球员得分表score, 使用zadd插入球员的得分,注意先插入value(得分),后插入key(球员名)。使用zrange获取得分排行榜;

127.0.0.1:6379> zadd score 54 harden
(integer) 1
127.0.0.1:6379> zadd score 38 curry
(integer) 1
127.0.0.1:6379> zadd score 20 klay
(integer) 1
127.0.0.1:6379> zadd score 0 lowery
(integer) 1
127.0.0.1:6379> zcard score
(integer) 4
127.0.0.1:6379> zrange score 0 -1
1) "lowery"
2) "klay"
3) "curry"
4) "harden"
127.0.0.1:6379> zrange score 0 -1 WITHSCORES
1) "lowery"
2) "0"
3) "klay"
4) "20"
5) "curry"
6) "38"
7) "harden"
8) "54"

注意上面查询得到的是分数从低到高。想要倒序求分数最高的前几个,只需先-n, 再-1即可。例如查询得分前3和前5:

127.0.0.1:6379> zrange score -3 -1
1) "klay"
2) "curry"
3) "harden"
127.0.0.1:6379> zrange score -5 -1
1) "lowery"
2) "klay"
3) "curry"
4) "harden"

选择第2、第3高分:

127.0.0.1:6379> zrange score -3 -2
1) "klay"
2) "curry"

统计得分在某个区间的球员数量:

127.0.0.1:6379> zcount score 0 30
(integer) 2
127.0.0.1:6379> zcount score 0 60
(integer) 4
127.0.0.1:6379> zcount score 0 10
(integer) 1

四、参考资料

  1. 如何阅读一份源代码
  2. 跳表论文
  3. Redis设计与实现 有序集
  4. 漫画科普跳表

你可能感兴趣的:(数据结构,Redis)