Redis远程字典服务器(8)—— zset类型详解

news2024/11/12 14:12:51

目录

一,基本情况

二,常用命令

2.1 zadd

2.2 zcard,zcount

2.3 zrange,zrevrange,zrangebyscore

2.4 zpopmax,bzpopmax

2.5 zpopmin,bzpopmin

2.6 zrank,zrevrank,zscore

2.7 zrem,zremrangebyrank,zremrangebyscore

2.8 zincrby

2.9 zinterstore,zunionstore

三,内部编码

四,应用场景

4.1 排行榜系统


一,基本情况

Zset,叫做“有序集合”

  • Set(集合):唯一性,无序性(孙行者,行者孙,者行孙 ==> 同一只猴 )
  • List(列表):有序性,可重复(孙行者,行者孙,者行孙 ==> 不同的猴)
  • Zset(有序集合):这里的“有序”,单指 “升序/降序 ”

问题:那么排序的顺序是啥?

解答:我们给 zset 中的 member 同时引入了一个属性 => 分数(score),浮点类型,进行排序的时候,就是按照此处的分数大小来进行 “ 升序/降序 ”的,如下图:

 注意: “升序/降序”只是为了解释“有序”这个词的广泛性,实际上zset内部还是按照“升序”来排列的

zset也是一个集合,所以member仍然要求是唯一的,score可以重复,因为 zset主要还是用来存member的,score只是辅助 

数据结构

是否允许重复元素是否有序有序依据应用场景
list(列表)索引下标时间轴,消息队列等
set(集合)标签,社交等
zset(有序集合)分数排行榜系统,社交等

二,常用命令

2.1 zadd

ZADD key [NX | XX] [GT | LT] [CH] [INCR] score member [score member
  ...]

zadd作用是往集合中添加或修改元素和分数,首先指定key,方括号为可选选项,score为添加的分数,member为添加的值,其中 member 和 score 称为是一个“pair”类型,类似于C++里的std:pair,类似于键值对,但又不是键值对,因为对于有序集合来说,是既可以通过member找到score,也可以通过score找到member的。

C++的pair使用可以参考:C++——map和set的基本使用_c++中关联式容器-CSDN博客

然后是对于方括号里面选项的解释:

  • NX:当member不存在的时候,添加新member,如果member存在什么都不做
  • XX:当member存在的时候,更新member的值,如果member不存在什么都不做
  • 不加 NX|XX 时:如果当前member不存在,此时就会达到“添加新member”的效果,如果当前member已经存在,就会更新分数
  • LT:在要更新分数的前提下,如果新分数比旧分数,就更新成功,否则不更新,不会阻止添加新member
  • GT:在要更新分数的前提下,如果新分数比旧分数,就更新成功,否则不更新,不会阻止添加新member
  • CH:“ C ” 是“ change ”的缩写,作用是指定返回值要返回什么信息,本来zadd返回的是新增元素的个数,添加CH选项后返回被修改的元素个数
  • INCR:作用是能针对现有的member的score进行运算

问题:之前的Hash,Set,List 很多时候,添加一个元素的时间复杂度都是O(1),但为什么Zset是O(logN)?

解答:因为zset是有序结构,所以新增元素时需要计算合适的位置再添加,当然之所以是logN不是N,也是利用了有序这样的特定。

但是也有分数相同的情况,所以当分数相同时,再按照元素自身的字典序来排列,分数不同仍然按照分数来排

下面是zadd的使用:

 但是上面的只能显示member,不显示score,所以我们可以在后面加上“ withscores ”选项使其显示分数:

我们也可以使用zadd修改对应member的分数:

 如果修改的分数影响到了之前的顺序,就会自动移动元素的位置,从而保持原有的升序顺序不变:

最后我们使用以下zadd的选项,NX|XX 作用和之前一样,这里就不介绍了:

注意:

  • LT和GT选项要在高版本的Redis才会有,但是使用起来也非常简单
  • C++中的std:set也可以存pair,同时也是有序的,但是和Redis的set最大的不同是,std:set不能修改,只能添加或者删除

2.2 zcard,zcount

ZCARD key
ZCOUNT key min max
  • zcard作用是获取zset里元素的个数
  • zcount作用是找出score符合区间[min, max]的member的个数,以返回值显示,[min, max]是闭区间:

上面的是闭区间,如果我们想排除边界值,需要用括号,但是这里的写法有点奇怪,如下图:

问题:一般来说,一个好的设计,是符合直觉的,因为这样学习和使用成本就越低;但是像这个添加括号就明显不符合直觉,那么为什么不改呢?

解答:既然已经这样设定了,只能将错就错,遵守这样的规则了,因为需要考虑兼容性,只要新的Redis一改,大量的代码直接就不能用了,需要大量的修改,成本是非常非常高的,会导致大量的服务器直接罢工

  • C++最近火起来的原因,就是因为C++兼容C,而当代大多数的热门操作系统都是用C写的
  • 而且其实为了解决IPv4地址不足的问题,我国早就开始高IPv6了,而且几近成熟,但是仍然没有大量推广,就是因为这玩意儿是和设备绑定的,要换IPv6,就需要摒弃IPv4的设备,引入IPv6的设备,这其实是一件非常耗时烧钱耗精力的事情,需要长期慢慢推进

zcount的时间复杂度是 O(logN)

问题:假设是先根据min和max找到对应的元素,如果要进行一个遍历,是不是就知道这里的元素个数了呢? 

解答:那样的话时间复杂度就是O(N)了,就不是O(logN)了,根据找min和max就是O(logN)了,再加上遍历就是O(logN + M)了,M是区间中元素的个数,N是整个有序集合的个数

实际上Zset内部,会记录每个元素当前的 “排行” / “次序”,查询到元素,就直接知道了元素所在的“次序”,就可以直接把max对应的元素次序和min对应的元素次序做减法即可。 

注意:zcount的min和max是支持分数的,所以可以写成浮点数,而且其中还有两个特殊值:inf表示无穷大,-inf表示负无穷大

负无穷大不是无穷小,无穷小应该指的是无限接近于0的数

2.3 zrange,zrevrange,zrangebyscore

  • zrange作用是查看指定的一对下标构成的区间的值,前面已经用过很多次了
  • zrevrange,z后面的三个字母“ rev ”其实是“ reverse ”的缩写,意思为“ 逆序 ”,所以zrevrange作用是按照分数降序进行遍历
  • 前面两个都是按照下标区间来找元素的,zrangebyscore作用是按照分数来找元素的,和zcount类似:

 注意:zrangebyscor已经标记成“废弃”,可能在Redis 6.2.0 之后废弃,并且功能会合并到zrange中

2.4 zpopmax,bzpopmax

zpopmax key [count]
BZPOPMAX key [key ...] timeout

  • zpopmax作用是删除并返回分数最高的count个元素,就是 topK 问题,如果存在多个分数相同的元素,同时为最大值,zpopmax仍然只删除其中一个 
  • 咱们这里的“有序集合”也可以视为一个“优先级队列”,有的时候,也需要一个带有“阻塞功能”的优先级队列,就可以用bzpopmax实现阻塞功能,timeout表示超时时间,其他的具体使用和细节再list的blpop和brpop已经讲解过,这里不再赘述:Redis远程字典服务器(6) —— list类型详解-CSDN博客

引出:zpopmax的时间复杂度是O(logN + M),其中N是有序集合的元素个数,M表示coun他,要删除的元素个数;此处zpopmax删除最大的元素,由于集合是有序的,最大值相当于最后一个元素,也就相当于“尾删”

问题:既然是尾删,那为什么不把这最后一个元素的位置特殊记录下来,省去了查找的过程,后续删除不就可以O(1)了吗?

解答:可以做到,但是Redis没有这么做,而且实际上,在Redis源码中,针对有序集合,确实是记录了“尾部”这样的特定位置,但是实际删除的时候,是直接调用了一个“通用的删除函数”,给定一个member的值,进行查找,找到位置在删除

所以是存在优化空间的,但是优化一般是要先找到性能瓶颈的,再针对性的优化

bzpopmax的效果和blpop一样,这里就不展示了:Redis远程字典服务器(6) —— list类型详解-CSDN博客 

2.5 zpopmin,bzpopmin

ZPOPMIN key [count]
BZPOPMIN key [key ...] timeout

 zpopmin就是和zpopmax反过来的,删除最小的count个元素:

此处zpopming和zpopmax的逻辑是一致的,bzpopmin也是一样的,删除的时候是使用的通用的删除函数,所以有了查找的过程,时间复杂度是O(logN + M) ,M是要删除的个数,所以可以不记,最后的时间复杂度可以为O(logN)

2.6 zrank,zrevrank,zscore

  • zrank作用是获取指定元素的排名,这个“排名”就是“下标”
  • zrevrank作用也是获取member的下标,但是是反着算的
  • zscore作用是查询指定member的分数

zrank: 

 zrank得到的下标是从前往后(升序)算的。

zrevrank

 zscore

问题:zscore明明也有“根据member找score”的查询操作,为什么它的时间复杂度是O(1)?

解答:此处可以认为是Redis对查询操作做了特殊优化,付出了额外的空间代价,针对这里进行优化到了O(1)的,因为Redis的设计者发现这个查询操作是“高频”的,所以做了针对性的优化

2.7 zrem,zremrangebyrank,zremrangebyscore

  • zrem作用是删除指定的member,时间复杂度尾O(logN * M),N是整个zset的元素个数,M是指定的member的个数
  • zremrangebyrank作用是范围式删除,通过在命令后面带上一个范围,对这个范围进行删除,是闭区间
  • zremrangebyscore作用也是范围式删除,上面的是指定下标,这个就是指定score的范围删除

这三个命令结合解释能很快理解和使用,就不进行演示了~

后面两个命令的时间复杂度都是O(logN + M)  

2.8 zincrby

zincrby key increment member

作用是对指定member的score进行一个加法的操作,可以通过加负数来实现减法的效果 

这个命令的效果和zadd的INCR选项效果是一样的 

2.9 zinterstore,zunionstore

在学习set类型的时候,我们直到集合操作还有交集,并集,差集,对应的命令是sinter,zunion,zdiff,那么zset有序集合有没有呢?有是有,因为这几个命令是从Redis 6.2 版本才开始支持的,咱们现在是5版本,暂时不涉及

暂时zset还是提供了两个命令求集合间操作的:

zinterstore destination numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX]
zunionstore destination numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX]
  • zinterstore作用是求交集,结构保存到另一个key中
  • zunionstore作用是求并集,结构保存到另一个key中

下面解释下选项:

  • destination:最后的结果存储到哪个key对应的zset中
  • numkeys:就是一个整数,描述了后续有几个key参与集合间运算,加入这个的原因是因为后面还有其他的选项,Redis解析命令的时候,需要知道有多少个key参与运算,避免选项和key混淆(此处的设定很像HTTP协议报头的“Content-Length”,描述了正文的长度,避免了“粘包问题”)
  •  weights:权重,咱们这个集合是有序集合,带有分数,所以现在好几个有序集合做操作,它们的地位不一定对等;权重相当于一个系数,会乘以当前的分数,具体使用可以观看后面的截图
  • aggregate:zset求交集时,不仅仅只考虑member,它还有score,如果member相同,score不同,所以需要一个选项来处理score,就三种方式:“SUM求和”,“MIN取小的值”,“MAX取大的值”,通过这三种方式对score合并

zinterstore: 

 然后我们可以带上权重,可以写成小数:

 最后的AGGREGATE的对score三种操作都很简单,这里就不演示了~

 zunionstore:求并集和求交集步骤一样:

对于zinterstore的时间复杂度

  1. N表示输入若干个有序集合里面元素最少的个数
  2. K表示有序集合的个数
  3. M表示最终结果的有序集合元素的个数

这个东西取决于Redis的源码咋写的,我们暂时不考虑~

表达式太复杂了,我们可以简化以下得到近似值:

  • 首先K不会很多,近似看作1
  • 再化简以下,可以认为N和M是接近的(不严谨,只是近似来看),
  • 可以化简为O(M) + O(M * logM) ==> O(M * logM)

 对于zunionstore的时间复杂度

 除了N变为了:参与运算的zset的个数,其他的和上面一致

三,内部编码

  • ziplist(压缩列表):当有序集合的元素个数小于 zset-max-ziplist-entries 配置(默认 128 个), 同时每个元素的值都小于 zset-max-ziplist-value 配置(默认 64 字节)时,Redis 会用ziplist 来作 为有序集合的内部实现,ziplist 可以有效减少内存的使用。
  • skiplist(跳表):当 ziplist 条件不满足时,有序集合会使用 skiplist 作为内部实现,因为此时 ziplist 的操作效率会下降。

关于跳表skiplist的结构可以参考下这道题目:LCR 154. 复杂链表的复制 - 力扣(LeetCode) 

要详细了解跳表,还是得结合源码来看,这里不做过多讨论 

四,应用场景

4.1 排行榜系统

最关键的应用场景就是这个了,比如应用热搜,游戏积分,成绩排行等等。

关键要点就是:用来排行的“分数”是频繁变化的,因此我们就需要在实时变化的环境下高效地更新排行,所以使用zset来实现就非常简单,因为可以高效地修改“分数”,排行顺序也能自动调整(logN)

Thousands(千):1KB

million(百万):1MB

billion(十亿):1GB 

对于游戏排行榜之类的,它的前后顺序就非常容易确定,但是有的排行榜就要复杂很多,比如某博热度(浏览量,点赞量,转发量,评论数),但是对于这部分,一般的大公司都有专门的团队来做这块的事情(通过一些人工智能的方式来进行计算),根据每个维度。计算得到综合得分 ==> 热度

总结:zset只是一个选择,不是说非得用zset,有些场景确实可以用到有序集合,但是不方便使用Redis,就可以考虑使用其他方式的有序集合 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2067885.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

跨界合作:联想拯救者Y9000P《黑神话:悟空》联名版震撼发布

在科技与文化的交汇点,联想拯救者与备受瞩目的国产单机游戏《黑神话:悟空》携手,共同推出了令人瞩目的Y9000P联名定制版笔记本。 这款笔记本不仅承载着联想拯救者对于极致性能的追求,更融入了《黑神话:悟空》深厚的文…

CAN总线/CAN应用层协议设计,理解并实践仲裁段位域定义

文章目录 概述位序和字节序驱动层CAN帧数据结构待发送数据的结构待接收数据的结构仲裁段使用U32的低位? 位段结构和寄存器位的映射数据段的发送顺序CAN协议定义协议文档中的定义仲裁段定义数据段定义跨平台CAN通信 概述 我们已然明确地知道,CAN仲裁段的…

全自动监控主机掉线或录像回放异常

支持浏览器的cookie或者密码进行登录监控主机。 已实现的功能: 1、后台常驻运行 2、支持多主机监测,回放丢失、线路中断 3、支持异常报警通知到企业微信 监控主机已经自带监控异常邮箱报警通知的功能了,为什么还要额外做呢? 1、…

数学生物学-4-混乱系统(Chaotic Systems)

混沌系统(Chaotic Systems)是一种复杂且非线性的动态系统,其基本概念和特征有: 对初始条件的敏感依赖性: 混沌系统的核心特征之一是对初始条件的极端敏感性,这一特性通常被称为“蝴蝶效应”。这意味着即使是…

NVIDIA超分辨率开启详细教程

NVIDIA超分辨率介绍 参考链接:NVIDIA RTX 视频超分辨率NVIDIA 目前推出的 RTX 视频超分辨率 (VSR) 更新可实现更出色的整体图形保真度,同时保留精美细节,提供对原生视频的上采样技术,并且还支持 GeForce RTX 20 系列 GPU。 RTX V…

unity Standard Assests资源商店无法安装解决方案

Unity游戏开发 “好读书,不求甚解;每有会意,便欣然忘食。” 本文目录: Unity游戏开发 Unity游戏开发unity中国 嗨嗨嗨,我来啦。 这几天的川渝之旅已经圆满结束了,今天开始正常给大家更新: Unity…

素数筛选(暴力排除)

前言&#xff1a;写这一题的时候没看到本质&#xff0c;只拿了一半的分&#xff0c;其实这一题就是找最小匹配的素数 而且我还忘记去重导致一半的样例没过 题目地址 法一&#xff1a;直接先去重&#xff0c;后对每一个数作为因子&#xff0c;开一个桶记录 #include<iostre…

前端css溢出属性overflow

overflow: visible;默认是显示溢出 overflow:hidden;溢出隐藏 overflow: scroll; 出现滚动条&#xff0c;就算内容不超过盒子都显示滚动条 overflow: auto;内容多了自动出现滚动条 overflow: inherit;继承父元素效果 <!DOCTYPE html> <html lang"en"&…

推荐 3 款最好用的流程图工具,简单易用,总有一款适合你

Dia Dia是一款开源的流程图绘制软件&#xff0c;是GNU计划的一部分&#xff0c;由Alexander Larsson创立。该软件使用单文档界面&#xff08;CSDI&#xff09;模式&#xff0c;类似于GIMP&#xff0c;并且设计上将多种需求以模块化的方式进行处理&#xff0c;如流程图、网络图、…

【JVM】JVM内存模型与操作系统内存模型(二)

JVM内存模型与操作系统内存模型 本地方法栈 与虚拟机栈发挥的作用是相似的&#xff0c;他们之间的区别不过是虚拟机栈为虚拟机执行Java方法(也就是字节码)服务&#xff0c;而本地方法栈则为虚拟机使用到的Native方法服务。在虚拟机规范中对本地方法栈中使用、使用方式与数据结…

7.添加购物车以及完善导航条跳转

添加购物车 一、引言二、前端2.1引入轻组件2.2为图片添加点击事件2.3创建addShoppingCart函数 三、后端四、运行效果五、导航条跳转 一、引言 在前几篇文章中&#xff0c;我们一步一步慢慢的实现了项目的建立&#xff0c;从数据库获取数据显示在页面&#xff0c;商品大图查看&…

08 内置函数

目录 日期函数字符串函数数学函数其他函数 1. 日期函数 函数名称描述current_date&#xff08;&#xff09;当前日期current_time&#xff08;&#xff09;当前时间current_timestamp&#xff08;&#xff09;当前时间戳date(datetime)返回datetime参数的日期部分date_add(da…

Java 继承Thread 和 实现Runnable的关联

在java中创建线程的最常用的两种方法 1.继承Thread类 2.继承Runnable接口 但是实现了Runnable接口之后&#xff0c;这个类不能自己启动&#xff0c;需要将其传递给一个Thread实例对象&#xff0c;然后通过Thread对象的start() 方法进行启动&#xff0c;因为只有Thread类中的 …

【系统架构设计】软件架构设计(1)

【系统架构设计】软件架构设计&#xff08;1&#xff09; 软件架构概述架构需求与软件质量属性软件架构风格数据流风格批处理序列管道-过滤器2者风格比较 仓库风格--黑板系统 层次系统架构风格二层及三层C/S架构风格MVCMVP 面向服务的架构 软件架构概述 基于架构的软件开发模型…

无心剑中译莎士比亚《吾爱稀罕胜天仙》

莎士比亚十四行诗第130首 Sonnet 130 吾爱稀罕胜天仙 My mistress’ eyes are nothing like the sun; Coral is far more red than her lips’ red; If snow be white, why then her breasts are dun; If hairs be wires, black wires grow on her head. I have seen roses d…

C++相关概念和易错语法(29)(lambda、function、bind)

1.lambda lamba表达式本质是匿名函数 书写格式&#xff1a;[ 捕捉列表 ] ( 参数 ) mutable -> 返回值 { 函数体 } 下面我会由简到难分享lambda的用法和它的理解 &#xff08;1&#xff09;基本使用和理解 我们先来看一段代码 我认为第一次看这个表达式应该还是能够…

LeetCode.55.跳跃游戏(贪心算法思路)

题目描述&#xff1a; 给你一个非负整数数组 nums &#xff0c;你最初位于数组的 第一个下标 。数组中的每个元素代表你在该位置可以跳跃的最大长度。 判断你是否能够到达最后一个下标&#xff0c;如果可以&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 输…

为什么大公司不愿意使用 Python 作为 Web 后端开发语言?

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐&#xff1a;「storm…

使用uart串口配置TMC2209模块

串口配置的优点&#xff1a; 通过串口助手配置TMC2209的寄存器实现转速&#xff0c;方向&#xff0c;细分数等寄存器设置。最大细分可达256。 TMC2209串口配置数据发送格式&#xff1a; 通过数据手册可知&#xff0c;TMC2209写入数据需要发送64位16进制。 前面0~7位是同步保…

OpenStack云计算平台实战

项目一 任务一 了解云计算 目前主流的开源云计算平台如下&#xff1a; OpenStack。OpenStack是一个提供IAAS开源解决方案的全球性项目&#xff0c;由Rackspace公司和NASA共同创办&#xff0c;采用了Apache2.0许可证&#xff0c;可以随意使用。OpenStack并不要求使用专门的硬…