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

目录

前言

一、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 按照约定好的权重,进行集合运算即可,最后得到的集合分数就是热度,排行榜也就出来了.

 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Java框架学习(三)spring5高级49讲

文章目录 1、BeanFactory与ApplicationContext2、BeanFactory与ApplicationContext的容器实现BeanFactory的容器实现后处理器排序 ApplicationContext的容器实现 3、Bean的生命周期Bean后处理器 4、常见的Bean后处理器5、常见BeanFactory后处理器6、Aware和InitializingBean接口…

Element-plus侧边栏踩坑

问题描述 el-menu直接嵌套el-menu-item菜单&#xff0c;折叠时不会出现文字显示和小箭头无法隐藏的问题&#xff0c;但是实际开发需求中难免需要把el-menu-item封装为组件 解决 vue3项目中嵌套两层template <template><template v-for"item in list" :k…

1.2 网络安全法律法规

数据参考&#xff1a;CISP官方 目录 国家立法体系网络安全法解析网络安全相关法律 一、国家立法体系 1、我国的立法体系 我国的立法体系在网络空间治理中扮演着基础工作的角色。为了应对快速发展的网络技术和威胁&#xff0c;我国采取了多级立法机制来完善网络空间的法律…

腾讯云 Cloud Studio 实战训练营——快速构建React完成点餐H5页面

目录 一、前言 1、什么是腾讯云 Cloud Studio 2、本文实验介绍 二、前期准备工作 1、注册 Cloud Studio 2、初始化工作空间 三、开发一个简版的点餐系统页面 1、安装依赖 1.1、安装 antd-mobile 1.2、安装 less 和 less-loader 1.3、暴露 webpack 配置文件 1.4、安…

FPGA设计时序分析三、恢复/去除时间

目录 一、背景说明 二、工程设计 2.1 工程代码 2.2 综合结果 一、背景说明 ​恢复时间recovery和去除时间removal和setup、holdup类型&#xff0c;不同点是数据信号为控制信号&#xff0c;如复位&#xff0c;清零&#xff0c;使能信号&#xff0c;更多的是异步的复位信号&a…

代理模式——对象的间接访问

1、简介 1.1、概述 由于某些原因&#xff0c;客户端不想或不能直接访问某个对象&#xff0c;此时可以通过一个被称为“代理”的第三者来实现间接访问&#xff0c;该方案对应的设计模式被称为代理模式。 代理模式是一种应用很广泛的结构型设计模式&#xff0c;而且变化很多。…

使用MyBatis(2)

目录 一、定义接口、实体类、创建XML文件实现接口&#xff09; 二、MyBatis的增删改查 &#x1f345;1、MyBatis传递参数查询 &#x1f388;写法一 &#x1f388;写法二 &#x1f388;两种方式的区别 &#x1f345;2、删除操作 &#x1f345;3、根据id修改用户名 &#x…

WPF线程使用详解:提升应用性能和响应能力

在WPF应用程序开发中&#xff0c;线程的合理使用是保证应用性能和响应能力的关键。WPF提供了多种线程处理方式&#xff0c;包括UI线程、后台线程、Task/Async Await和BackgroundWorker。这些方式与传统的Thread类相比&#xff0c;更加适用于WPF框架&#xff0c;并能够简化线程操…

使用pikachu管理工具下的XSS后台进行实战

写在前面的重要提示&#xff1a; Attention&#xff1a;技术没有好坏之分&#xff0c;关键在于使用技术的人或组织。网络安全技术是一把双刃剑 – 作为网络安全人&#xff0c;虽然无法控制头上的帽子是否会变绿&#xff0c;但能控制不让它变黑&#xff1b;无论我们在物质上面对…

深度学习实践——卷积神经网络实践:裂缝识别

深度学习实践——卷积神经网络实践&#xff1a;裂缝识别 系列实验 深度学习实践——卷积神经网络实践&#xff1a;裂缝识别 深度学习实践——循环神经网络实践 深度学习实践——模型部署优化实践 深度学习实践——模型推理优化练习 深度学习实践——卷积神经网络实践&#xff…

VUE之VueRouter页面跳转

参考资料&#xff1a; 参考视频 参考demo及视频资料 VUE之基本部署及VScode常用插件 VUE之基本组成和使用 VUE之Bootstrap和Element-UI的使用 VUE之axios使用&#xff0c;跨域问题&#xff0c;拦截器添加Token Vue Router官网 Vue Router说明&#xff1a; 说明&#xf…

【计算机网络】10、ethtool

文章目录 一、ethtool1.1 常见操作1.1.1 展示设备属性1.1.2 改变网卡属性1.1.2.1 Auto-negotiation1.1.2.2 Speed 1.1.3 展示网卡驱动设置1.1.4 只展示 Auto-negotiation, RX and TX1.1.5 展示统计1.1.7 排除网络故障1.1.8 通过网口的 LED 区分网卡1.1.9 持久化配置&#xff08…

详细介绍 React 中如何使用 redux

在使用之前要先了解它的配套插件&#xff1a; 在React中使用redux&#xff0c;官方要求安装其他插件 Redux Toolkit 和 react-redux Redux Toolkit&#xff1a;它是一个官方推荐的工具集&#xff0c;旨在简化 Redux 的使用和管理。Redux Toolkit 提供了一些提高开发效率的工具…

GO语言日志切割 + 记录调用源

准备工作 日志记录对程序排查问题比较关键&#xff0c;记录下GO中日志选择&#xff0c;从以下出发点考虑&#xff1a; 日志文件能自动切割&#xff0c;以免过大能记录从哪个文件哪行代码调用的&#xff0c;方便排查问题配置简单明了库文件使用人数较多&#xff0c;稳定 经过一段…

ChatIE:通过多轮问答问题实现实命名实体识别和关系事件的零样本信息抽取,并在NYT11-HRL等数据集上超过了全监督模型

项目设计集合&#xff08;人工智能方向&#xff09;&#xff1a;助力新人快速实战掌握技能、自主完成项目设计升级&#xff0c;提升自身的硬实力&#xff08;不仅限NLP、知识图谱、计算机视觉等领域&#xff09;&#xff1a;汇总有意义的项目设计集合&#xff0c;助力新人快速实…

python读取json文件

import json# 文件路径(同目录文件名即可,不同目录需要绝对路径) path 1.json# 读取JSON文件 with open(path, r, encodingutf-8) as file:data json.load(file)#data为字典 print(data) print(type(data))

文件上传

js绕过 打开网页尝试上传一句话木马&#xff0c;发现只能上传图片文件 审计源代码&#xff0c;发现使用一个checkfile函数js对文件类型进行了屏蔽 于是我们修改网页代码&#xff0c;去除返回值的检查函数 checkFile() 上传成功&#xff0c;使用蚁剑连接 连接成功 .htaccess绕…

element-ui使用动态渲染下拉选择框el-select已经选择的下拉框的值不可以重复选择让其disabled

调接口拿到下拉框数据的数据的时候将其disabled全为true 但是如果编辑的时候就需要与详情接口对比&#xff0c;如果有id一致就将disabled为true if (res.code 0) {if (this.dialogtitle "新增合同") {res.data.map((v) > {v.nameUnitVoList.forEach((item) >…

小程序新渲染引擎 Skyline 发布正式版

为了进一步提升小程序的渲染性能和体验&#xff0c;我们推出了一套新渲染引擎 Skyline&#xff0c;现在&#xff0c;跟随着基础库 3.0.0 发布 Skyline 正式版。 我们知道&#xff0c;小程序一直用 WebView 来渲染界面&#xff0c;因其有不错的兼容性和丰富的特性&#xff0c;且…

lc209.长度最小的子数组

暴力破解&#xff1a;二次for循环遍历num[i]...num[j]&#xff0c;记录满足条件的最小长度 前缀和二分&#xff1a;前缀和降低计算num[i]...num[j]的时间复杂度 对前缀和数组中的每个数进行遍历&#xff0c;找到距离这个数满足条件的最小长度 前缀和数组单调递增&#xff0c;此…