【Redis】zset常用命令集合间操作内部编码使用场景

文章目录

  • 前置知识
    • 列表、集合、有序集合三者的异同点
  • 普通命令
    • ZADD
    • ZCARD
    • ZCOUNT
    • ZRANGE
    • ZREVRANGE
    • ZRANGEBYSCORE
    • ZPOPMAX
    • BZPOPMAX
    • ZPOPMIN
    • BZPOPMIN
    • ZRANK
    • ZREVRANK
    • ZSCORE
    • ZREM
    • ZREMRANGEBYRANK
    • ZREMRANGEBYSCORE
    • ZINCRBY
  • 集合之间的操作
    • ZINTERSTORE
    • ZUNIONSTORE
  • 命令小结
  • 内部编码
    • 测试内部编码
  • 使用场景

前置知识

有序集合中的每个元素都有⼀个唯⼀的浮点类型的分数(score)与之关联,这使得有序集合中的元素是可以维护有序性的,进行排序的时候,就是按照此处的分数大小进行升序/降序排序

注意:zset主要还是用来存member,score只是辅助

例子:使用有序集合显⽰三国中的武将的武⼒

image-20231021213941478
  • 分数不同,则按照分数来升序排序(zset内部按照升序排列),分数相同的时候,再按照元素自身字符串的字典序来排序

有序集合中的元素是不能重复的,但分数允许重复。类⽐于⼀次考试之后,每个⼈⼀定有⼀个唯⼀的分数,但分数允许相同


列表、集合、有序集合三者的异同点

image-20231021214034003


普通命令

ZADD

添加或者更新指定的元素以及关联的分数到zset中,分数应该符合double类型,+inf/-inf作为正负极限也是合法的

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

相关选项:

  • XX:仅仅⽤于更新已经存在的元素,不会添加新元素
  • NX:仅⽤于添加新元素,不会更新已经存在的元素
  • CH:默认情况下,ZADD返回的是本次添加的元素个数,但指定这个选项之后,就会还包含本次更新的元素的个数,可能会影响zadd的返回值
    • 注意:通常ZADD的返回值只计算添加的新元素的数量。
  • INCR:此时命令类似ZINCRBY的效果,将元素的分数加上指定的分数。此时只能指定⼀个元素和分数

返回值:本次添加成功的元素个数 时间复杂度:O(log(N))

  • 注意:之前hash,set,list添加一个元素都是 O ( 1 ) O(1) O(1),但是zset添加元素的时间复杂度为 O ( l o g N ) O(logN) O(logN),这是因为zset是有序结构,要新增新元素,要放到合适的位置上,之所以不是 O ( N ) O(N) O(N),是因为zset内部的数据结构为调表

image-20231023205409741

不加NX || XX选项的时候:

  • 如果当前member不存在,此时就会达到添加新member的效果
  • 如果当前member已经存在,此时就会更新分数

image-20231026204150134

XX:只更新已经存在的元素。不要添加新元素。

NX:只添加新元素。不要更新已经存在的元素。


LT和ET:

image-20231026204212618

LT:现在要更新分数了,发现现在给定的新的分数比之前的分数小,此时就更新成功,否则就不更新

  • 只有当新分数小于当前分数时才会更新现有元素。这个标志不会阻止添加新元素

GT:仅在新分数大于当前分数时更新现有元素。这个标志不会阻止添加新元素。


ZCARD

获取⼀个zset的基数(cardinality),即zset中的元素个数

语法:ZCARD key

返回值:zset内的元素个数 时间复杂度:O(1)

image-20231023205457393

ZCOUNT

返回分数在min和max之间的元素个数,默认情况下,min和max都是包含的,即范围是 [ m i n , m a x ] [min,max] [minmax],可以通过(来排除边界值

语法:ZCOUNT key min max

返回值:满⾜条件的元素列表个数 时间复杂度:O(log(N))

image-20231023205653093

时间复杂度分析:先根据min找到对应的元素,再根据max找到对应的元素(时间复杂度为: O ( l o g N ) O(logN) O(logN)),实际上,zset内部会记录每个元素当前的排行/次序,查询到元素就直接知道了元素所在的次序(下标),就可以直接把max对应的元素的次序和min对应的元素的次序做减法即可

注意1:min和max可以写成浮点数,因为zset的分数本身就是浮点数

注意2:在浮点数当中,存在两个特殊的数值:inf表示无穷大,-inf表示负无穷大(负无穷大 != 无穷小)


ZRANGE

返回指定区间⾥的元素,分数按照升序。带上WITHSCORES可以把分数也返回

语法:ZRANGE key start stop [WITHSCORES] 
  • 注意:此处的[start,stop]为下标构成的区间.从0开始,⽀持负数.

返回值:区间内的元素列表 时间复杂度:O(log(N)+M)

image-20231023205739557

时间复杂度分析:先根据下标找到边界值(O(logN)),然后从start对应位置开始往后遍历,M:start-stop区间的元素个数

ZREVRANGE

返回指定区间⾥的元素,按照分数降序打印,带上WITHSCORES可以把分数也返回

语法:ZREVRANGE key start stop [WITHSCORES]

返回值:区间内的元素列表 时间复杂度:O(log(N)+M)

image-20231024112139729

ZRANGEBYSCORE

返回分数在min和max之间的元素,默认情况下,min和max都是包含的,可以通过(排除

语法:ZRANGEBYSCORE key min max [WITHSCORES] 

返回值:区间内的元素列表 时间复杂度:O(log(N)+M)

image-20231024112305645

ZPOPMAX

删除并返回分数最⾼的count个元素

语法:ZPOPMAX key [count] 

返回值:分数和元素列表 时间复杂度:*O(log(N)M)

  • N:有序集合元素个数 M:count要删除的元素个数

image-20231024112408261

如果存在多个元素分数相同,同时为最大值的时候,zpopmax进行删除,如果不指定count,仍然只删除其中一个元素

  • 如果分数相同会按照member字符串的字典序排序

注意:此处删除的是最大值,在有序集合当中,最大值相当于就是最后一个元素(删除最大值==>尾删),既然是尾删,此时可以把最后一个元素的位置特殊记录下来,后续就可以以 O ( 1 ) O(1) O(1)的复杂度进行删除,所以 O ( l o g N ) = > O ( 1 ) O(logN) =>O(1) O(logN)=>O(1)是可能的,但是redis并没有这么做


BZPOPMAX

ZPOPMAX的阻塞版本,有序集合可以视为是优先级队列,此时使用bzpopmax就相当于是一个带有阻塞功能的优先级队列

语法:BZPOPMAX key [key ...] timeout #timeout:超时时间,单位是s,支持小数形式,写做0.1 就是代表100ms

返回值:元素列表 时间复杂度:O(log(N)) =>删除最大值花费的时间

image-20231024112512538

可以同时等待多个key对应的有序集合当中的元素就绪,阻塞到有其它客户端往任意一个key当中插入元素。如果有序集合已经有元素了,直接就能返回,不会阻塞

ZPOPMIN

删除并返回有序集合当中分数最低的count个元素

语法:ZPOPMIN key [count] 

返回值:分数和元素列表 时间复杂度:O(log(N)*M)

image-20231024112657296

BZPOPMIN

ZPOPMIN的阻塞版本

语法:BZPOPMIN key [key ...] timeout 

返回值:元素列表 时间复杂度:O(log(N))

image-20231024112738424

ZRANK

返回指定元素的排名,升序计算下标

语法:ZRANK key member 

返回值:排名 时间复杂度:O(log(N)) =>查询位置的过程

image-20231024112956618

注意:zcount在计算的时候,就是先根据分数找到元素,再根据元素获取到排名,再把排名相减就得到了元素个数

zrank得到的下标,是从前往后计算的,下标从0开始

ZREVRANK

返回指定元素的排名,降序计算下标(是从后往前计算的,下标从0开始)

语法:ZREVRANK key member 

返回值:排名 时间复杂度:O(log(N))

image-20231024113017435


ZSCORE

返回指定元素的分数

语法:ZSCORE key member 

返回值:分数 时间复杂度:O(1)

image-20231024113034949

注意:前面根据member找分数都是 l o g N logN logN,此处相当于是redis对于这样的查询做了优化,付出了额外的空间代价,优化到了 O ( 1 ) O(1) O(1)查询

ZREM

删除指定的元素

语法:ZREM key member [member ...] 

返回值:本次操作删除的元素个数 时间复杂度:O(M*log(N))

  • N:有序集合元素个数 M:参数当中member的个数

image-20231024113052529

ZREMRANGEBYRANK

按照排序,升序删除指定范围的元素,左闭右闭 [ s t a r t , s t o p ] [start,stop] [start,stop]

语法:ZREMRANGEBYRANK key start stop 

返回值:本次操作删除的元素个数 时间复杂度:O(log(N)+M)

  • N:有序集合元素个数 M: s t o p − s t a r t stop- start stopstart区间的元素个数

image-20231024113502072

ZREMRANGEBYSCORE

按照分数删除指定范围的元素,左闭右闭 [ m i n , m a ] [min,ma] [min,ma],可以通过(排除边界值

语法:ZREMRANGEBYSCORE key min max 

返回值:本次操作删除的元素个数 时间复杂度:O(log(N)+M)

image-20231024113618390

ZINCRBY

为指定的元素的关联分数添加指定的分数值,修改之后,仍然会保持整个有序集合是升序的

语法:ZINCRBY key increment member 

返回值:增加后元素的分数 时间复杂度:O(log(N))

image-20231024113716214


集合之间的操作

ZINTERSTORE

出给定有序集合中元素的交集并保存进⽬标有序集合中,在合并过程中以元素为单位进⾏合并,元素对应的分数按照不同的聚合⽅式和权重得到新的分数

语法:ZINTERSTORE destination numkeys key [key ...] [WEIGHTS weight  [weight ...]] [AGGREGATE <SUM | MIN | MAX>]
  • destination:要把求得的交集存储到哪个key当中,对应的zset
  • numkeys:是一个整数,描述后续有几个key参与交集运算
  • weights:权重,因为是有序集合,带有分数,此处的权重相当于是一个系数,会乘当前的分数
  • AGGREGATE:表示最后交集的结果取什么结果,求和,最小值,最大值

有序集合的交集操作

image-20231021222217546

返回值:⽬标集合中的元素个数

时间复杂度:O(N*K)+O(M*log(M))

  • N是输⼊的有序集合中,最⼩的有序集合的元素个数
  • K是输⼊了⼏个有序集合 ==>多少个有序集合进行合并求交集
  • M是最终结果的有序集合的元素个数

K一般不会很多,可以近似看成1,也可以认为N和M是接近的(同一个数量级),那么O(N*K)+O(M*log(M)) =>O(M) + O(M * logM) =>O(M*logM)

image-20231024114600105


ZUNIONSTORE

求出给定有序集合中元素的并集并保存进⽬标有序集合中,在合并过程中以元素为单位进⾏合并,元素对应的分数按照不同的聚合⽅式和权重得到新的分数

语法:ZUNIONSTORE destination numkeys key [key ...] [WEIGHTS weight  [weight ...]] [AGGREGATE <SUM | MIN | MAX>]

有序集合的并集操作

image-20231021222423802


返回值:⽬标集合中的元素个数

时间复杂度:O(N)+O(M*log(M))N是输⼊的有序集合总的元素个数;M是最终结果的有序集合的元素个数


命令小结

image-20231021222600898


内部编码

有序集合类型的内部编码有两种:

  • ziplist(压缩列表):当有序集合的元素个数⼩于zset-max-ziplist-entries配置(默认128个),同时每个元素的值都⼩于zset-max-ziplist-value配置(默认64字节)时,Redis会⽤ziplist来作为有序集合的内部实现,ziplist可以有效减少内存的使⽤
  • skiplist(跳表):当ziplist条件不满⾜时(元素个数比较多或者单个元素比较大),有序集合会使⽤skiplist作为内部实现,因为此时ziplist的操作效率会下降
    • 跳表是一个复杂链表,查询元素的时间复杂度为 l o g N logN logN,相比于树形结构,更适合按照范围获取元素

测试内部编码

1)当元素个数较少且每个元素较⼩时,内部编码为ziplist

image-20231021222747827

2)当元素个数超过128个,内部编码skiplist

3)当某个元素⼤于64字节时,内部编码skiplist

image-20231021222822679


使用场景

有序集合⽐较典型的使⽤场景就是排⾏榜系统。例如常⻅的⽹站上的热榜信息,榜单的维度可能是多⽅⾯的:按照时间、按照阅读量、按照点赞量。

例子:使⽤点赞数这个维度,维护每天的热榜:

1)添加⽤⼾赞数:例如⽤⼾james发布了⼀篇⽂章,并获得3个赞,可以使⽤有序集合的zadd和zincrby功能

zadd user:ranking:2022-03-15 3 james 

之后如果再获得赞,可以使⽤zincrby:

zincrby user:ranking:2022-03-15 1 james 

2)取消⽤⼾赞数:由于各种原因(例如⽤⼾注销、⽤⼾作弊等)需要将⽤⼾删除,此时需要将⽤⼾从榜单中删除掉,可以使⽤zrem。例如删除成员tom

zrem user:ranking:2022-03-15 tom 

3)展⽰获取赞数最多的10个⽤⼾

zrevrangebyrank user:ranking:2022-03-15 0 9

4)展⽰⽤⼾信息以及⽤⼾分数

该功能将⽤⼾名作为键后缀,将⽤⼾信息保存在哈希类型中,⾄于⽤⼾的分数和排名可以使⽤zscore和zrank来实现

hgetall user:info:tom
zscore user:ranking:2022-03-15 mike
zrank user:ranking:2022-03-15 mik

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【LeetCode刷题-树】--998.最大二叉树II

998.最大二叉树II /*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* TreeNode(int val) { this.val val; }* TreeNode(int val, TreeNode left, TreeNode right) {* …

【数据结构】C语言实现队列

目录 前言 1. 队列 1.1 队列的概念 1.2 队列的结构 2. 队列的实现 2.1 队列的定义 2.2 队列的初始化 2.3 入队 2.4 出队 2.5 获取队头元素 2.6 获取队尾元素 2.7 判断空队列 2.8 队列的销毁 3. 队列完整源码 Queue.h Queue.c &#x1f388;个人主页&#xff1a…

电子学会2023年6月青少年软件编程(图形化)等级考试试卷(四级)真题,含答案解析

青少年软件编程(图形化)等级考试试卷(四级) 一、单选题(共10题,共30分) 1. 下列积木运行后的结果是?( )(说明:逗号后面无空格) A.

【Java集合】聊聊Hashmap的哈希函数、扩容、树化

哈希函数 hashmap是开发中常用的一个集合&#xff0c;除了一些基本的属性、put、get等流程&#xff0c;本篇文章主要介绍下哈希函数、扩容、树化的一些细节。 而hash函数就是hashmap的重中之重。 static final int hash(Object key) {int h;return (key null) ? 0 : (h key…

阅读芯片源码(RTL)

part one 主要的原则。 一个rtl可以是这样的&#xff1a; 经常大家习惯于算法和数据结构。对于设计的部分&#xff0c;落实不一定多。 另外一个rtl也可以是这样的&#xff1a; 所以从不同的层面来讲&#xff0c;一个Rtl有不同的表述。 首先大概把所有的部分浏览一遍&#x…

CICD 持续集成与持续交付——gitlab

部署 虚拟机最小需求&#xff1a;4G内存 4核cpu 下载&#xff1a;https://mirrors.tuna.tsinghua.edu.cn/gitlab-ce/yum/el7/ 安装依赖性 [rootcicd1 ~]# yum install -y curl policycoreutils-python openssh-server perl[rootcicd1 ~]# yum install -y gitlab-ce-15.9.3-ce.0…

Jmeter——结合Allure展示测试报告

在平时用jmeter做测试时&#xff0c;生成报告的模板&#xff0c;不是特别好。大家应该也知道allure报告&#xff0c;页面美观。 先来看效果图&#xff0c;报告首页&#xff0c;如下所示&#xff1a; 报告详情信息&#xff0c;如下所示&#xff1a; 运行run.py文件&#xff0c;…

AVL树实现

目录 ​编辑 一&#xff0c;AVL树的概念 二&#xff0c;实现AVL树&#xff08;部分&#xff09; 1.AVL树的节点 2.AVL数的插入 1.当根节点为nullptr时要执行如下代码&#xff1a; 2.当根节点不为nullptr时 1.当parent的_bf变为0时&#xff0c;parent之前的_bf的大小就是…

前端面试:如何实现并发请求数量控制?

题目&#xff1a;实现一个并发请求函数concurrencyRequest(urls, maxNum) 要求如下&#xff1a; 要求最大并发数 maxNum;每当有一个请求返回&#xff0c;就留下一个空位&#xff0c;可以增加新的请求;所有请求完成后&#xff0c;结果按照 urls 里面的顺序依次打出&#xff1b;…

C++初阶-内存管理

内存管理 一、C/C内存分布二、C语言中动态内存管理方式&#xff1a;malloc/calloc/realloc/free三、C内存管理方式new/delete操作内置类型new和delete操作自定义类型 四、operator new与operator delete函数operator new与operator delete函数 五、new和delete的实现原理内置类…

论文-分布式-拜占庭将军问题

目录 0-前言 1-导引 2-不可能性 3将军(1叛徒)问题不存在解/不能达成共识 少于3m1个将军(有m个叛徒)不存在解/不能达成共识 精确一致性与近似一致性是同等困难的 3-使用口头消息的解 “口头消息”的含义 OM(m)算法的步骤 OM(m)算法的正确性推导 4-使用签名消息情况下…

054-第三代软件开发-信号槽

第三代软件开发-信号槽 文章目录 第三代软件开发-信号槽项目介绍信号槽实现原理与MFC消息映射机制区别Qt信号槽机制的优缺点 关键字&#xff1a; Qt、 Qml、 关键字3、 关键字4、 关键字5 项目介绍 欢迎来到我们的 QML & C 项目&#xff01;这个项目结合了 QML&#x…