一个lua版的zset数据结构实现
来源:互联网 发布:沥青库存数据 编辑:程序博客网 时间:2024/05/19 17:58
redis里的zset是一种有序集合,从逻辑上,可以理解为在集合的基础上,为每一个成员增加了分数字段,分数是一种浮点数值,并可以相同,它们按序排列起来。它能对分数在增,删,查找上都能提供对数时间复杂度的操作。
redis里的zset是利用一个skiplist和一个dict实现的,其中关键数据结构就是skiplist,跳跃表。
skiplist的原理和基本实现网上有很多,不再啰嗦。基于zset的需求,会有稍微的改进。
1,结构定义
typedef struct skiplistnode { double score; int member; /* (1) */ struct skiplistnode *backward; /* (2) */ struct skiplistforward { struct skiplistnode *ptr; unsigned long span; /* (3) */ } forward[];} skiplistnode;typedef struct skiplist { int level; unsigned long length; struct skiplistnode *header, *tail;} skiplist;
基本参考了redis的定义,其中member为什么是int(1),是用来放luaL_ref的ref值,所有放在容器里的数据都会先引用,然后记在这个C结构里,所以它可以记任何lua类型:函数,对象等。那个backward(2),是用反向遍历结点,比如zrevrange*这类操作。那个span的含义是(3),当前那一层的下一跳ptr距离本结构的距离,这是为了实现rank计算排名等相关操作的,为什么要这样定义,这样可以在只有局部结点变化时,需要修改的span值比较少,而且也比较容易实现各种计数操作。
2,为什么skiplist有平均很好的性能
具体有严格的证明,我只写下我自己的理解。本质上它就是一个list,只不过每个结点有一个层级level,任何结构的层级都是随机生成,像这样:
intsl_randomlevel() { int level = 1; while (random() * 2 < RAND_MAX) level++; return level > MAX_SKIP_LEVEL ? MAX_SKIP_LEVEL : level;}
那个随机函数的比较意思是,概率为p(比如1/2)的随机事件是否发生,如果发生level++,那么如果p=1/2,那level的取值和机率像这样:
- level = 1 ,
p=12 - level = 2 ,
p=122 - …
- level = n ,
p=12n
层级越高的结点成指数减少,一个level=n的结点,会有1至n的forward指针,指向下一个较大分数的结点。这样查找结点时,从最高层级的header开始,往右查找,当右结点的分值或者排名不符合条件时,层级减少,这样一直循环,直到0层的右结点不符合条件,查找就完成了,每一层往下跳的结点就是这次查找的关键结点,每层一个,它表示当前查找的值在每一层上所处的位置,查找,修改和删除都基于这个基本过程。如果最高层级是n,那从平均概率上会有O(2^n)个结点,本层的查找平均也会减少一半结点数。
3,字典
实现zset还需要一个dict,用来保存当前member的分值等。这样能过member修改分数的过程就是:从dict查找当前分数,通过score,member删除当前结点,添加新的score,member。
4,枚举器
zset还实现了一系列枚举器:zset:forward(), zset:backward(), zset:range()等,这样有个小问题,在枚举遍历的过程当中如果删除当前结点,要保证不能破坏当前枚举,不能crash,也不能漏元素。这里用了个小技巧,每次当前结点返回前,把下一个结点计算好,下次进来时直接先赋值下一个结点,这样当前结点,作为list进行了删除是不会影响下一个结点的。
开源代码:(待上传)
TODO
redis的zset对于相同分数的结点之间的顺序是基于member的字典序,因为member是redis字符串,所以这样实现比较巧妙,在lua环境下member是任何数据类型,因此没有这一系列操作。除此之外,还有几个批量删除的操作也未实现,zremrange*,单个结点的删除是O(logn),如果删除m个结点,用原有删除是O(mlogn),而这几个批量删除仍然保持O(logn+m)。实现思路很明显,类似查询过程,以后完善代码。
- 一个lua版的zset数据结构实现
- 用python实现一个redis的zset数据结构
- 基于redis的zset实现排行榜功能
- Redis中zset的golang实现
- lua数据结构之table的内部实现
- lua数据结构之TString的内部实现
- SSDB --- zset实现
- jedis操作zset数据结构总结
- Redis数据结构详解之Zset
- Redis数据结构详解之Zset
- Redis数据结构详解之Zset
- redis的数据类型-zset
- 一个简易版本的lua debugger实现
- 一个纯C#的Lua 5.2实现
- zset
- zset
- lua的数据结构
- Lua的数据结构
- Numpy(1)
- 给定一个数n,输出n的环形矩阵
- ExtJs6第二弹-- 学会查看ExtJs api文档
- cocos2d-x经验分享-spine边界框(碰撞框)
- Visual Studio中监视数组
- 一个lua版的zset数据结构实现
- c/c++内存管理
- 阻塞和非阻塞的区别
- SpringjdbcTempete的配置文件
- python socket基本过程之二(server)
- mybatis字段取别名
- JAVA中集合解析篇二 HashMap解析
- yii2授权之ACF
- cocos2d-x经验分享2-取按键按下