初识Redis · set和zset

目录

前言:

set

基本命令

交集并集差集

内部编码和应用场景

zset

基本命令

交集并集差集

内部编码和应用场景

应用场景(AI生成)

排行榜系统

应用背景

设计思路

热榜系统

应用背景

设计思路

热度计算方式

总结对比表


前言:

在前几篇Redis的基本数据类型中,我们已经了解了string list hash,并且了解了对应的内部编码和实际的应用场景,也是对于基本的命令操作花了比较多的文字描述的去介绍。那么在本文呢,既然有了前几个类型多个基础,我们学习set和zset也是会比较轻松的了。

废话不多说,我们直接进入主题吧。


set

基本命令

首先,set它是代表集合的意思,那么对于它来说,存在的几个特点有:无序的,不可重复的,那么因为无序,它也就不存在索引的特点。

既然是集合,我们曾经在高中阶段,或者是大学阶段均接触过集合的概念,对于集合来说有的基本操作有交集并集差集,在Redis中也存在这几个操作,所以这里的set虽然说和C++中的set差别比较大,但是和数学中的集合还是挺有相同点的。

sadd:给集合中添加元素,返回值为添加成功了几个元素

因为集合是去重的,所以我们重复添加元素的话,是会添加失败的。

smembers:查看集合中的元素 

sismember: 判断元素是否在集合内部

不过sismember只能判断一个元素,其实对于这种命令来说我们甚至可以通过翻译的方式判断它是什么意思,比如sismember,s代表set is的意思代表是 member代表成员,那么sismember的意思就是是否是set的成员。所以我们也没有必要专门记命令。

scard:查询set中有多少个元素

spop:随机删除一个元素

注意,这里的随机是真的随机,我们也可以通过验证的方式判断是否是随机的,第一次是1 4,第二次是1 3,不存在任何的顺序。这其实就有点意思了,随机删除。

至于为什么是随机的呢,可能也和set本身是无序的有关系吧,而且官方文档也明确说了spop是随机的,并且在源码中也是使用的生成随机数的方式的:

既然我们现在提到了无序,我们可以简单引出一个话题,即什么是有序的?

其实对于有序来说,分为有顺序和排名有序的,比如我们拿set来说,有1 2 3 4和4 1 3 2的两种情况的set都是一样的,拿list来说,1 2 3 4和 1 2 4 3的两种不是一样的,因为list是有序的,但是以上我们两种说的有序的都是指的是有顺序的。对于有顺序的话,比如排名一类的,就是有序的,但是这个有序的是指按照某种权重进行排序的,而非顺序上的有序。

以上是对有序这个词的讨论。

smove:将某个元素从source移动到destination

不过要是没有这个元素,也就返回0了。

srem:将某个元素从key中删除。

这里的rem代表的意思是remove,即移除的意思,它的返回值的意思是删除成功的元素的个数。

交集并集差集

对于集合之间的运算涉及到的命令是:sinter,sunion,sdiff,sinterstore,sunionstore,sdiffstore。

sinter:求多个集合的交集

比如sinter key key1就是求两个key之间的交集,我们也可以求多个,不过我们设置的多个集合之间并没有交集,所以什么也没有返回。

sinterstore:求多个集合之间的交集并存在一个集合中

我们倒是发现了一个有意思的点是,key2原本的数据被清空了,只剩下了求完交集之后的元素。以上两个命令的时间复杂度是O(N*M),N和M分别代表的是最小的集合元素个数和最大的集合元素个数。

sunion,sunionstore:求并集元素(并把并集的元素放在另一个集合中)。

同样,还是具备清空的效果,那么对于以上两个命令的时间复杂度是O(N),这个N是总的元素个数。

有意思的是,不管我们如何更换求交集或者并集的set的次序,结果都是不会改变的,而差集不一样,差集存在一定的次序问题,比如sdiff key1 key2 和sdiff key2 key1之间的结果是不同的,因为差集的定义是key1中有的而key2中没有的,反过来肯定就不一样了:

就像这样。

同理,它的时间复杂度还是O(N),因为要遍历所有的元素,但是具体怎么实现的,就是Redis内部源码的实现了,我们后面更新。

以上就是Redis中set的基本命令了。

内部编码和应用场景

对于set来说,它内部的编码方式分为了两种,一种是intset一种是hashtable,如果set内部的元素是整数并且数据个数不多的就是使用的intset,如果是字符串一类的,那么就是hashtable了:

而且我们也能发现,内部编码方式一旦确定了,也是不太好轻易发生修改的。

它的应用场景的话也是非常显然的,比如使用QQ的时候,我们经常会收到某某某和你有多少个共同好友,是否加他为好友?这其实就是set求交集的结果,set也可以用来保存用户的标签,而用户的标签是多样化的,因为千人千面嘛,所以对于用户数据来说很多都是公司是共享的,比如A软件的用户数据有青年,18岁,喜欢看美女,B软件的用户数据有青年,18岁,喜欢跑车,那么就有一种业务是让两个公司对接一下用户数据。

这样用户的互联网画像也就是越来越完整了。

还有一个非常经典的应用场景是:用Set统计PV和UV数据。同学可自行下来探索~


zset

基本命令

对于zset和set来说,zset的特点是有序的,这里的有序代表的就是用权重来进行排序了

就像这张表一样,不同的三国猛将用武将值来进行排序,这样就构成了一个zset,不过因为引入了分数的概念,那么对于zset来说,它的命令操作自然就要复杂的多了。

不过我们首先引入一个问题,因为zset中元素是不允许重复的,分数是可以重复的,如果分数重复了,如何进行排序呢?实际上就按照元素的字典序来进行排序

既然引入了分数的概念,我们需要认识到一个点是zset存储的是元素,对于分数来说,它只是一个辅助工具而已。

zadd:向集合添加元素,不过这里涉及到了一些选项,比如NX|XX GT|LT INCR等 。

我们一个一个来,先是最普通的应用:

我们插入了之后,可以通过zrange查看,其中如果要带有分数的查看,就加上withscores就行了,这里默认的是升序,咱们虽然说zset是有序的,有序无非是升序和降序,对于zset来说默认的就是升序了。

对于选项,NX和XX 与之前我们学习string的时候有点差别

string是对key存在性的判断,zset是对成员存在性的判断。

因为两个成员都存在,所以自然就添加失败,对于XX来说,就更像是一种更新了:

而对于zadd来说它的返回值是新增了的元素个数(并不包括更新的元素)

对于LT和GT官方的描述是这样的,如果要更新元素,那么LT代表是less than,GT代表的是greater than,如果分数小于原来的就更新或者分数如果大于原来的就更新。

不过不幸的是,GT和LT是6.2之后才有的,我们的版本没到那里,所以我们先了解一下。

接着是CH,它的作用就是更改返回值,因为zadd的返回值是只返回新增加的元素,对于更新的元素是不管的,那么CH就是加上了更新的元素

最后是incr,它其实就是用来单个增加分数的,和之前的incr hincr没啥区别: 

它类似的有这个命令

但是zadd已经可以完成了。

这个命令的时间复杂度是logN:

它不像之前的hash list set一样的时间复杂度为O(1),它因为是有序的,并且要找到对应的位置,所以在它内部实现的时候,使用跳表利用有序的特点找到对应的位置。

zcard:查看集合中的所有元素

返回值是有几个元素。

但是因为引入了分数的概念,所以我们也可以使用zcount指定区间查看对应的元素个数:

那么我们想用zcount实现成zcard的效果,我们就可以:

使用inf,inf代表的是无穷大,那么我们指定闭区间为负无穷大到无穷大,就可以完成所有元素的遍历。

它的时间复杂度是O(logN)其中主要是为了找到Min和max对应的位置,然后因为zset内部会记录每个元素当前的次序,找到了之后做个减法,就可以得到对应的结果了。

不过这里有一个比较反人类的设定,如果我们想要设置为开区间,就在想要设置为开区间的元素前面加一个(即可:

zrevrange:逆序遍历,这个是对上面zrange的补充。

但是对应的索引是不变的。

既然我们可以通过索引来查看元素,我们是否也可以通过分数来查看呢?

使用命令zrangebyscore即可

zpopmax:删除最高分数的count个元素

首先我们先记住,它的时间复杂度是O(M * logN),其中N是key中的总元素,M是count的,我们有基础的话,很难不去想对于删除一个有序的特殊位置,比如尾部,它的时间复杂度是logN而不是O(1),这就让人有点疑惑了,因为按照Redis的技术是完全可以的,但是咱也不知道为啥没有优化,可能是技术人员认为优化这里完全没有必要,因为LogN已经很快了,所以在zpopmax的内部还是调用的通用的删除函数:

Bzpopmax:按照阻塞的方式删除最高分数的count个元素

和前面学习的blpop一摸一样的,它也是能够一次性检测多个key。它的时间复杂度是O(M * logN),这个M不是监测了几个key的M,而是在key上删除了元素的key个数。

zpopmin和bzpopmin:(按照阻塞的方式)删除最低分数的count个元素,因为用法几乎是一样的,所以咱在这里啊 也就不演示了。

zrank和zrevrank:从前往后(从后往前)计算对应的排名,对应的时间复杂度是O(logN)

zscore:返回对应member的分数,时间复杂度是O(1),这里的话就是Redis针对进行特殊的优化的,是采取了空间换时间的做法

zrem和zremrangebyrank:(根据排名)删除元素

有趣的是,根据排名的时候是可以使用负数的,-1代表的就是最后一名,不过用法还是从前往后的,时间复杂度是logN + M,其中N是成员总数,M是区间个数。

zincrby:调整某个元素的分数

但是改变了分数之后,整体还是会保持升序的。

交集并集差集

这里涉及到的命令有:zunion,zinter,zdiff,不过这三个命令都是6.2版本才开始支持,我们这里就暂时不讨论,我们这里讨论两个命令,一个是zinterstore,一个是zunionstore

destination代表的存储到这个key里面,numbers代表的是有几个集合参与运算,weights代表的是权重,虽然说是不同的key进行运算,但是根据实际情况不同的key自己的份量不同,所以运算的时候分数会乘上我们给定的权重,然后就是合并的时候,是总和呢还是最小的还是最大的,就是最后一个参数了,默认是按照总和。

这里使用到了numkeys来指定集合数,就非常像我们之前学习http的时候,报头里面有一个字段是正文部分的长度,如果这里出现了问题,就会导致粘包问题。

就像这样。

主要涉及到的还是score的计算,那你说,zunionstore的使用是不是一样的?完全一样嘛,所以这里就不演示了。

内部编码和应用场景

它的内部编码方式有ziplist和skiplist,如果元素较少,单个元素体积小,就使用ziplist,反之就使用skiplist,不过对于这里的ziplist还是使用的空间换取时间的做法,自然就不敢元素多了还用ziplist了。

明白啦,下面是为博客撰写的纯文字版内容,格式整洁、内容完整,可直接复制粘贴上传到博客平台(如 CSDN、掘金、个人博客等):


应用场景(AI生成)

排行榜系统

应用背景

排行榜常用于游戏、竞赛、学习平台等系统中,主要用来展示用户在某项指标上的相对排名,例如积分榜、活跃度榜、胜率榜等。系统要求能够快速地更新用户得分、获取前几名用户,以及查询某个用户的实时排名。

设计思路

在排行榜中,每个用户对应一个唯一标识(如用户 ID),而其得分则用于排序依据。Redis 的有序集合能够很好地完成这项任务。它会根据用户得分自动维持从低到高的顺序,同时支持查询、更新、分页等操作。

为了方便扩展,不同类型的排行榜可以用不同的 key 名进行管理,比如设置日榜、周榜、总榜等。

热榜系统

应用背景

在内容平台或社区系统中,文章、帖子或视频的“热度”是衡量内容受欢迎程度的重要指标。平台通常会根据内容的浏览量、点赞数、评论量等因素,计算一个热度得分,并据此展示热门内容排行榜。

与普通排行榜不同,文章热度往往涉及多维因素,还可能加入时间衰减,以保持榜单的新鲜感,避免长期霸榜。

设计思路

在 Redis 中,可以将每篇文章的唯一标识作为 ZSet 成员,将计算后的热度值作为排序依据。每当文章发生交互行为时(例如点赞、被浏览、被评论等),系统即可对其热度进行更新。

为了支持多分类内容推荐,也可以为不同的内容类别创建独立的热度榜,例如科技类、娱乐类、教育类等,每个分类对应一个有序集合。

热度计算方式

热度得分通常不是单一数据,而是由多个指标加权计算而来。例如,可以设定一个公式,综合点击量、点赞量、评论量等数据;还可以为旧文章引入时间衰减机制,防止内容因早期流量过大而长期占据榜单。

最终热度值由业务逻辑层计算完成,再写入 Redis 中的有序集合。


总结对比表

功能需求排行榜系统文章热度榜
目标成员用户 ID文章 ID
分数含义积分、活跃度热度(阅读、点赞等加权计算)
更新频率实时(如游戏加分)实时或周期更新(如点赞、点击)
查询方式Top N、用户排名、分页热榜分页、热度值查询
清理策略删除低排名用户,节省空间删除冷文章,保持热榜实时性

感谢阅读!

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

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

相关文章

playwright 教程高级篇:掌握网页自动化与验证码处理等关键技术详解

Playwright 教程高级篇:掌握网页自动化与验证码处理等关键技术详解 本教程将带您一步步学习如何使用 Playwright——一个强大的浏览器自动化工具,来完成网页任务,例如提交链接并处理旋转验证码。我们将按照典型的自动化流程顺序,从启动浏览器到关闭浏览器,详细讲解每个步骤…

数据结构(完)

树 二叉树 构建二叉树 int value;Node left;Node right;public Node(int val) {valueval;} 节点的添加 Node rootnull;public void insert(int num) {Node nodenew Node(num);if(rootnull) {rootnode;return;}Node index root;while(true) {//插入的节点值小if(index.value&g…

FastAPI与SQLAlchemy数据库集成与CRUD操作

title: FastAPI与SQLAlchemy数据库集成与CRUD操作 date: 2025/04/16 09:50:57 updated: 2025/04/16 09:50:57 author: cmdragon excerpt: FastAPI与SQLAlchemy集成基础包括环境准备、数据库连接配置和模型定义。CRUD操作通过数据访问层封装和路由层实现,确保线程安全和事务…

一个基于Django的写字楼管理系统实现方案

一个基于Django的写字楼管理系统实现方案 用户现在需要我用Django来编写一个写字楼管理系统的Web版本,要求包括增删改查写字楼的HTML页面,视频管理功能,本地化部署,以及人员权限管理,包含完整的代码结构和功能实现&am…

mongodb在window10中创建副本集的方法,以及node.js连接副本集的方法

创建Mongodb的副本集最好是新建一个文件夹,如D:/data,不要在mongodb安装文件夹里面创建副本集,虽然这样也可以,但是容易造成误操作或路径混乱;在新建文件夹里与现有 MongoDB 数据隔离,避免误操作影响原有数…

Maven 多仓库与镜像配置全攻略:从原理到企业级实践

Maven 多仓库与镜像配置全攻略:从原理到企业级实践 一、核心概念:Repository 与 Mirror 的本质差异 在 Maven 依赖管理体系中,repository与mirror是构建可靠依赖解析链的两大核心组件,其核心区别如下: 1. Repositor…

STM32 四足机器人常见问题汇总

文章不介绍具体参数,有需求可去网上搜索。 特别声明:不论年龄,不看学历。既然你对这个领域的东西感兴趣,就应该不断培养自己提出问题、思考问题、探索答案的能力。 提出问题:提出问题时,应说明是哪款产品&a…

MySQL 中 `${}` 和 `#{}` 占位符详解及面试高频考点

文章目录 一、概述二、#{} 和 ${} 的核心区别1. 底层机制代码示例 2. 核心区别总结 三、为什么表名只能用 ${}?1. 预编译机制的限制2. 动态表名的实现 四、安全性注意事项1. ${} 的风险场景2. 安全实践 五、面试高频考点1. 基础原理类问题**问题 1**:**问…

C语言编译预处理2

#include <XXXX.h>和#include <XXXX.c> #include "XXXX.h" 是 C 语言中一条预处理指令 #include <XXXX.h>&#xff1a;这种形式用于包含系统标准库的头文件。预处理器会在系统默认的头文件搜索路径中查找XXXX.h 文件。例如在 Linux 系统中&#…

Elasticvue-轻量级Elasticsearch可视化管理工具

Elasticvue一个免费且开源的 Elasticsearch 在线可视化客户端&#xff0c;用于管理 Elasticsearch 集群中的数据&#xff0c;完全支持 Elasticsearch 版本 8.x 和 7.x. 功能特色&#xff1a; 集群概览索引和别名管理分片管理搜索和编辑文档REST 查询快照和存储库管理支持国际…

Git提交规范及最佳实践

Git 提交规范通常是为了提高代码提交的可读性、可维护性和自动化效率&#xff08;如生成 ChangeLog&#xff09;。以下是常见的 Conventional Commits 规范&#xff0c;结合社区最佳实践总结而成&#xff1a; 1. 提交格式 每次提交的 commit message 应包含三部分&#xff1a;…

Ubuntu中snap

通过Snap可以安装众多的软件包。需要注意的是&#xff0c;snap是一种全新的软件包管理方式&#xff0c;它类似一个容器拥有一个应用程序所有的文件和库&#xff0c;各个应用程序之间完全独立。所以使用snap包的好处就是它解决了应用程序之间的依赖问题&#xff0c;使应用程序之…

android studio 运行java main报错

运行某个带main函数的java文件报错 Could not create task :app:Test.main(). > SourceSet with name main not found. 解决办法&#xff1a;在工程的.idea/gradle.xml 文件下添加&#xff1a; <option name"delegatedBuild" value"false" /&g…

openssh离线一键升级脚本分享(含安装包)

查看当前的版本 [rootmyoracle ~]#ssh -V相关安装包下载地址 openssh下载地址&#xff1a;http://ftp.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssl下载地址&#xff1a;https://www.openssl.org/source/zlib下载地址&#xff1a;http://www.zlib.net/今天演示从7.4升级…

Mac M1管理多个Node.js版本

目录 1. 使用 nvm (Node Version Manager) 1.1.安装 nvm 1.2.安装Node.js版本 1.3.查看已安装的node版本列表 1.4.使用特定版本的Node.js 1.5.查看当前使用的版本 2. 使用 fnm (Fast Node Manager) 2.1.安装 fnm 2.2.安装Node.js版本 2.3.查看已安装的版本 2.4.使用…

Unity中国战略调整简讯:Unity6下架 团结引擎接棒

Unity中国战略调整简讯&#xff1a;Unity6下架 团结引擎接棒 免费版 2025年4月9日 —— Unity中国宣布自即日起&#xff0c;中国大陆及港澳地区停止提供Unity 6及后续版本下载与服务&#xff0c;相关功能由国产引擎“团结引擎”承接。国际版2022 LTS及更早版本仍由Unity中国维护…

TestNG 单元测试详解

1、测试环境 jdk1.8.0 121 myeclipse-10.0-offline-installer-windows.exe TestNG 插件 org.testng.eclipse 6.8.6.20130607 0745 2、介绍 套件(suite):由一个 XML 文件表示,通过<suite>标签定义,包含一个或更多测试(test)。测试(test):由<test>定义&#xf…

C复习(主要复习)

指针和数组 指针数组是一个数组&#xff0c;数组的每个元素都是指针。它适用于需要存储多个指针的场景&#xff0c;如字符串数组。数组指针是一个指针&#xff0c;指向一个数组。它适用于需要传递整个数组给函数或处理多维数组的场景。 函数指针&#xff1a;函数指针的定义需要…

探索大语言模型(LLM):定义、发展、构建与应用

文章目录 引言大规模语言模型的基本概念大规模语言模型的发展历程1. 基础模型阶段&#xff08;2018年至2021年&#xff09;2. 能力探索阶段&#xff08;2019年至2022年&#xff09;3. 突破发展阶段&#xff08;以2022年11月ChatGPT的发布为起点&#xff09; 大规模语言模型的构…

5. k8s 之 pod原理与使用

Kubernetes Pod 原理详解 1. Pod 的部署方式 Pod 是 Kubernetes 的最小调度单元&#xff0c;其部署方式分为 声明式&#xff08;YAML&#xff09; 和 命令式&#xff08;kubectl&#xff09; 两种&#xff1a; (1) 声明式部署&#xff08;推荐&#xff09; 通过 YAML 文件定…