Redis数据结构之Set

Set 类型是一个无序并唯一的键值集合,它的存储顺序不会按照插入的先后顺序进行存储。Redis 中集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是 O(1)。相对于列表,集合也有两个特点:无序、不可重复

一个集合最多可以存储 2^32-1 个元素。概念和数学中个的集合基本类似,数学集合的概念是指具有某种特定性质的具体的或抽象的对象汇总而成的集体。

简而言之,Redis 集合就是一些不重复值的组合。利用集合(Set)这个数据结构,Redis 可以存储一些集合类型的数据,Redis也通过一些简便的命令很好的支持了交集、并集和差集等集合的基本运算。


文章目录

    • @[toc]
        • 1、Set数据类型
          • 1.1、Set类型简介
          • 1.2、Set应用场景
        • 2、Set底层结构
          • 2.1、List底层结构介绍
          • 2.2、整数集合IntSet
          • 2.3、哈希表HashTable
        • 3、Set常用命令
          • 3.1、添加集合元素
          • 3.2、查看集合所有值
          • 3.3、判断一个值是否在集合中
          • 3.4、查看某集合的存值的数量
          • 3.5、删除集合中指定值的元素
          • 3.6、随机选出某集合中一个元素
          • 3.7、随机删除某集合中一个元素
          • 3.8、将一个集合中的某值移动至另一个集合
          • 3.9、集合运算:差集
          • 3.10、集合运算:交集
          • 3.11、集合运算:并集

1、Set数据类型

1.1、Set类型简介

Set 类型是一个无序并唯一的键值集合,它的存储顺序不会按照插入的先后顺序进行存储。Redis 中集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是 O(1)。相对于列表,集合也有两个特点:无序、不可重复

一个集合最多可以存储 2^32-1 个元素。概念和数学中个的集合基本类似,数学集合的概念是指具有某种特定性质的具体的或抽象的对象汇总而成的集体。

简而言之,Redis 集合就是一些不重复值的组合。利用集合(Set)这个数据结构,Redis 可以存储一些集合类型的数据,Redis也通过一些简便的命令很好的支持了交集、并集和差集等集合的基本运算。

1.2、Set应用场景

常见的应用场景有:投票系统、标签系统、共同好友、共同关注、共同爱好、抽奖、商品筛选栏,访问 IP 统计等

使用场景:

  • 点赞、踩、收藏:Set 类型可以保证一个用户只能点一个赞;
  • 共同关注、标签:Set 类型支持交集运算,所以可以用来计算共同关注的好友、公众号等;
  • 抽奖活动:存储某活动中中奖的用户名 ,Set 类型因为有去重功能,可以保证同一个用户不会中奖两次

2、Set底层结构

2.1、List底层结构介绍

Redis Set 的底层存储采用 整数集合 IntSet 和哈希表,二者是相互转换的,使用 IntSet 存储必须满足下面两个条件,否则使用 HashTable,条件如下:

  • 结合对象保存的所有元素都是整数值;
  • 集合对象保存的元素数量不超过 512 个

以 Set 的 SADD 命令为例子,整个添加过程如下:

  • 检查 Set 是否存在不存在则创建一个 Set 结合。
  • 根据传入的 Set 集合一个个进行添加,添加的时候需要进行内存压缩。
  • setTypeAdd 执行 Set 添加过程中会判断是否进行编码转换
void saddCommand(redisClient *c) {robj *set;int j, added = 0;// 取出集合对象set = lookupKeyWrite(c->db,c->argv[1]);// 对象不存在,创建一个新的,并将它关联到数据库if (set == NULL) {set = setTypeCreate(c->argv[2]);dbAdd(c->db,c->argv[1],set);// 对象存在,检查类型} else {if (set->type != REDIS_SET) {addReply(c,shared.wrongtypeerr);return;}}// 将所有输入元素添加到集合中for (j = 2; j < c->argc; j++) {c->argv[j] = tryObjectEncoding(c->argv[j]);// 只有元素未存在于集合时,才算一次成功添加if (setTypeAdd(set,c->argv[j])) added++;}// 如果有至少一个元素被成功添加,那么执行以下程序if (added) {// 发送键修改信号signalModifiedKey(c->db,c->argv[1]);// 发送事件通知notifyKeyspaceEvent(REDIS_NOTIFY_SET,"sadd",c->argv[1],c->db->id);}// 将数据库设为脏server.dirty += added;// 返回添加元素的数量addReplyLongLong(c,added);
}

稍微深入分析一下set的单个元素的添加过程,首先如果已经是 HashTable 的编码,那么我们就走正常的 HashTable 的元素添加,如果原来是 IntSet 的情况,那么我们就需要进行如下判断:

  • 如果能够转成 int 的对象(isObjectRepresentableAsLongLong),那么就用 IntSet 保存。
  • 如果用 IntSet 保存的时候,如果长度超过5 12(REDIS_SET_MAX_INTSET_ENTRIES)就转为 HashTable 编码。
  • 其他情况统一用 HashTable 进行存储。
2.2、整数集合IntSet

整数集合 IntSet 是 Redis用来保存整数值的集合的一种数据结构,可以用来保存 int 类型数据,并且可以保证不会出现重复元素。因此当一个集合中只包含整数元素且数量不多的时候,Redis 会选择使用整数集合作为底层实现。

IntSet 内部其实是一个数组(int8_t coentents[] 数组),而且存储数据的时候是有序的,因为在查找数据的时候是通过二分查找来实现的。

img

如果你的集合只有整数值元素,并且数量是轻量的,这时候 Redis 会使用使用整数集合作为 Redis 集合的底层数据结构。参考如下代码:

typedef struct IntSet{// 编码格式uint32_t encoding;// 集合中的元素个数uint32_t length;// 保存元素数据int8_t contents[];
} IntSet;

我们拆解下:

属性说明
“encoding”编码方式
“length”数组中元素个数,也就是数组的整体长度
“contents[]”整数集合,集合的每个元素都是数组的一个数组项(item)。具有特点:按值的大小增序排列、不包含任何重复项

“contents” 是整数集合的底层实现,保存了整数集合的每一个元素,每个元素在该数组中从小到大有序排列,并且不重复(如何保证有序性和唯一性我们后面讨论插入的时候在说)。“contents” 数组虽然声明为 int8_t 类型,但其实真正的类型取决于 “encoding” 的值。在操作一个整数集合的时候,会首先获取 “encoding” 的值。

举个栗子,当我们执行 SADD numbers 1 3 5 向集合对象插入数据时,该集合对象在内存的结构如下:

image-20230823235054892

2.3、哈希表HashTable

Redis 中的 key-value 是通过 dictEntry 对象来实现的,而哈希表就是将 dictEntry 进行了再一次的包装得到的,这就是哈希表对象 dictht:

typedef struct dictht {dictEntry **table;//哈希表数组unsigned long size;//哈希表大小unsigned long sizemask;//掩码大小,用于计算索引值,总是等于size-1unsigned long used;//哈希表中的已有节点数
} dictht;

PS:table 是一个数组,其每个元素都是一个 dictEntry 对象。

hashtable 编码的集合对象使用字典作为底层实现,字典的每个键都是一个字符串对象,每个字符串对象对应一个集合元素,字典的值都是 NULL。当我们执行 SADD fruits "apple" "banana" "cherry" 向集合对象插入数据时,该集合对象在内存的结构如下:

image-20230823235716144


3、Set常用命令

3.1、添加集合元素

使用 SADD 命令添加集合元素

SADD set value

若值已存在,则不进行添加,并返回 0

image-20230821235344528

3.2、查看集合所有值

使用 SMEMBERS 命令查看集合所有值

SMEMBERS set

image-20230821235614136

3.3、判断一个值是否在集合中

使用 SISMEMBER 命令判断一个值是否在集合中

image-20230821235954832

3.4、查看某集合的存值的数量

使用 SCARD 命令查看某集合的存值的数量

SCARD set

image-20230822000410786

3.5、删除集合中指定值的元素

使用 SREM 删除集合中指定值的元素

SREM set value

image-20230822000710429

3.6、随机选出某集合中一个元素

使用 SRANDMEMBER 命令随机选出某集合中一个元素

SRANDMEMBER set

image-20230822000949807

3.7、随机删除某集合中一个元素

使用 SPOP 命令随机删除某集合中一个元素

SPOP set

image-20230822001227634

3.8、将一个集合中的某值移动至另一个集合

使用 SMOVE 命令 将一个集合中的某值移动至另一个集合

SMOVE source target value

image-20230822001457709

3.9、集合运算:差集

使用 SDIFF 命令进行集合运算:差集

SDIFF set1 set2

image-20230822001906994

3.10、集合运算:交集

使用 SINTER 命令进行集合运算:交集

SINTER set1 set2

image-20230822002039149

3.11、集合运算:并集

使用 SUNION 命令进行集合运算:并集

SUNION set1 set2

image-20230822001939037

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

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

相关文章

js reverse实现数据的倒序

2023.8.25今天我学习了如何在数组顺序进行倒序排列&#xff0c;如&#xff1a; 原数组为&#xff1a; 我们只需要对数组使用reverse()方法 let demo [{id: 1, name: 一号},{id: 2, name: 二号},{id: 3, name: 三号},]demo.reverse()console.log(demo) 扩展&#xff1a; 当我…

jmeter入门:接口压力测试全解析

一.对接口压力测试 1.配置 1.添加线程组&#xff08;参数上文有解释 这里不介绍&#xff09; 2.添加取样器 不用解释一看就知道填什么。。。 3.添加头信息&#xff08;否则请求头对不上&#xff09; 也不用解释。。。 4.配置监听器 可以尝试使用这几个监听器。 2.聚合结果…

Tushare入门小册

Tushare入门小册 一、Tushare平台介绍 Pro版数据更稳定质量更好了&#xff0c;我们提供的不再是直接从互联网抓取&#xff0c;而是通过社区的采集和整理存入数据库经过质量控制后再提供给用户。但Pro依然是个开放的&#xff0c;免费的平台&#xff0c;不带任何商业性质和目的…

4.网络设计与redis、memcached、nginx组件(一)

网络组件系列文章目录 第四章 网络设计与redis、memcached、nginx组件 文章目录 网络组件系列文章目录文章的思维导图前言一、网络相关的问题&#xff0c;网络开发中要处理那些问题&#xff1f;网络操作IO连接建立连接断开消息到达消息发送网络操作IO特性 二、网络中IO检测IO函…

SpringBoot概述SpringBoot基础配置yml的使用多环境启动

&#x1f40c;个人主页&#xff1a; &#x1f40c; 叶落闲庭 &#x1f4a8;我的专栏&#xff1a;&#x1f4a8; c语言 数据结构 javaEE 操作系统 石可破也&#xff0c;而不可夺坚&#xff1b;丹可磨也&#xff0c;而不可夺赤。 SpringBoot简介 一、 SpringBoot概述1.1 起步依赖…

Docker自学:利用FastAPI建立一个简单的web app

环境配置&#xff1a;下载Docker Desktop 文件一&#xff1a;main.py from typing import Unionfrom fastapi import FastAPIimport uvicornapp FastAPI()app.get("/") def read_root():return {"Hello": "World"}app.get("/items/{item…

线性代数的学习和整理10:各种特殊类型的矩阵(草稿-----未完成 建设ing)

目录 1 图形化分类 1.1对称矩阵 1.2 梯形矩阵 1.3 三角矩阵 1.3.1 上三角矩阵 1.4 对角线矩阵 2 按各自功能分 2.1 等价矩阵 2.2 增广矩阵 2.3 伴随矩阵 2.4 正交矩阵 2.5 正交矩阵 2.6 相似矩阵 1 图形化分类 1.1对称矩阵 1.2 梯形矩阵 1.3 三角矩阵 1.3.1 上…

有序充电运营管理平台是基于物联网和大数据技术的充电设施管理系统-安科瑞黄安南

随着我国能源战略发展以及低碳行动的实施&#xff0c;电动汽车已逐步广泛应用&#xff0c;而电动汽车的应用非常符合当今社会对环保意识的要求&#xff0c;以及有效节省化石燃料的消耗。 由于其没有污染排放的优点以及政府部门的关注&#xff0c;电动汽车将成为以后出行的重要…

OnePlus Open可折叠手机:规格、价格、发布日期等详细信息汇总!

我们知道OnePlus可折叠手机即将问世,无论它是否被命名为OnePlus Open。我们迫不及待地想让它到来,为该公司再添一根弦,为最好的可折叠手机增添一个新的竞争对手。 OnePlus以前没有生产过任何可折叠产品,但它确实拥有合作伙伴公司Oppo的丰富知识,并可以向三星、摩托罗拉和…

LLM预训练大型语言模型Pre-training large language models

在上一个视频中&#xff0c;您被介绍到了生成性AI项目的生命周期。 如您所见&#xff0c;在您开始启动您的生成性AI应用的有趣部分之前&#xff0c;有几个步骤需要完成。一旦您确定了您的用例范围&#xff0c;并确定了您需要LLM在您的应用程序中的工作方式&#xff0c;您的下…

day-30 代码随想录算法训练营 回溯part06

332.重新安排行程 思路&#xff1a;使用unordered_map记录起点机场对应到达机场&#xff0c;内部使用map记录到达机场的次数&#xff08;因为map会进行排序&#xff0c;可以求出最小路径&#xff09; class Solution { public:vector<string>res;unordered_map<stri…

【动手学深度学习】--21.锚框

锚框 学习视频&#xff1a;锚框【动手学深度学习v2】 官方笔记&#xff1a;锚框 1.锚框 目标检测算法通常会在输入图像中采样大量的区域&#xff0c;然后判断这些区域中是否包含我们感兴趣的目标&#xff0c;并调整区域边界从而更准确地预测目标的真实边界框&#xff08;gro…

智慧党建VR虚拟3D数字化展厅发展和传承传统文化

三维全景虚拟现实技术应用在虚拟展馆中&#xff0c;主要是通过全景照片的虚拟与建模&#xff0c;营造出三维虚拟仿真的场景&#xff0c;从而结合展馆展示的需求&#xff0c;营造出更加有效的氛围&#xff0c;起到优化展示效果的作用。 三维全景虚拟现实技术的应用&#xff0c;能…

05有监督学习——神经网络

线性模型 给定n维输入&#xff1a; x [ x 1 , x 1 , … , x n ] T x {[{x_1},{x_1}, \ldots ,{x_n}]^T} x[x1​,x1​,…,xn​]T 线性模型有一个n维权重和一个标量偏差: w [ w 1 , w 1 , … , w n ] T , b w {[{w_1},{w_1}, \ldots ,{w_n}]^T},b w[w1​,w1​,…,wn​]T,b 输…

面试题(三)

目录 一.Spring 1.Spring IOC & AOP 2.Spring bean (1) 作用域 (2) Spring 中的 bean ⽣命周期 (3) Spring 框架中⽤到了哪些设计模式 二.Mybatis 1.标签 2.Dao接口 3.返回与映射 4.延迟加载 三.Kafka 四.设计模式 1.IO 设计模式 2.Spring 中的设计模式详解…

若依前后端分离版本项目总结笔记

若依前后端分离学习笔试 1.路由问题 注意这个是前端找到你的路由的路径。 2.表格开关按钮快速实现 <el-table-column label"状态" align"center" key"status"><template slot-scope"scope"><el-switchv-model"s…

VS+Qt 自定义Dialog

与QtCreator不同&#xff0c;刚用VS添加Qt Dialog界面有点懵&#xff0c;后整理了下&#xff1a; 1.右击项目&#xff0c;选择“添加-模块”&#xff0c;然后选择“Qt-Qt Widgets Class” 2.选择基类[1]QDialog,更改[2]ui文件名称&#xff0c;修改定义Dialog[3]对应类名&#…

【面试题系列】(一)

Redis有哪些数据结构&#xff1f;其底层是怎么实现的&#xff1f; Redis 系列&#xff08;一&#xff09;&#xff1a;深入了解 Redis 数据类型和底层数据结构 字符串&#xff08;String&#xff09;&#xff1a; 用于存储文本或二进制数据。可以执行字符串的基本操作&#xf…

每日一题 113路径总和||(递归)

题目 给你二叉树的根节点 root 和一个整数目标和 targetSum &#xff0c;找出所有 从根节点到叶子节点 路径总和等于给定目标和的路径。 叶子节点 是指没有子节点的节点。 示例 1&#xff1a; 输入&#xff1a;root [5,4,8,11,null,13,4,7,2,null,null,5,1], targetSum 22…

【Mybatis源码分析】Mybatis 是如何实现预编译的?

Mybatis 是如何实现预编译的&#xff1f; 一、前言二、源码分析三、总结 一、前言 在介绍 Mybatis 是如何实现预编译之前&#xff0c;需提前知道俩个预备知识&#xff1a; MySQL的运行流程&#xff08;对应的 SQL 会成为一个文本-》查询缓存&#xff08;8.0后没了&#xff09…