Redis数据结构之有序集合对象(zset)

  • Redis对象
  • 有序集合对象
    • ziplist编码
    • skiplist编码

Redis对象

在了解Redis数据结构的时候我们会学习到简单动态字符串,压缩链表等。
但Redis并没有直接使用这些数据结构来实现键值对数据库,而是基于这些数据结构创建了一个对象系统,这个系统包含字符串对象、列表对象、哈希对象、集合对象和有序集合对象这五种类型的对象。Redis使用对象表示键和值,每次新建一个键值对时,我们就创建了两个对象。

有序集合对象

有序集合的编码可以是ziplist或者skiplist。

ziplist编码

ziplist编码的压缩列表对象使用压缩列表作为底层实现,每个集合元素使用两个紧挨在一起的压缩列表节点来保存,第一个节点保存元素的成员(member),而第二个元素则保存元素的分值(score)。

压缩列表内的集合元素按分值从小到大进行排序,分值较小的元素被放置在靠近表头的方向,而分值较大的元素则被放置在靠近表尾的方向。

创建一个ziplist编码的有序集合对象

127.0.0.1:6379> zadd price 1 appel 2 banana 3 cherry
(integer) 3
127.0.0.1:6379> type price
zset
127.0.0.1:6379> object encoding price
"ziplist"

它的存储结构如下

可以看到它是通过ziplist来实现的,ziplist结构适合元素长度较短,元素数量较少的情况。它的优点和缺点都明显,优点是所有数据都在一块内存中,空间利用率高。缺点是查找某个分值的内容时时间仍然为O(n),插入新的数据也要重新移动原来元素的位置。

skiplist编码

当有序集合的元素很多或是元素的长度很大时,ziplist会自定转码为skiplist。
skiplist顾名思义是通过跳跃表来实现的。

这是一个普通有序链表,假如我们要查找19,则依次同3,7,11,19比较,就查找到19.

假如我们将链表的部分节点添加一个指针指向下下个节点,如下图所示。我们查找19就只需要比较7,19即可。


现在好汉元素7的节点有2个指针,这种结构称为2层,同理我们可以继续在节点7上继续添加指针,让它指向跨度更大的节点,变成3层,4层。这样可以有效减少比较的次数。当链表很长时,查找效率的提升就非常可观。

跳跃表的整体结构如下

Redis中跳跃链表通过两个结构实现,zskiplist和zskiplistNode。

zskiplist

zskiplist保存整个跳跃表的基本信息


header:指向跳跃表的表头节点。
tail:指向跳跃表的表尾节点。
level:记录目前跳跃表内,层数最大的那个节点的层数(表头节点的层数不计算在内)。
length:记录跳跃表的长度,也即是,跳跃表目前包含节点的数量(表头节点不计算在内)。

zskiplistNode

zskiplistNode就是我们储存的对象信息,它可以是我们要排序的任何数据,可以是个字符串,也可以是个二进制文件

层(level):节点中用L1、L2、L3等字样标记节点的各个层,L1代表第一层,L2代表第二层,以此类推。每个层都带有两个属性:前进指针和跨度。前进指针用于访问位于表尾方向的其他节点,而跨度则记录了前进指针所指向节点和当前节点的距离。在上面的图片中,连线上带有数字的箭头就代表前进指针,而那个数字就是跨度。当程序从表头向表尾进行遍历时,访问会沿着层的前进指针进行。
后退(backward)指针:节点中用BW字样标记节点的后退指针,它指向位于当前节点的前一个节点。后退指针在程序从表尾向表头遍历时使用。
分值(score):各个节点中的1.0、2.0和3.0是节点所保存的分值。在跳跃表中,节点按各自所保存的分值从小到大排列。
成员对象(obj):各个节点中的o1、o2和o3是节点所保存的成员对象。

zskiplist的表头结构,它是一个固定包含32个指针的结构,包含前进指针和跨度,为什么是32?

每次创建一个新跳跃表节点的时候,程序都根据幂次定律(power law,越大的数出现的概率越小)随机生成一个介于1和32之间的值作为level数组的大小,这个大小就是层的“高度”

我们向一张空的跳跃表新增一个节点o1,节点层数由幂次定律,是1的可能性最大,2次之,依次类推。结果我们运气比较好,生成了一个 层数为4的节点,此时表头的前4层都指向我们新增的节点,它的跨度都为一。

我们再新增个score为2.0的节点o2,它排在1的后面,它的随机层数是2,此时跳跃表的结构如下。o1节点的L1,L2都指向该节点。

当我们加入第三个元素score为3.0的节点o3,它随机到5层。此时假如我们要查找一个score为4.0的节点,Redis先从表头跨度最大的层也就是L5开始找,先找到o3,o3的scroe为3.0,小于4.0,继续向o3中L5的前进指针遍历,结果指向null,说明score为4.0的节点并不存在。


有时候我们对集合的排序并不感兴趣,比如我们要查找o3,通过跳跃表就只能通过遍历所有L1来依次比较,时间复杂度为O(n),所以真正的skiplist编码的zset对象的结构是这样的,zset额外维护存储所有节点数据的字典,这样当我们不关注排序直接获取元素时它的时间复杂度就只有O(1),只需要散列一次即可。字典和跳跃表会共享元素的成员和分值,所以并不会造成任何数据重复,也不会因此而浪费任何内存。

当有序集合对象可以同时满足以下两个条件时,对象使用ziplist编码:
·有序集合保存的元素数量小于128个;
·有序集合保存的所有元素成员的长度都小于64字节;不能满足以上两个条件的有序集合对象将使用skiplist编码。

注意以上两个条件的上限值是可以修改的,具体请看配置文件中关于zset-max-ziplist-entries选项和zset-max-ziplist-value选项的说明。

Redis数据结构之有序集合对象(zset)相关推荐

  1. 有序集合对象 ZSet 的底层原理

    这里写目录标题 ziplist 压缩列表结构 压缩列表结构 压缩列表节点结构 连锁更新 压缩列表在Redis中的用途 skiplist 传统跳表 改进后的跳表 zset中的跳表 redis中如何保证s ...

  2. Redis数据结构之有序集合

    本文来说下Redis数据结构之集合 文章目录 概述 Redis有序集合的部分命令 相关命令 集合内 集合间 内部编码 ziplist(压缩列表) skiplist(跳跃表) 使用场景 本文参考 本文小 ...

  3. 对 Redis 中的有序集合SortedSet的理解

    本篇说一下Redis中的 有序集合类型,曾几何时,我们想把所有数据存到内存中的 数据结构 中,但为了多机器共享内存,不得不将这块内存包装成wcf单独部署,同时还要考虑怎么序列化,烦心事太多太多...后 ...

  4. redis 数据类型之有序集合(sorted set) 详细介绍

    Redis的有序集合(sorted set)同时具有"有序"和"集合"两种性质,这种数据结构中的每个元素都由一个成员和一个与成员相关联的分值组成,其中成员以字符 ...

  5. 「Redis数据结构」哈希对象(Hash)

    「Redis数据结构」哈希对象(Hash) 文章目录 「Redis数据结构」哈希对象(Hash) 一.概述 二.编码 ZipList HashTable 三.编码转换 一.概述 Redis中hash对 ...

  6. redis数据库hset(有序集合)类型常用命令

    redis数据库hset类型常用命令 1 向有序集合添加一个或多个成员,或者更新已存在成员的分数 zadd key score1 member1 [score2 member2] 2 获取有序集合的成 ...

  7. redis——数据结构(整数集合,压缩列表)

    4.整数集合 整数集合(intset)是 Redis 用于保存整数值的集合抽象数据结构, 可以保存 int16_t . int32_t . int64_t 的整数值, 并且保证集合中不会出现重复元素. ...

  8. redis基础之有序集合应用

    '''音乐播放排名''' import redisr = redis.Redis(host='127.0.0.1', port=6379, password='123456', db=0) r.zad ...

  9. 「Redis数据结构」集合对象(Set)

    「Redis数据结构」集合对象(Set) 文章目录 「Redis数据结构」集合对象(Set) 一.概述 二.结构 三.编码转换 四.小结 一.概述 Set是Redis中的单列集合,其特点为不保证有序性 ...

  10. php redis 搜索,PHP+Redis有序集合(zset)实现博客园阅读排行榜功能

    许多网站都有排行榜的功能,比如球员人气榜单.阅读排行榜,对于一些小网站,通过查数据库就能实现排行榜的功能,但是对于稍微有点用户量而且还是实时排名的网站,使用一些关系型数据库如(MySQL.Oracle ...

最新文章

  1. java编写词法分析器
  2. 为什么读写文件要有缓冲区?为什么要有输入输出缓冲区?
  3. 寒冷的高纬度——我的梦开始的地方
  4. Python基础(一)--初识Python
  5. 数据分析常用Python库:数值计算、可视化、机器学习等领域
  6. 图解Java中的18 把锁!
  7. GitHub 版本控制 项目托管 04 创建GitHub远程仓库
  8. SQL中的ROW_NUMBER()和while循环对每一行执行操作
  9. js事件(事件冒泡与事件捕获)
  10. linux rs232驱动程序,Ubuntu安装USB to RS232驱动
  11. 夏普MAX3101N复印故障
  12. Linux 网络基础篇
  13. mysql还原数据库非常慢_mysql还原数据库慢
  14. cache 提高计算机运行速度,使用cache可以提高计算机的运行速度,是什么原因?...
  15. Python学习之路-爬虫(四大名著)
  16. 仿百度糯米页面 html5,Thinkphp5.0 仿百度糯米 开发多商家 电商平台(完整版)
  17. 什么是抽象方法 java_java抽象类和抽象方法
  18. 聚力优创:拼多多的店铺怎么引流?秘诀分享
  19. SQL语句创建、修改视图的方法
  20. MOOC微信小程序开发从入门到实践~笔记

热门文章

  1. 【统计学】相关性和因果关系
  2. java assert 断言_Java 之 assert (断言)
  3. latex 罗马数字方程组大括号
  4. 无需翻墙解决谷歌浏览器-谷歌翻译无法使用的解决方法
  5. 河北化工医药职业技术学院学计算机怎么样,河北化工医药职业技术学院宿舍条件怎么样 男生女生宿舍图片...
  6. RestTemplate使用实战-exchange方法讲解
  7. linux 文件隐藏,Linux系统怎么隐藏文件夹和文件?
  8. Visual Studio 20052008 各个版本下载
  9. 接入Paypal 实现跨境支付 - springboot实战电商项目mall4j
  10. Devexpress TreeList控件支持拼音首字母查询