Redis 有序集合(Sorted Set)
Redis 有序集合(Sorted Set)
以下从基础命令、内部编码和使用场景三个维度对 Redis 有序集合进行详细解析:
一、基础命令
| 命令 | 时间复杂度 | 命令含义 |
|---|---|---|
| zadd key score member [score member …] | O ( k × l o g ( n ) ) O(k×log(n)) O(k×log(n)),k是添加成员的个数,n是当前有序集合成员个数 | 向有序集合(Sorted Set)中添加一个或多个成员,同时为每个成员关联一个分数(score),根据分数对成员进行排序 。 |
| zcard key | O(1) | 获取有序集合中成员的数量 。 |
| zscore key member | O(1) | 获取有序集合中指定成员的分数 。 |
| zrank key member | O(log(n)),n是当前有序集合成员个数 | 获取有序集合中指定成员的排名(从0开始,按分数从小到大排序) 。 |
| zrevrank key member | O(log(n)),n是当前有序集合成员个数 | 获取有序集合中指定成员的排名(从0开始,按分数从大到小排序) 。 |
| zrem key member [member …] | O(k*log(n)),k是删除成员的个数,n是当前有序集合成员个数 | 从有序集合中删除一个或多个成员 。 |
| zincrby key increment member | O(log(n)),n是当前有序集合成员个数 | 为有序集合中指定成员的分数加上指定的增量 。 |
| zrange key start end [withscores] | O(log(n)+k),k是要获取的成员个数,n是当前有序集合成员个数 | 按照分数从小到大的顺序,获取有序集合中指定区间内的成员,可选择是否返回成员的分数 。 |
| zrevrange key start end [withscores] | O(log(n)+k),k是要获取的成员个数,n是当前有序集合成员个数 | 按照分数从大到小的顺序,获取有序集合中指定区间内的成员,可选择是否返回成员的分数 。 |
| zrangebyscore key min max [withscores] [limit offset count] | O(log(n)+k),k是要获取的成员个数,n是当前有序集合成员个数 | 按照分数范围,获取有序集合中分数在指定区间内的成员,可选择是否返回成员的分数,还可通过limit指定返回结果的偏移量和数量 。 |
| zrevrangebyscore key max min [withscores] [limit offset count] | O(log(n)+k),k是要获取的成员个数,n是当前有序集合成员个数 | 按照分数范围,从大到小获取有序集合中分数在指定区间内的成员,可选择是否返回成员的分数,还可通过limit指定返回结果的偏移量和数量 。 |
| zcount key min max | O(log(n)),n是当前有序集合成员个数 | 计算有序集合中分数在指定区间内的成员数量 。 |
| zremrangebyrank key start end | O(log(n)+k),k是要删除的成员个数,n是当前有序集合成员个数 | 根据成员排名范围,从有序集合中删除指定区间内的成员 。 |
| zremrangebyscore key min max | O(log(n)+k),k是要删除的成员个数,n是当前有序集合成员个数 | 根据分数范围,从有序集合中删除分数在指定区间内的成员 。 |
有序集合的核心命令围绕元素分数(score)的增删查改、排序和集合运算展开,主要分为以下几类:
1. 元素操作
-
ZADD:添加或更新元素及其分数(支持
NX、XX、INCR 等选项)。ZADD leaderboard 100 "player1" # 添加玩家1,分数100 -
ZREM:移除指定元素。
-
ZINCRBY:增减元素的分数(如积分变动)。
ZINCRBY leaderboard 50 "player1" # 玩家1分数+50 -
ZSCORE:获取元素分数。
2. 范围查询与排序
- ZRANGE / ZREVRANGE:按分数升序/降序返回指定区间元素。
- ZRANGEBYSCORE:按分数区间筛选元素(支持开闭区间)。
- ZRANK / ZREVRANK:获取元素的正序/逆序排名。
3. 集合运算
-
ZINTERSTORE / ZUNIONSTORE:计算多个有序集合的交集/并集,结果存储到新键。
ZINTERSTORE result 2 key1 key2 # 计算key1和key2的交集
4. 统计与删除
- ZCARD:获取集合元素总数。
- ZCOUNT:统计指定分数区间内的元素数量。
- ZREMRANGEBYRANK / ZREMRANGEBYSCORE:按排名或分数区间批量删除元素。
5. 阻塞操作
- BZPOPMAX / BZPOPMIN:阻塞弹出最高/最低分元素,适用于优先级队列。
二、内部编码
Redis 根据数据规模动态选择编码方式,以平衡内存和性能:
1. ziplist(压缩列表)
-
触发条件:
- 元素数量 ≤
zset-max-ziplist-entries(默认 128)。 - 每个元素的成员(member)长度 ≤
zset-max-ziplist-value(默认 64 字节)。
- 元素数量 ≤
-
特点:
- 内存紧凑,连续存储成员和分数,无指针开销。
- 增删操作时间复杂度为 O(n),适用于小规模数据。
2. skiplist(跳表)
-
触发条件:超出 ziplist 容量限制时自动转换。
-
特点:
- 跳表+哈希表组合实现:跳表维护有序结构(O(logN) 查询),哈希表存储成员到分数的映射(O(1) 查询)。
- 支持高效范围查询和动态数据插入。
3. 编码转换规则
- 只允许从 ziplist 转为 skiplist,不可逆。
- 可通过
OBJECT ENCODING key 查看当前编码类型。
三、使用场景
有序集合凭借唯一性和有序性,适用于以下典型场景:
1. 排行榜系统
-
代码案例: 音乐排行榜 统计音乐播放量以及相关排名信息
package com.example.redis.sortedset;import redis.clients.jedis.Jedis; import redis.clients.jedis.Tuple;import java.util.Set;/*** 描述: 音乐排行榜系统* 使用 Redis 有序集合(Sorted Set)存储歌曲排行榜,key 为 RANKING_KEY(例如:"music:ranking")。* 每个元素的 score 用于表示歌播放量、点赞数等,可以根据具体业务情况计算。* 使用 zadd 命令添加或更新歌曲得分,zadd 如果已有成员会更新其得分,保证同一首歌曲不会重复添加。* clickPlay 方法,每次点击播放时使用 jedis.zincrby(RANKING_KEY, 1, songId) 将歌曲的播放量加 1。* 使用 zrevrange 按分数降序查询排行榜,从而获取热门歌曲;同时可使用 zrevrank、zscore 获取歌曲排名和得分。* 示例中 main 函数模拟了添加/更新操作,并展示了如何获取排行榜、查询具体歌曲信息等操作* @author ZHOUXIAOYUE* @date 2025/4/21 15:19*/ public class MusicRankingSystem {// 定义排行榜对应的 Redis keyprivate static final String RANKING_KEY = "music:ranking";private Jedis jedis;public MusicRankingSystem() {// 连接 Redis 服务,默认地址 localhost:6379jedis = new Jedis("localhost", 6379);}/*** 添加歌曲到排行榜* 如果歌曲已存在,则不会重复添加,初始播放量可为 0(或指定值)** @param songId 歌曲 ID* @param playCount 初始播放量*/public void addSong(String songId, double playCount) {// 判断歌曲是否已存在if (jedis.zscore(RANKING_KEY, songId) == null) {jedis.zadd(RANKING_KEY, playCount, songId);System.out.println("添加歌曲 " + songId + ",初始播放量为:" + playCount);} else {System.out.println("歌曲 " + songId + " 已存在,当前播放量为:" + jedis.zscore(RANKING_KEY, songId));}}/*** 模拟点击播放歌曲,每点击一次歌曲播放量自动+1** @param songId 歌曲 ID*/public void clickPlay(String songId) {// 使用 zincrby 实现播放量自动加 1double newPlayCount = jedis.zincrby(RANKING_KEY, 1, songId);System.out.println("歌曲 " + songId + " 播放一次,当前播放量为:" + newPlayCount);}/*** 获取排行榜前 N 名的歌曲(播放量从高到低排序)** @param top 前 N 名* @return 歌曲 ID 集合*/public Set<String> getTopSongs(int top) {// 使用 zrevrange 按播放量从高到低排序Set<String> topSongs = jedis.zrevrange(RANKING_KEY, 0, top - 1);System.out.println("排行榜前 " + top + " 名:" + topSongs);return topSongs;}/*** 获取某个歌曲的排名(排名从 1 开始,播放量越高排名越靠前)** @param songId 歌曲 ID* @return 歌曲排名,歌曲不存在返回 -1*/public long getSongRank(String songId) {Long rank = jedis.zrevrank(RANKING_KEY, songId);if (rank == null) {System.out.println("歌曲 " + songId + " 不在排行榜中。");return -1;} else {System.out.println("歌曲 " + songId + " 的排名是:" + (rank + 1));return rank + 1;}}/*** 获取某个歌曲的播放量** @param songId 歌曲 ID* @return 播放量*/public double getSongPlayCount(String songId) {Double playCount = jedis.zscore(RANKING_KEY, songId);System.out.println("歌曲 " + songId + " 的播放量为:" + playCount);return playCount == null ? 0 : playCount;}/*** 获取排行榜中指定区间的歌曲及播放量** @param start 区间开始索引(从 0 开始)* @param end 区间结束索引*/public void getSongsWithPlayCount(int start, int end) {Set<Tuple> tuples = jedis.zrevrangeWithScores(RANKING_KEY, start, end);System.out.println("排行榜区间 [" + start + ", " + end + "] 内的歌曲信息:");for (Tuple tuple : tuples) {System.out.println("歌曲 ID:" + tuple.getElement() + ",播放量:" + tuple.getScore());}}/*** 关闭 Jedis 连接*/public void close() {if (jedis != null) {jedis.close();}}public static void main(String[] args) {MusicRankingSystem rankingSystem = new MusicRankingSystem();// 添加几首歌曲到排行榜,初始播放量设置为 0rankingSystem.addSong("song:101", 0);rankingSystem.addSong("song:102", 0);rankingSystem.addSong("song:103", 0);rankingSystem.addSong("song:104", 0);rankingSystem.addSong("song:105", 0);// 模拟点击播放,不同歌曲点击次数不同rankingSystem.clickPlay("song:101");rankingSystem.clickPlay("song:102");rankingSystem.clickPlay("song:101");rankingSystem.clickPlay("song:103");rankingSystem.clickPlay("song:102");rankingSystem.clickPlay("song:101");rankingSystem.clickPlay("song:104");rankingSystem.clickPlay("song:105");rankingSystem.clickPlay("song:103");rankingSystem.clickPlay("song:105");// 获取排行榜前 3 名的歌曲rankingSystem.getTopSongs(3);// 获取指定歌曲的排名与播放量rankingSystem.getSongRank("song:101");rankingSystem.getSongPlayCount("song:101");// 获取排行榜中索引 0~4 区间内的歌曲信息rankingSystem.getSongsWithPlayCount(0, 4);// 关闭连接rankingSystem.close();} } -
实现:以用户积分作为分数,利用
ZREVRANGE 获取排名前 N 的用户。ZREVRANGE leaderboard 0 9 WITHSCORES # 获取Top10玩家及分数
2. 优先级队列
- 实现:任务分数表示优先级,通过
BZPOPMAX 优先处理高优先级任务。
3. 时间轴/事件排序
-
实现:以时间戳为分数,存储用户行为日志,按时间范围查询。
ZADD user:1001:actions 1620000000 "login" ZRANGEBYSCORE user:1001:actions 1620000000 1620003600 # 查询某时间段行为
4. 动态统计与过滤
-
实现:统计分数区间内元素(如商品价格区间)。
ZCOUNT products 100 500 # 统计价格在100-500的商品数量
5. 数据去重与聚合
- 实现:通过
ZINTERSTORE 计算用户兴趣标签的交集。
四、调优建议
-
内存优化:
- 小规模数据优先使用 ziplist,调整
zset-max-ziplist-entries 和zset-max-ziplist-value 参数。
- 小规模数据优先使用 ziplist,调整
-
性能优化:
- 高频写入场景建议增大 ziplist 阈值,减少跳表转换开销。
-
查询优化:
- 避免全量遍历(如
ZRANGE 0 -1),改用分页或迭代器(ZSCAN)。
- 避免全量遍历(如
相关文章:
Redis 有序集合(Sorted Set)
Redis 有序集合(Sorted Set) 以下从基础命令、内部编码和使用场景三个维度对 Redis 有序集合进行详细解析: 一、基础命令 命令时间复杂度命令含义zadd key score member [score member …] O ( k l o g ( n ) ) O(klog(n)) O(klog(n))&…...
[c语言日寄]免费文档生成器——Doxygen在c语言程序中的使用
【作者主页】siy2333 【专栏介绍】⌈c语言日寄⌋:这是一个专注于C语言刷题的专栏,精选题目,搭配详细题解、拓展算法。从基础语法到复杂算法,题目涉及的知识点全面覆盖,助力你系统提升。无论你是初学者,还是…...
QtCreator的设计器、预览功能能看到程序图标,编译运行后图标消失
重新更换虚拟机(Vmware Kylin),重新编译和配置了很多第三方库后,将代码跑到新的这个虚拟机环境中,但是出现程序图标不可见,占位也消失,后来继续检查ui文件,ui文件图标也异常&#x…...
QT文件和文件夹拷贝操作
1.拷贝文件夹 //(源文件目录路劲,目的文件目录,文件存在是否覆盖) bool copyDirectory(const QString& srcPath, const QString& dstPath, bool coverFileIfExist) { QDir srcDir(srcPath); QDir dstDir(dstPath); if (!dstDir.exi…...
面试常用基础算法
目录 快速排序归并排序堆排序 n n n皇后问题最大和子数组爬楼梯中心扩展法求最长回文子序列分割回文串动态规划求最长回文子序列最长回文子串单调栈双指针算法修改 分割回文串滑动窗口栈 快速排序 #include <iostream> #include <algorithm>using namespace std;…...
Python-24:小R的随机播放顺序
问题描述 小R有一个特殊的随机播放规则。他首先播放歌单中的第一首歌,播放后将其从歌单中移除。如果歌单中还有歌曲,则会将当前第一首歌移到最后一首。这个过程会一直重复,直到歌单中没有任何歌曲。 例如,给定歌单 [5, 3, 2, 1,…...
悬空引用和之道、之禅-《分析模式》漫谈57
DDD领域驱动设计批评文集 做强化自测题获得“软件方法建模师”称号 《软件方法》各章合集 “Analysis Patterns”的第5章“对象引用”原文: Unless you can catch all such references, there is the risk of a dangling reference, which often has painful con…...
Python accumulate 函数详解
https://docs.python.org/zh-cn/3/library/itertools.html#itertools.accumulate 在 Python 中,accumulate 是一个生成器(generator), 是来自 itertools 模块的一个函数。 它的作用是返回一个迭代器,该迭代器生成输入数据的累积结…...
Cursor可视化大屏搭建__0420
主题:用Cursor怎么进行数据洞察,做AI预测化内容。 Python基础语法与AI python生态强大,代码简洁,相对其他语言Python更好上手,浙江高考将Python列为可选科目 科学计算:Sklearn,Numpy,Pandas 人工智能:Tensorflow,Pytorch 网络爬虫:Scrapy…...
【初阶数据结构】树——二叉树(上)
文章目录 目录 前言 一、树 1.树的概念与结构 2.树相关术语 3.树的表示 二、二叉树 1.概念与结构 2.特殊的二叉树 3.二叉树存储结构 总结 前言 本篇带大家学习一种非线性数据结构——树,简单认识树和二叉数以及了解二叉树的存储结构。 一、树 1.树的概念与结构 树…...
ECharts散点图-散点图14,附视频讲解与代码下载
引言: ECharts散点图是一种常见的数据可视化图表类型,它通过在二维坐标系或其它坐标系中绘制散乱的点来展示数据之间的关系。本文将详细介绍如何使用ECharts库实现一个散点图,包括图表效果预览、视频讲解及代码下载,让你轻松掌握…...
C++中的算术转换、其他隐式类型转换和显示转换详解
C中的类型转换(Type Conversion)是指将一个数据类型的值转换为另一个数据类型的过程,主要包括: 一、算术类型转换(Arithmetic Conversions) 算术类型转换通常发生在算术运算或比较中,称为**“标…...
GAIA-2:用于自动驾驶的可控多视图生成世界模型
25年3月来自英国创业公司 Wayze 的论文“GAIA-2: A Controllable Multi-View Generative World Model for Autonomous Driving”。(注:23年9月其发布GAIA-1) 生成模型为模拟复杂环境提供一种可扩展且灵活的范例,但目前的方法不足…...
(一)CMake / MsBuild Ninja Make/ MSVC g++ clang++ 等c++编译概念解释
c 几个编译概念 一 概念二 层级关系总结2.1层级表格2.2 关键点说明2.3 示例流程(以 Ninja 为例)2.4 示例流程(Windows 平台) 三 总结 一 概念 CMake 通过 CMakeLists.txt 生成不同平台的构建文件(如 .sln、build.n…...
创建 Node.js Playwright 项目:从零开始搭建自动化测试环境
一、环境准备 在开始创建 Playwright 项目之前,确保你的电脑上已经安装了以下工具: Node.js:Playwright 依赖于 Node.js 环境,确保你已经安装了最新版本的 Node.js。可以通过以下命令检查是否安装成功: node -v npm -…...
浅谈AI致幻
文章目录 当前形势下存在的AI幻觉(AI致幻)什么是AI幻觉AI幻觉的类型为什么AI会产生幻觉AI幻觉的危害与影响当前应对AI幻觉的技术与方法行业与学术界的最新进展未来挑战与展望结论 当前形势下存在的AI幻觉(AI致幻) 什么是AI幻觉 …...
postman乘法计算,变量赋值
postman脚本怎么计算乘法 在Postman中,你可以通过多种方式计算乘法,这取决于你的具体需求。例如,如果你想在发送请求前计算乘法结果,或者在测试标签中计算响应数据的乘法,下面是一些常见的方法。 1. 使用JavaScript代…...
自定义错误码的必要性
为什么要使用错误码,直接返回一个错误信息不好么? 下面介绍一下,在程序开发中使用错误码的必要性~ 便于排查问题 想象你开了一家奶茶店,顾客下单后可能出现各种问题: 没珍珠了(错误码:50…...
车载软件架构 --- 二级boot设计说明需求规范
我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 周末洗了一个澡,换了一身衣服,出了门却不知道去哪儿,不知道去找谁,漫无目的走着,大概这就是成年人最深的孤独吧! 旧人不知我近况,新人不知我过…...
管理杂谈——采石矶大捷的传奇与启示
南宋抗金史上,岳飞与岳家军的铁血传奇家喻户晓,但另一位力挽狂澜的“文官战神”却常被忽视——他从未掌兵,却在南宋存亡之际整合溃军,以少胜多,缔造采石矶大捷。此人正是虞允文。一介书生何以扭转乾坤?他的…...
Java高效合并Excel报表实战:GcExcel让数据处理更简单
前言:为什么需要自动化合并Excel? 在日常办公场景中,Excel报表合并是数据分析的基础操作。根据2023年企业办公效率报告显示: 财务人员平均每周花费6.2小时在Excel合并操作上人工合并的错误率高达15%90%的中大型企业已采用自动化…...
第十四届蓝桥杯 2023 C/C++组 平方差
目录 题目: 题目描述: 题目链接: 思路: 核心思路: 第一种思路: 第二种思路: 坑点: 代码: 数学找规律 O(n) 50分代码详解: O(1)满分代码详解&#x…...
前端路由缓存实现
vue3缓存实现完整版,查看这篇设计和实现方式吧,更完整...
I/O复用函数的使用——select
I/O复用函数的使用——select 目录 一、概念 二、select接口 2.1 基础概念 2.2 使用 select 函数的标准输入读取代码 2.3 基于 select 模型的多客户端 TCP 服务器实现 一、概念 i/o复用使得程序能同时监听多个文件描述符,可以提高程序性能。 之前为了让服务器能…...
ubuntu20.04安装安装x11vnc服务基于gdm3或lightdm这两种主流的显示管理器。
前言:在服务端安装vnc服务,可以方便的远程操作服务器,而不用非要插上显示器才行。所以在服务器上安装vnc是很重要的。在ubuntu20中,默认的显示管理器已经变为gdm3,它可以带来与 GNOME 无缝衔接的体验,强调功…...
图像预处理-图像轮廓特征查找
其实就是外接轮廓,有了轮廓点就可以找到最上、最下、最左、最右的四个坐标(因为有xmin,xmax,ymin,ymax)。就可以绘制出矩形。 一.外接矩形 cv.boundingRect(轮廓点) - 返回x,y,w,h,传入一个轮廓的轮廓点,若有多个轮廓需…...
全同态加密医疗数据分析集python实现
目录 摘要一、前言二、全同态加密与医疗数据分析概述2.1 全同态加密(FHE)简介2.2 医疗数据分析需求三、数据生成与预处理四、系统架构与流程4.1 系统架构图五、核心数学公式六、异步任务调度与(可选)GPU 加速七、PyQt6 GUI 设计八、完整代码实现九、自查测试与总结十、展望…...
list的学习
list的介绍 list文档的介绍 list是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代。list的底层是双向链表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向其前一个元素和后一…...
HarmonyOS:Navigation实现导航之页面设置和路由操作
导读 设置标题栏模式设置菜单栏设置工具栏路由操作页面跳转页面返回页面替换页面删除移动页面参数获取路由拦截 子页面页面显示类型页面生命周期页面监听和查询 页面转场关闭转场自定义转场共享元素转场 跨包动态路由系统路由表自定义路由表 示例代码 Navigation组件适用于模块…...
管道位移自动化监测方案
一、背景 管道系统在区域性地质沉降作用下易形成非均匀应力场集中现象,诱发管体屈曲变形及环焊缝界面剥离等连续损伤累积效应,进而导致管道力学性能退化与临界承载能力衰减。传统人工巡检受限于空间覆盖度不足及数据采集周期长(≥72h…...
