redis源码之zset结构的实现
zset为有序的,自动去重的集合数据类型,zset数据结构底层实现为字典(dict)+跳表(skiplist)当数据比较少时,用ziplist编码数据结构存储,当满足以下条件之一时,则采用字典+跳表来存储
zset-max-ziplist-entries 128 //元素个数超过128,将用skiplist编码 zset-max-ziplist-value 64 //单个元素大小超过64byte,将用skiplist编码
typedef struct zset {
dict *dict;
zskiplist *zsl;
} zset;
对于字典(dict)的数据结构来说,可以用O(1)的复杂度拿到对应元素 比如zscore命令 zscore key value 就可以拿到以key为键,value的对应的分值,这个就是在字典(dict)的数据结构中取的,字典(dict)数据结构主要用于判断值是否存在以及拿对应的分值,这个不是我们文章阐述的重点,重点看一下跳跃表(skiplist)的数据结构,看看它是如何实现排序的
skiplist的实现
首先我们来看一看链表的数据结构示意图
链表的话查找我们所需的元素的时间复杂度为O(n),显然这是我们不能接受的,所以需要对链表进行一步改造
我们每隔两个元素给加一层,然后我们查询从索引层开始查询,遇到了比目标元素大的元素再返回,前往数据层来查询,这样速度会快一些,但是这样速度快的不明显,大概也就快了一半左右,于是我们便想到了加高层数
其实按照上图我们可以计算一下 元素的总个数为N 那么在上图的所以第一层,元素的个数为n/2 index: 1 n/2
那么在上图的所以第二层,元素的个数为n/2^2 index: 2 n/2^2
那么在上图的所以第三层,元素的个数为n/2^3 index: 3 n/2^3
那么在上图的所以第K层,元素的个数为n/2^k index: k n/2^k
比如顶层为2个节点 2 = n/2^k 也就是说 2^k = n/2 ----> k=log2(n-1) 加上数据层的话 k = log2 n 所以我们的层高是 log2 n 查找的时候的时间复杂度是log n
我们可以来看一下skiplist的源码
// zskiplistNode包含了数据和索引,也就是跳表中的一列
typedef struct zskiplistNode {
sds ele; //元素
double score; //分数
struct zskiplistNode *backward; //往前指的指针
struct zskiplistLevel {
struct zskiplistNode *forward;//往后指的指针
unsigned long span; // 从当前节点到下一个节点的跨度
} level[];
} zskiplistNode;
typedef struct zskiplist {
struct zskiplistNode *header, *tail; //header和tail是方便双向遍历
unsigned long length; //当前数据包含元素个数
int level; // 层高
} zskiplist;
skiplist.png
如何确定索引层的层高
索引层的层高是由一个随机函数,幂次定律实现的
int zslRandomLevel(void) { //幂次定律,随机生成层高,越高的层出现概率越低
int level = 1;
while ((random()&0xFFFF) < (ZSKIPLIST_P * 0xFFFF))
level += 1;
return (level<ZSKIPLIST_MAXLEVEL) ? level : ZSKIPLIST_MAXLEVEL;
}
- 【专知-PyTorch手把手深度学习教程02】CNN快速理解与PyTorch实现: 图文+代码
- [接口测试 - 基础篇] 03 unittest测试框架了解多少才够?
- 关关的刷题日记09——Leetcode 80. Remove Duplicates from Sorted Array II
- [linux][kernel]dump kmsg到文件系统中
- [接口测试 - 基础篇] 12 还是要掌握python日志管理模块的
- Spring整合Quartz分布式调度
- [接口测试 - 基础篇] 11 掌握下python解析YAML格式也是需要的
- 如何用Python创建眼下火爆的区块链?这篇干货请收藏
- [接口测试 - 基础篇] 10 别啊,还有INI格式呢,别忘记我
- [接口测试 - 基础篇] 09 其实吧,读写csv格式也是要掌握的
- 【源码分享】机器学习之Python支持向量机
- [接口测试 - 基础篇] 08 封装个基本的excel解析类
- 关关的刷题日记10——Leetcode 1. Two Sum 方法1
- BZOJ 2463: [中山市选2009]谁能赢呢?(新生必做的水题)
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Qt音视频开发29-Onvif云台控制
- 十大高性能开发
- 【云+社区年度征文】java agent及字节码技术得到DNS时间流程尝试
- 短视频商城源码,制作彩色验证码
- ubuntu下的mysql安装与使用
- 排序算法
- 并发编程框架Disruptor之高性能设计
- 3分钟短文:Laravel说要用软删除,可不要真删
- leetcode队列之设计循环双端队列
- MySQL8.0的binlog详解
- Flutter NestedScrollView实现的一个经典滑动折叠头部图片的效果
- Maven 编译拷贝资源的时候提示 UTF-8 编码信息
- Qt音视频开发30-Onvif事件订阅
- 干掉 Navicat:正版,MySQL 官方客户端真香!
- CentOS 搭建 K8S 环境教程,一次性成功,收藏了!