一、redis原理之sort set底层数据结构?

SortedSet(zset)有序集合可以看做是在Set集合的的基础上为集合中的每个元素维护了一个顺序值: score,它允许集合中的元素可以按照score进行排序,所以它的经典实用场景如:考生按分数排名,某游戏玩家分数排行,网站首页某数据排行,最新评论按时间排序等等。

Redis是一个内存数据库,它在保证读写速度的同时也需要考虑内存开销,那对于SortedSet有序集合而言它需要维护一个顺序值,而对于有序集合的底层实现可以选择:数组,链表,平衡树或者红黑树等结构,但是SortedSet没有选择这些结构。数组插入和删除元素性能很差,链表查询慢,平衡树或红黑树虽然查询效率高,但是在插入和删除元素的时候需要维持树的平衡导致性能下降,而且实现极为复杂。

所以,SortedSet底层而是采用了一种新型的数据结构— 跳跃表。

skiplist跳跃表原理:

跳跃表的性能堪比红黑树,而且实现起来比红黑树简单很多。那么什么是跳跃表?理解跳跃表之间我们先来看一看下面这个链表。

假如我们要查询值为 13的节点,对于上面的单向链表来说,我需要从前往后遍历节点,算一下要进行 10 次查找,性能是非常差的,如何提升查询速度?我们知道即使有序的链表也是没变法进行二分查找的,除非我们把这个链表变成红黑树这样的结构,但是红黑树实现起来太过麻烦。所以,如果我把这个链表像这样处理一下呢?

我把第一层链表中的元素,每隔2个元素就向上提取一个元素,形成第二层的链表,如上图,如果我查找元素的时候先从最上面的层级找 13 ,当找到 18的时候大于13,就退回10,往下一层找,然后就找到13了,你数一下这一次的查找次数几乎是之前的单向链表的一半,大大节省了查询时间。那如果我再往上抽取一层呢?

按照刚才的规律,我们再向上抽取一层,这一次查找的次数是不是又变少了?其实这种数据结构就是“跳跃表”的存储结构了。其实你可以发现他的查询性能是可以媲美红黑树的,但是实现起来比红黑树简单许多。

SortedSet底层实现:

SortedSet底层使用到了Ziplist压缩列表和“跳跃表”两种存储结构,在Redis配置文件中有如下两个配置:

  • zset-max-ziplist-entries 128:zset采用压缩列表时,元素个数最大值。默认值为128。
  • zset-max-ziplist-value 64:zset采用压缩列表时,每个元素的字符串长度最大值。默认值为64。

zset插入第一个元素时,会判断下面两种条件,zset-max-ziplist-entries的值是否等于0;zset-max-ziplist-value小于要插入元素的字符串长度,满足任一条件Redis就会采用跳跃表作为底层实现,否则采用压缩列表作为底层实现方式。

void zaddGenericCommand(client *c, int flags) {...省略...if (zobj == NULL) {if (xx) goto reply_to_client; /* No key + XX option: nothing to do. */if (server.zset_max_ziplist_entries == 0 ||server.zset_max_ziplist_value < sdslen(c->argv[scoreidx+1]->ptr)){zobj = createZsetObject();/ *创建跳跃表*/} else {zobj = createZsetZiplistObject(); / *创建压缩列表 */}dbAdd(c->db,key,zobj);}
}

一般情况下,不会将zset-max-ziplist-entries配置成0,元素的字符串长度也不会太长,所以在创建有序集合时,默认使用压缩列表的底层实现。zset新插入元素时,会判断以下两种条件:zset中元素个数大于zset_max_ziplist_entries;插入元素的字符串长度大于zset_max_ziplist_value。当满足任一条件时,Redis便会将zset的底层实现由压缩列表转为跳跃表 ,见t_zset.c 中的 zsetAdd 函数

if (zzlLength(zobj->ptr) > server.zset_max_ziplist_entries ||sdslen(ele) > server.zset_max_ziplist_value)zsetConvert(zobj,OBJ_ENCODING_SKIPLIST);/* 转跳跃表 */

值得注意的是,zset在转为跳跃表之后,即使元素被逐渐删除,也不会重新转为压缩列表。

skiplist的结构:

跳表主要有:跳表节点,头节点,尾节点,节点数,节点最大层级数组成,如下:

typedef struct zskiplist {struct zskiplistNode *header, *tail;//跳表节点 ,头节点 , 尾节点unsigned long length;//节点数量int level;//目前表内节点的最大层数
} zskiplist;typedef struct zset {dict *dict;zskiplist *zsl;
} zset;

解释:

  1. header: 指向跳跃表头节点,头节点是跳跃表的一个特殊节点,它的level数组元素个数为64。头节点在有序集合中不存储任何member和score值,ele值为NULL,score值为0;也不计入跳跃表的总长度。头节点在初始化时,64个元素的forward都指向NULL,span值都为0。
  2. tail:指向跳跃表尾节点
  3. length:跳跃表长度,表示除头节点之外的节点总数
  4. level:跳跃表的最大的节点的高度。

zskiplistNode 结构

解释:

  1. ele : 用于存储字符串类型的数据
  2. backward:后退指针,只能指向当前节点最底层的前一个节点,头节点和第一个节点——backward指向NULL,从后向前遍历跳跃表时使用。
  3. score:用于存储排序的分值
  4. level:为柔性数组。每个节点的数组长度不一样,在生成跳跃表节点时,随机生成一个1~64的值,值越大出现的概率越低。
  5. forward:指向本层下一个节点,尾节点的forward指向NULL。
  6. span:forward指向的节点与本节点之间的元素个数。span值越大,跳过的节点个数越多

跳跃表的每个节点的ele存储有序集合的成员member值,score存储成员score值。所有节点的分值是按从小到大的方式排序的,当有序集合的成员分值相同时,节点会按member的字典序进行排序。

每个元素插入的时候随机造层:

新增过程:比如上面的图,我要插入一个11进去,每个元素插入的时候随机造层,我先拿11比较最上层,跟3比较,然后跟null比较,发现没有,就往下一层找,然后找到第二层,3比较,跟22比较,然后再找下一层,找到22 和7比较,找到了就把7的指针指向11,把22的指针指向11,

更新过程:先找到更新的地方,将值修改后,比较前后,如果正好大小合适,就修改完成,如果大小不合适就把前面的指针指向后面的指针,然后跟新增一样,找到自己合适的位置

删除过程:将前面的指针指向后面的指针

随机造层:比如上面的11插入进去了,这时候系统会随机判断要不要把11更新到其他的层次上面,如果随机判断不要,则就结束了,如果随机判断是要造层,如上图,11先跟7比较,然后跟3比较,然后到第二层,然后跟22比较,发现找到位置了,就把3的指针指向11,把11的指针指向22

五、redis原理之sort set底层数据结构相关推荐

  1. 一文读懂Redis常见对象类型的底层数据结构

    点击上方 好好学java ,选择 星标 公众号 重磅资讯.干货,第一时间送达 今日推荐:硬刚一周,3W字总结,一年的经验告诉你如何准备校招! 个人原创100W+访问量博客:点击前往,查看更多 转自:伍 ...

  2. Redis源码篇(1)——底层数据结构与对象

    一:底层数据结构 1.SDS struct sdshdr {int len;int free;char buf[]; } 注: 1.1.'\0'结尾遵循C语言规范,可以直接调用C语言字符串的部分api ...

  3. Redis从入门到精通之底层数据结构快表QuickList详解

    文章目录 0.前言 1. 快表的结构 2. Redis 6.0 快表quicklist 基本结构 2.1 成员变量 2.1 主要操作 2.1 推导结果 3. 快表的操作 3. 快表的优缺点 3.1 优 ...

  4. Redis底层数据结构介绍

    文章目录 前言 1. 哈希表 2. 简单动态字符串 使用SDS的好处 对比C字符串 3. 压缩列表 4. 跳表 5. 整数集合.双向链表 Redis数据类型与底层数据结构对照表 前言 一谈到Redis ...

  5. Redis原理篇—数据结构

    Redis原理篇-数据结构 笔记整理自 b站_黑马程序员Redis入门到实战教程 底层数据结构 动态字符串SDS 我们都知道 Redis 中保存的 Key 是字符串,value 往往是字符串或者字符串 ...

  6. 一文带你深入理解Redis中的底层数据结构,再也不怕不懂数据类型的底层了

    数据结构前言 都说Redis快,因为什么呢?只是因为它是内存数据库,所有操作都是基于内存进行的吗?其实不然,这与它的数据结构也是密不可分的.下面我们就来了解一下Redis的数据结构. Redis 数据 ...

  7. 通过对象属性去重_Redis常见对象类型的底层数据结构

    作者:伍陆七 来源:cnblogs.com/chentianming/p/13838347.html Redis 是一个基于内存中的数据结构存储系统,可以用作数据库.缓存和消息中间件.Redis 支持 ...

  8. MySQL 索引底层数据结构实现

    文章目录 概述 讨论范围 查询数据结构 查询数据结构种类及其高性能查询原理 MySQL 索引的底层数据结构 MySQL 索引的需求分析 选择 MySQL 索引的底层数据结构 B- 树和 B+ 树的对比 ...

  9. 《别看了,你学不会的》——Redis原理与实战(一)

    Redis 安装homebrew 我使用的是macOS操作系统,所以在安装Redis之前还需要先安装homebrew 安装命令,在终端中输入 /bin/bash -c "$(curl -fs ...

最新文章

  1. Java 进行 RSA 加解密时不得不考虑到的那些事儿
  2. 更改android模拟器sdcard文件夹的权限
  3. SOFA 源码分析 — 连接管理器
  4. multiprocessing python_Python教程:进程和线程amp;多进程
  5. GoJs Pictures 官方介绍文档
  6. 基于 WPF 模块化架构下的本地化设计实践
  7. linux 创建ll,两台linux建立GRE隧道
  8. python如何进行格式化输出变量_Python变量格式化输出实现原理解析
  9. 51CTO寄来的奖品
  10. Node.js + React + MongoDB 实现 TodoList 单页应用
  11. 通达OA2015版与金蝶K3系统集成方案
  12. c语言头文件sys wait.h,错误:sys/wait.h:没有这样的文件或目录
  13. spring自定义yml文件解析器
  14. 初入职场着装宝典(BOY)
  15. java.net.UnknownHostException: localhsot
  16. 艾永亮:企业微信,私域流量“后时代”的开启者
  17. FPGA设计开发(基础课题):分频器设计
  18. eclipse导入import git项目
  19. 简单的出身年月实现 2018.9.29
  20. P2713(罗马游戏 可并堆模板)

热门文章

  1. maven详细配置教程
  2. 编辑视频贴纸软件_DIY贴纸制作软件下载-DIY贴纸制作app(视频教程) v1.0.2手机版_5577安卓网...
  3. 爱加密:解析APP开发团队生存难题
  4. java 锁定excel单元格格式化,java – Apache POI – Excel写入 – 锁定单个单元格
  5. 2022年WordPress主题最优秀热门主题排名TOP30
  6. MCDF实验4(4)
  7. [附源码]Python计算机毕业设计Django基于Java的员工管理系统
  8. 32位寄存器、64位寄存器
  9. 名片上最常用的中英文称呼
  10. html jq固定div高度,js/jQuery获取/设置div的高度,宽度···