深入学习 Redis - 深挖经典数据类型之 zset

news2024/11/16 21:53:57

目录

前言

一、zset 类型

1.1、操作命令

zadd / zrange(添加 / 查询)

zcard(个数)

zcount(区间元素个数)

zrevrange(逆序展示)

zrangebyscore(按分数找元素)

zpopmax / zpopmin(删除)

bzpopmax / bzpopmin(阻塞删除)

zrank / zrevrank(排名)

zscore(分数)

zrem(删除) 

zremrangebyrank / zremrangebyscore(删除指定区间)

zincrby(增加分数)

交集、并集、差集说明

zinterstore / zunionstore(交集 / 并集)

1.2、内部编码方式

1.3、使用场景

排行榜系统


前言


redis 中所有的 key 都是字符串,value 的类型是存在差异的,因此出现了操控不同 value 的命令,接下来,就一起来学习一下吧~

Ps1:接下来,我给出的指令都是按照 Redis 官方文档的语法格式来解析的,[ ] 相当于一个独立的单元,表示可选项(可有可无),其中 | 表示 “或者” 的意思,多个只能出现一个,[ ] 和 [ ] 之间是可以同时存在的.

Ps2:一个快速失去年终奖的小技巧 —— 清除 redis 上所有的数据 =》 FLUSHALL,这个操作可以把 redis 上所有的键值对全部带走.

一、zset 类型


1.1、操作命令

Zset 是一个有序且唯一的集合,在 zset 中的 member 同时引入了一个属性 score(分数),支持浮点类型,也就是说每一个 member 都安排一个分数,并且默认按照分数进行升序排序.

Ps:zset 中的 member 和 score 不是键值对的关系,可以称为是一个 "pair",类似于 C++ 中的 std::pair。

对于有序集合来说,既可以通过 member 找到对应的 score ,又可以通过 score 找到匹配的 member。

zadd / zrange(添加 / 查询)

使用 zadd 往有序集合中,添加元素和分数.

时间复杂度是 O(log N),这是由于 zset 是有序结构,新增元素需要放在合适的位置上,底层是通过 跳表 这个数据结构实现的.

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

以下是对几个选项的说明:

  • NX | XX:的使用和之前 set 添加元素使用一样,这里值得注意的是 不加 NX | XX 选项时,当 member 不存在,此时就会达到 “添加新 member” 的效果,如果当前 member 已经存在,此时就会更新分数.
  • LT | GT:LT(less than)表示一旦要进行更新分数,如果分数比之前小,则更新成功,否则不跟新;GT(greater than)反之.
  • CH:默认情况下,ZADD 返回的是本次添加的元素个数,但指定这个选项之后,就会还包含本次更新的元素的个数。
  • INCR:此时命令类似 zincrby 的效果,将元素的分数加上指定的分数。此时只能指定⼀个元素和 分数。

使用 zrange 查看 有序集合 中的元素详情(类似于 lrange 可以指定下标构成区间)

ZRANGE key start stop [WITHSCORES]

Ps:加上 withscores ,就可以连分数一起显示.

 

zcard(个数)

获取 zset 中的元素个数.

ZCARD key

zcount(区间元素个数)

获取分数在 min 和 max 之间(默认是闭区间)的元素个数,其中 min 和 max 本身就是浮点数类型,可以哦那个 inf 表示无穷大, -inf 表示 负无穷大.

ZCOUNT key min max

时间复杂度是 O(log N),具体执行流程如下:

先根据 min 找到对应的元素,再根据 max 找到对应的元素(这里的时间复杂度就是 O(log N) ),这里实际上 zset  内部会记录每个元素当前的 “排行 / 次序”,因此查到元素后就知道元素的 “次序”(下标),就可以直接把 max 对应的元素次序和 min 对应的元素次序,做减法即可.

Ps:如果想要排除边界值,可以使用括号,但是表示比较奇葩~,使用 ( 表示开区间,例如我要获取(70,98)这个区间的个数,就需要如下写法表示

一个好的设定是符合直觉的,但为什么这么设定?

这是为了考虑兼容性的原因:一旦在新的版本中引入和之前不兼容的特性,成本是非常高的(不兼容的案例最典型的就是 IPv6),一般来说,如果确实需要做出这种不兼容的修改,可以先把这个要修改的内容标记成 “弃用”(给程序员打个预防针),同时推出新的版本,若干个版本之后,再逐渐把这样的功能完成修改.

zrevrange(逆序展示)

zrevrange 中的 rev 就是 reverse 的缩写,表示逆序的意思。默认是升序,通过 zrevrange 就可以实现分数降序排序.

ZREVRANGE key start stop [WITHSCORES]

zrangebyscore(按分数找元素)

按照分数来找元素,和 zcount 类似.
 

ZRANGEBYSCORE key min max [WITHSCORES]

Ps:这个命令可能在 6.2.0 之后废弃,并且将功能合并到 zrange 中.

zpopmax / zpopmin(删除)

zpopmax 用来删除分数最高的 count 个元素.

zpopmin 用来删除分数最低的 count 个元素.

Ps:若分数一样,就按照字典序来删除其中的 count 个元素

ZPOPMAX key [count]
ZPOPMIN key [count]

时间复杂度为 O(log(N) * M),其中:

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

使用 zpopmax 删除的是最大值,在有序集合中就是最后一个元素(尾删),既然是尾删,为什么我们不把最后一个元素的位置的记录下来,后续删除不就直接就 O(1) 了嘛?

但是很遗憾, redis 目前并没有这么做~

事实上,redis 的源码中,针对有序集合,确实是记录尾部的位置,但在删除的时候,并没有用上这个特性,而是直接使用了一个 “通用的删除函数”(给定一个 member 的值,进行查找找到位置后再进行删除).

但这里我认为是存在优化空间的,确实可以通过记录+尾删达到 O(1) 的复杂度,但是这种优化的活要优化在刀刃上~  优化一般是要找到性能瓶颈,进行针对的优化,但是当前这个 log N 的速度也不慢,如果 N 不是夸张的大,基本是可以近似 O(N) 的~

比如去追一个妹子:

  1. 经常在她面前刷存在感,混个脸熟.(1个月)
  2. 约出来一起玩(叫上很多僚机). (2个月)
  3. 单独约出来玩.(3个月)
  4. 确定关系.(1年)

那么如果你在第一条上使劲优化,是没啥乱用的~

bzpopmax / bzpopmin(阻塞删除)

阻塞版本的 zpopmax / zpopmin,这里的 有序集合 可以认为是一个 带阻塞功能 的 "优先级队列",在队列为空的时候阻塞,直到其他客户端插入元素为止.

BZPOPMAX key [key ...] timeout
BZPOPMIN key [key ...] timeout

 timeout 表示超时时间,表示最多阻塞多久,单位是 s,支持小数形式.

时间复杂度是 log(N).

Ps:当 bzpopmax / bzpopmin 监听了多个 key,表示是从这若干个 key 中只删除一次.

 

zrank / zrevrank(排名)

zrank 和 zrevrank 都是返回指定元素的排名(下标),其中 zrank 按照升序排序,zrevrank 按照降序排序.

时间复杂度为 O(log N),主要是有个查询位置的过程(可以认为是在堆中查找元素)。

ZRANK key member
ZREVRANK key member

 

zscore(分数)

获取指定元素的分值.

ZSCORE key member

时间复杂度是 O(1),之前根据 member 找元素,都是 logN ,这里虽然也是要先找元素,但是 redis 对于此处的查询做了特殊的优化,付出了额外的空间代价.

zrem(删除) 

删除指定的 member 元素.

ZREM key member [member ...]

时间复杂度是 O(logN * M),其中 N 表示有序集合中元素的个数(因为要先找到元素),M 是 member 的个数.

zremrangebyrank / zremrangebyscore(删除指定区间)

zremrangebyrank 是指定下标描述范围来进行删除(元素升序排序).

zremrangebyscore 是指定分数区间进行删除.

Ps:区间都是闭合的(左闭右闭).

ZREMRANGEBYRANK key start stop
ZREMRANGEBYSCORE key min max

时间复杂度是 O(logN * M),其中 N 表示有序集合中元素的个数(因为要先找到元素),M 是要删除的元素个数.

zincrby(增加分数)

为指定的元素的关联分数添加指定的分数值。

ZINCRBY key increment member

Ps:修改分数后,任然保持升序. 

交集、并集、差集说明

zinter、zunion 、zdiff 这三个命令是从 redis 6.2 开始支持的,咱们使用的是 redis 5 ,此处不涉及.

zinterstore / zunionstore(交集 / 并集)

求出多个 key 之间的交集,并保存到一个新的集合中,合并的同时元素按照不同方式得到新的分值.

ZINTERSTORE destination numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE <SUM | MIN | MAX>]

几个参数的说明:

destination:表明了要把结果存储到哪一个 zset 中.

numkeys:是一个整数,描述了后面又几个 key 参与交集运算(这样设定是为了将自定义的 key   和后面的参数区分开来,类似于 HTTP 协议中 Content-Length 的作用——解决 TCP 面向字节流传输的粘包问题).

weights:权重,相当于一个系数,会乘上当前的分数(类似于数学中的权值,假如我是一个好看有才华的妹子,那么追我的不同小哥哥身上都会有不同的权值,比如长得帅占5%、会舔占2%、有钱占93%......).

addregete:这里提供了三种参数,描述了相交的元素分数的处理方式(若不设置,默认是求和).

Ps:zunionstore 用法 和 zinterstore 基本一致.

 

1.2、内部编码方式

内部编码方式有以下两种:

  • skiplist:跳表,类似于 leetcode 上的一个经典题目,“复制带随机指针的链表”,跳表也是链表,不同于普通的链表,每一个节点上有多个指针域,巧妙的搭配这些指针域的指向就可以做到,从跳表上查询元素的时间复杂度是 O(logN),相比于树形结构,更适合范围获取元素.
  • ziplist:压缩列表,当哈希表里的元素比较少的时候,就优化成了 ziplist 了,能够节省空间(压缩的原因:redis 上有很多 key,可能某些 key 的 value 是 hash,此时如果 key 特别多,对应的 hash 也特别多,但是每个 hash 又不是特别大的情况下,就尽量去压缩,让整体占用内存更小了).

Ps:如果有序集合中元素较少,或者单个元素体积较小,使用 ziplist 来存储,这样更节省内存空间。如果元素较多,或者单体过大,就是用 skiplist 来存储了.

1.3、使用场景

排行榜系统

这种场景有很多:微博热搜、游戏天梯排行、成绩排行......

例如使用 zset 来实现 游戏天梯排行,只需要把玩家的信息和对应的分数给放到有序集合中即可,自动就生成一个排行榜,随时可以按照排行(下标),按照分数,进行范围查询~

也可以通过 zincrby 来修改分数,排行也能自适应调整(logN).

玩家那么多,zset 能存下吗?

userId 4 个字节 score 8 个字节来理解,那么表示一个玩家大概是 12 个字节,往多了算,1 亿玩家,大概是 12亿 字节 => 1.2 GB(这里的单位换算一定要张口就来~ 影响升职加薪),这大么?

游戏排行榜的先后顺序还是比较容易确定的,但是像 微博热度 这种就不太好算~

他是根据综合数值来衡量的:

  1. 浏览量
  2. 点赞量
  3. 转发量
  4. 评论量

这就需要根据每一个维度的权重,来计算综合得分,此时就可以借助 zinterstore / zunionstore 按照加权的方式来处理了,具体的,member 就是 微博 的 id,而 score 就是通过 zinterstore / zunionstore 按照约定好的权重,进行集合运算即可,最后得到的集合分数就是热度,排行榜也就出来了.

 

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

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

相关文章

服务器(容器)开发指南——SSH打洞开发

文章目录 SSH容器服务打包测试服务文件镜像打包 SSH打洞开发部署带SSH的容器SSH连接服务器&#xff08;容器内部&#xff09;SSH访问容器内的缺陷 IDE远程SSH开发VSCode远程SSH开发Jetbrains系列产品SSH远程开发 在进行定制化的服务开发时&#xff0c;我们有时候只能在固定的服…

页面设计—GridRow栅格行组件

1、何时使用 在栅格布局基础上&#xff0c;一般需要通过行&#xff08;GridRow&#xff09;和列&#xff08;GridCol&#xff09;来定义信息区块的外部框架&#xff0c;以保证页面的每个区域能够稳健地排布起来。 2、如何使用 &#xff08;1&#xff09;找到GridRow组件&…

乙酰基四肽-2--------增加肌肤紧实度,重建表皮结构

简介 为了避免皮肤下垂和松弛&#xff0c;以下几种重要蛋白共同参与&#xff0c;维持皮肤结构的粘着和紧致&#xff1a;Fibulin 5亦称FBLN-5&#xff0c;是细胞外基质蛋白Fibulin家族中的最新成员。它广泛分布于富含弹性纤维的组织&#xff0c;能直接与原弹性蛋白结合&#xf…

【矩特征】图像矩特征

every blog every motto: You can do more than you think. https://blog.csdn.net/weixin_39190382?typeblog 0. 前言 cv2.moments() 矩特征 1. 正文 (1). 空间矩 零阶矩&#xff1a;m00一阶矩&#xff1a;m10,m01二阶矩&#xff1a;m20,m11,m02三阶矩&#xff1a;m30,m…

配置Tomcat远程调试

配置Tomcat远程调试 1.Tomcat开启远程访问调试1.1 编写脚本,开启配置1.2 重启Tomcat服务 2. IDEA配置远程调试Tomcat2.1 IDEA右上角更改配置2.2 添加一个remote jvm debug2.3 进行配置2.4 配置后进行启动连接至此在本地打断点即可调试远程服务器 1.Tomcat开启远程访问调试 1.1…

Cesium:加载geojson面贴地和显示边界问题

1.背景 cesium加载geojson面数据后&#xff0c;有部分数据在地形下面显示不全&#xff0c; 加了clampToGround: true&#xff0c;设置贴地后&#xff0c;边界又不见了 this.viewer.dataSources.add(GeoJsonDataSource.load(http://xx/xzbj.geojson, {stroke: Color.BLACK.with…

questasim一个

安装好questasim后默认的波形查看界面字体很小&#xff0c;颜色看起来也不舒服&#xff0c;所以调整了一下颜色布局如下图&#xff0c;顺便记录一下波形窗口颜色大小及选中行高亮如何设置 1、波形字体颜色设置 参考如何设置一个清爽的仿真窗口&#xff08;仿真工具使用技巧&a…

vue build 打包遇到bug解决记录

文章目录 vue-cli-service servevue打包修改dist文件夹名字vue build require is not defined 和 exports is not defind 错误 vue-cli-service serve 通常vue是不能直接使用vue-cli-service命令在终端运行的&#xff0c;所以才会在package.json中配置了scripts&#xff1a; …

精选 2023 年大厂高频 Java 面试真题集锦(含答案),面试一路开挂

本文涵盖了阿里巴巴、腾讯、字节跳动、京东、华为等大厂的 Java 面试真题&#xff0c;不管你是要面试大厂还是普通的互联网公司&#xff0c;这些面试题对你肯定是有帮助的&#xff0c;毕竟大厂一定是行业的发展方向标杆&#xff0c;很多公司的面试官同样会研究大厂的面试题。 …

小程序轮播图的两种后台方式(PHP)--【浅入深出系列008】

微信目录集链接在此&#xff1a; 详细解析黑马微信小程序视频–【思维导图知识范围】难度★✰✰✰✰ 不会导入/打开小程序的看这里&#xff1a;参考 让别人的小程序长成自己的样子-更换window上下颜色–【浅入深出系列001】 文章目录 本系列校训学习资源的选择啥是轮播图轮播…

基于Android系统的外卖APP【纯干货分享,免费领源码04871】

摘要 立足于当下餐饮行业现有的点餐模式&#xff0c;分析传统APP点餐的运作流程&#xff0c;结合Android系统的特点设计新型的外卖APP。近几年&#xff0c;人们生活水平日益提升&#xff0c;但工作强度和压力不断增强&#xff0c;尤其是对于上班族而言&#xff0c;到餐厅吃饭费…

C++实现通用进制转换

C实现通用进制转换 #include <iostream> #include <string> #include <algorithm> #include <sstream> using namespace std; // 将一个字符转换为对应的数字 int charToNum(char c) { if (c > 0 && c < 9)return c - 0; el…

四旋翼无人机使用教程

文章目录 前言一、检查遥控器电源开关混控拨码开关微调开关飞行模式刹车开关行程开关接收机对码 二、检查飞机检查接线 三、解锁并飞行 前言 PX4固件 QGC地面站 Pixhwak飞控 Mc6c遥控器 开源飞控博大精深&#xff0c;欢迎广大爱好者加博主微信名片&#xff0c;一起学习交流。…

Windows版filehub安装教程

一个基于Github开发的文件存储软件&#xff0c;美其名曰&#xff1a;FileHub&#xff0c;可存万物&#xff0c;而且绝不和谐任何文件。类似于百度云盘的功能&#xff0c;但是功能上肯定达不到百度云盘的效果&#xff0c;但是基本功能还是有的&#xff1a;例如登录注册&#xff…

C语言每天一练:输出杨辉三角

题目&#xff1a;请输出以下杨辉三角(要求输出前10行) 列&#xff1a; 1 1 1 1 2 1 1 3 3 1 1 4 6 4 1 1 5 10 10 5 1 ...... 题解析&#xff1a;不了解杨辉三角的可以百度看一下&#xff0c;大概是这样的&#xff0c;咱们就可以解…

日撸代码300行:第55天(基于 M-distance 的推荐 (续))

代码来自闵老师”日撸 Java 三百行&#xff08;51-60天&#xff09;“&#xff0c;链接&#xff1a;https://blog.csdn.net/minfanphd/article/details/116975957 工作承接第54天的基于M-distance 的推荐&#xff0c;目标是自己实现一下user-based recommendation。原博客中的…

NLP实验案例100个(6-10)

实验六 数据类型 一、实验目的及要求 熟悉数据的数据类型二、实验设备&#xff08;环境&#xff09;及要求 开发环境&#xff1a;jupyter notebook 开发语言以及相关的库&#xff1a;python开发语言 numpy库 三、实验内容与步骤 1.创建一个array类型的数据&#xff0c;设置…

【ShaderToy中图形效果转译到UnityShaderlab案例分享,纯代码实现卡通心跳_Pattern】

Shader"ShaderToy/Pattern" {Properties{_MainTex("_MainTex", 2D) = "white"{}}SubShader{Pass{CGPROGRAM

性能如何通过分析后台资源确定瓶颈之CPU内存

确定瓶颈之CPU、内存 影响性能的因素 CPU 内存 网络 硬件 i/o 中间件 应用服务器 数据库 家门口的路比较拥堵&#xff1a;解决方案---多来几条路/加宽路/修地铁/修个桥、修整路面、找交警指挥交通/红绿灯、限行、分散周围居民、收费、其他路段的问题 CPU--中央处理器--…

使用go与智能合约交互之函数选择器调用

go与智能合约交互的方式有很多种&#xff0c;其中一种方式可以在不知道合约源码的情况下进行调用&#xff0c;接下来让我们一起学习一下。 1、首先我们安装一下go-ethereum go get -u github.com/ethereum/go-ethereum2、新建main.go文件&#xff0c;添加依赖 import ("…