redis基本数据结构-set

文章目录

    • 1. set的基本介绍
      • 1.1. set底层结构之hash表的简单介绍
      • 1.2. 常用命令
    • 2. 常见的业务场景
      • 2.1. 标签系统
      • 2.2. 社交网络好友关系

1. set的基本介绍

参考链接:https://mp.weixin.qq.com/s/srkd73bS2n3mjIADLVg72A
redis 的 set 数据结构是一个无序的集合,可以存储不重复的元素。适合于多种应用场景,尤其是在需要去重、快速查找和集合运算的场合。从上面的介绍可以看到set主要的特性为:
无序性:元素在set中没有特定的顺序;
唯一性:set中的元素是唯一的,不能重复。
其中还有一个比较重要的特性就是高效性。因为redis 的 Set 数据结构是基于哈希表(hash table)实现的,这也是为什么对元素的添加、删除和查找操作的时间复杂度都是 O(1) 的原因。

1.1. set底层结构之hash表的简单介绍

  1. 哈希函数:哈希表使用哈希函数将元素映射到一个数组的索引中。通过这个索引,我们可以快速定位到存储在该位置的元素。
  2. 存储结构:在 Redis 中,Set 存储元素时会使用一个哈希表,其键是元素的值,值通常是一个指向该元素的指针。这样,每个元素的插入、查找或删除操作都可以直接通过哈希表的索引进行。
  3. 添加元素(SADD):当添加一个新元素时,Redis 计算该元素的哈希值,并根据这个哈希值找到相应的索引位置。如果该位置为空(即没有相同的元素),就将该元素插入数组中。这一过程的时间复杂度是 O(1)。
  4. 删除元素(SREM):删除一个元素时,Redis 同样计算该元素的哈希值并找到相应的索引位置。如果该元素存在,就将其从哈希表中移除。这一过程也是 O(1)。
  5. 查找元素(SISMEMBER):查找一个元素是否在集合中,Redis 计算该元素的哈希值,并通过该哈希值定位到对应的索引位置。如果该元素存在于该位置,则返回 true;如果不存在,则返回 false。这一过程同样是 O(1)。
    理论上的限制
    尽管哈希表的平均时间复杂度是 O(1),在某些情况下,哈希表可能会出现冲突(即不同的元素计算出相同的哈希值)。当发生冲突时,多个元素可能会存储在同一个索引位置,导致查找、插入和删除的性能下降。此时,复杂度可能退化为 O(n),其中 n 是发生冲突的元素数量。然而,Redis 使用了一些策略(如动态扩容和链表/树结构来处理冲突)来保持哈希表的性能,使得在大多数情况下,操作的时间复杂度仍然可以保持在 O(1)。

1.2. 常用命令

SADD key member [member ...]:将一个或多个成员添加到集合中。
SREM key member [member ...]:移除集合中的一个或多个成员。
SISMEMBER key member:判断某个成员是否是集合中的成员。
SMEMBERS key:返回集合中的所有成员。
SCARD key:返回集合中成员的数量。
交集:SINTER key1 [key2 ...]:返回所有给定集合的交集。
并集:SUNION key1 [key2 ...]:返回所有给定集合的并集。
差集:SDIFF key1 [key2 ...]:返回第一个集合与其他集合的差集。
SADD myset "apple" "banana" "orange"
SREM myset "banana"
SISMEMBER myset "apple"  # 返回 1 (true)
SMEMBERS myset  # 返回 ["apple", "orange"]
SCARD myset  # 返回 2SADD set1 "a" "b" "c" 
SADD set2 "b" "c" "d" 
SINTER set1 set2  # 返回 ["b", "c"] 
SUNION set1 set2  # 返回 ["a", "b", "c", "d"] 
SDIFF set1 set2   # 返回 ["a"]

2. 常见的业务场景

一般业务场景的使用是会根据对应数据结构的特性来说明的:

  1. 去重:当需要存储一组唯一的用户 ID、标签或其他信息时,可以使用 Set 来自动去重。例如,存储用户的浏览历史,确保每个用户的历史记录中没有重复的页面。【唯一性】
  2. 社交网络:在社交网络应用中,可以使用 Set 来表示用户的好友关系。比如,用户 A 的好友可以存储在 Set 中,快速判断用户 B 是否是用户 A 的好友。【唯一性,高效性】
  3. 标签系统:对于文章、商品等,可以使用 Set 来存储相关标签,方便进行标签查询和管理。【高效性】
  4. 投票系统:可以使用 Set 存储已经投票的用户 ID,确保每个用户只能投一次票。【唯一性】
  5. 实时分析:在实时分析场景中,可以使用 Set 来跟踪活跃用户、访问过的页面等。【高效性】

2.1. 标签系统

标签系统:Set类型可用于存储和处理具有标签特性的数据,如商品标签、文章分类标签等。
背景
在一个内容平台上,用户可以给文章打上不同的标签,系统需要根据标签过滤和推荐文章。
在这里插入图片描述
优势

  1. 快速查找:使用Set可以快速判断一个元素是否属于某个集合。
  2. 灵活的标签管理:方便地添加和删除标签,实现标签的灵活管理。
  3. 集合运算:通过集合运算,如交集和并集,可以轻松实现复杂的标签过滤逻辑。

代码实现

package mainimport ("context""fmt""github.com/go-redis/redis/v8"
)var ctx = context.Background()// Redis 客户端初始化
var rdb = redis.NewClient(&redis.Options{Addr:     "",  // Redis 服务器地址Password: "", // 密码DB:       0,                  // 使用默认 DB
})// 文章标签管理结构体
type ArticleTagManager struct{}// 给文章添加标签
func (atm *ArticleTagManager) AddTagToArticle(articleID string, tags []string) error {_, err := rdb.SAdd(ctx, "article:"+articleID+":tags", tags).Result()if err != nil {return err}for _, tag := range tags {_, err := rdb.SAdd(ctx, "global:tags:"+tag, "article:"+articleID).Result()if err != nil {return err}}log.Printf("Added tags %v to article %s\n", tags, articleID)return nil
}// 从文章中删除标签
func (atm *ArticleTagManager) RemoveTagFromArticle(articleID string, tag string) error {_, err := rdb.SRem(ctx, "article:"+articleID+":tags", tag).Result()if err != nil {return err}log.Printf("Removed tag %s from article %s\n", tag, articleID)return nil
}// 获取文章的所有标签
func (atm *ArticleTagManager) GetTagsOfArticle(articleID string) ([]string, error) {tags, err := rdb.SMembers(ctx, "article:"+articleID+":tags").Result()if err != nil {return nil, err}return tags, nil
}// 根据标签获取文章列表
func (atm *ArticleTagManager) GetArticlesByTag(tag string) ([]string, error) {articles, err := rdb.SMembers(ctx, "global:tags:"+tag).Result()if err != nil {return nil, err}return articles, nil
}// 获取文章的标签数量
func (atm *ArticleTagManager) GetTagCountOfArticle(articleID string) (int64, error) {count, err := rdb.SCard(ctx, "article:"+articleID+":tags").Result()if err != nil {return 0, err}return count, nil
}// 获取某个标签的文章数量
func (atm *ArticleTagManager) GetArticleCountByTag(tag string) (int64, error) {count, err := rdb.SCard(ctx, "global:tags:"+tag).Result()if err != nil {return 0, err}return count, nil
}func main() {atm := &ArticleTagManager{}// 为文章添加标签if err := atm.AddTagToArticle("1", []string{"Golang", "Redis"}); err != nil {log.Fatalf("Error adding tags: %v", err)}if err := atm.AddTagToArticle("2", []string{"Python", "Redis"}); err != nil {log.Fatalf("Error adding tags: %v", err)}if err := atm.AddTagToArticle("3", []string{"Golang", "Python"}); err != nil {log.Fatalf("Error adding tags: %v", err)}// 获取文章的所有标签if tags, err := atm.GetTagsOfArticle("1"); err == nil {fmt.Println("Tags for article 1:")for _, tag := range tags {fmt.Println(tag)}}// 根据标签获取文章列表if articles, err := atm.GetArticlesByTag("Golang"); err == nil {fmt.Println("Articles with tag 'Golang':")for _, article := range articles {fmt.Println(article)}}// 获取文章标签数量if count, err := atm.GetTagCountOfArticle("1"); err == nil {fmt.Printf("Article 1 has %d tags.\n", count)}// 获取某个标签的文章数量if count, err := atm.GetArticleCountByTag("Redis"); err == nil {fmt.Printf("Tag 'Redis' has %d articles associated.\n", count)}// 测试完移除整个集合rdb.Del(ctx, "article:1:tags")rdb.Del(ctx, "article:2:tags")rdb.Del(ctx, "article:3:tags")rdb.Del(ctx, "global:tags:Golang")rdb.Del(ctx, "global:tags:Redis")rdb.Del(ctx, "global:tags:Python")
}

运行结果:
在这里插入图片描述

2.2. 社交网络好友关系

社交网络好友关系:Set类型可以表示用户的好友列表,支持快速好友关系测试和好友推荐。
背景
在一个社交网络应用中,用户可以添加和删除好友,系统需要管理用户的好友关系。
在这里插入图片描述
优势

  1. 唯一性:保证好友列表中不会有重复的好友。
  2. 快速关系测试:快速判断两个用户是否互为好友。
  3. 好友推荐:利用集合运算,如差集,推荐可能认识的好友。

解决方案
使用Redis Set类型存储用户的好友集合,实现好友关系的管理。
代码实现

package mainimport ("context""fmt""github.com/go-redis/redis/v8""log"
)var ctx = context.Background()// Redis 客户端初始化
var rdb = redis.NewClient(&redis.Options{Addr:     "",  // Redis 服务器地址Password: "", // 密码DB:       0,                  // 使用默认 DB
})// 添加好友
func addFriend(userOneID string, userTwoID string) error {_, err := rdb.SAdd(ctx, "user:"+userOneID+":friends", userTwoID).Result()if err != nil {return err}_, err = rdb.SAdd(ctx, "user:"+userTwoID+":friends", userOneID).Result()return err
}// 判断是否是好友
func isFriend(userOneID string, userTwoID string) bool {return rdb.SIsMember(ctx, "user:"+userOneID+":friends", userTwoID).Val()
}// 获取用户的好友列表
func getFriendsOfUser(userID string) ([]string, error) {friends, err := rdb.SMembers(ctx, "user:"+userID+":friends").Result()return friends, err
}// 删除好友
func removeFriend(userOneID string, userTwoID string) error {_, err := rdb.SRem(ctx, "user:"+userOneID+":friends", userTwoID).Result()if err != nil {return err}_, err = rdb.SRem(ctx, "user:"+userTwoID+":friends", userOneID).Result()return err
}// 推荐可能认识的好友
func recommendFriends(userID string) ([]string, error) {// 获取用户的好友friends, err := getFriendsOfUser(userID)if err != nil {return nil, err}// 如果没有好友,则没有推荐if len(friends) == 0 {return nil, nil}// 找到所有好友的好友,以及去掉自己的好友和自己potentialFriends := make(map[string]struct{})for _, friendID := range friends {friendFriends, err := getFriendsOfUser(friendID)if err != nil {return nil, err}for _, potentialFriend := range friendFriends {// 排除自己和直接好友if potentialFriend != userID {potentialFriends[potentialFriend] = struct{}{}}}}// 将推荐的好友转换为切片recommendations := []string{}for friend := range potentialFriends {// 确保不是已经存在的好友if !isFriend(userID, friend) {recommendations = append(recommendations, friend)}}return recommendations, nil
}func main() {// 示例:添加好友if err := addFriend("user1", "user2"); err != nil {log.Fatalf("Error adding friend: %v", err)}// 示例:检查好友关系if isFriend("user1", "user2") {fmt.Println("user1 and user2 are friends.")}// 示例:获取好友列表friends, err := getFriendsOfUser("user1")if err != nil {log.Fatalf("Error getting friends: %v", err)}fmt.Println("Friends of user1:", friends)// 示例:推荐可能认识的好友recommendations, err := recommendFriends("user1")if err != nil {log.Fatalf("Error recommending friends: %v", err)}fmt.Println("Recommended friends for user1:", recommendations)// 示例:删除好友if err := removeFriend("user1", "user2"); err != nil {log.Fatalf("Error removing friend: %v", err)}
}

注意事项:

  • 虽然Set是无序的,但Redis会保持元素的插入顺序,直到集合被重新排序。
  • Set中的元素是唯一的,任何尝试添加重复元素的操作都会无效。
  • 使用集合运算时,需要注意结果集的大小,因为它可能会影响性能。

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

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

相关文章

暴雨传染病智能监测预警前置一体机筑牢疾控第一道防线

自新冠疫情爆发以来,疾病防控已成为全球关注的焦点。只有加强监测预警、做到“早发现”才能及时防范和化解传染病疫情。近日,经国务院批准,国家疾控局、国家卫生健康委等九部门联合发布了《关于建立健全智慧化多点触发传染病监测预警体系的指…

信息安全数学基础(12)剩余类及完全剩余系

一、剩余类 定义:设 m 是一个正整数,a 是任意整数。模 m 的 a 的剩余类定义为集合 Ca​{c∣c∈Z,c≡a(modm)}。这个集合包含了所有模 m 余数为 a 的整数。 解释:剩余类实际上是将整数集 Z 分成了 m 个等价类,每个类中的元素在模 m…

大公司与小公司:产品经理的职业抉择与发展之路

在产品经理的职业旅程中,常常面临一个重要的抉择:是选择大公司还是小公司?这个问题困扰着许多初入职场的新人以及寻求职业转型的资深人士。今天,我们就来深入探讨一下大公司与小公司对于产品经理的不同意义,以及如何规…

互相关、相关系数和内积的关系

目录 问题互相关与卷积xcorr互相关xcorr2 2-D cross-correlationconv卷积conv2二维卷积关系与区别xcov互协方差 相关系数cov协方差与协方差矩阵corrcoef相关系数与相关系数矩阵图像均值、标准差和相关系数 内积与相似系数内积(Inner Product)欧几里得空间…

AUTOSAR_EXP_ARAComAPI的5章笔记(6)

返回目录 5.3.5.5 Event-Driven vs Polling-Based access ara::com实现完全支持事件驱动和轮询的方式来访问新数据。 对于轮询方式,典型的用例是,一个应用程序被周期性地触发并在特定的截止时间前进行一些处理。这是调节器/控制算法的典型模式 —— 循…

Visual Studio安装教程

这次我给大家讲一下Visual Studio安装 一、官网下载 官网下载地址:https://visualstudio.microsoft.com/zh-hans/downloads/ 下载下来的是一个.exe文件 双击打开,出现下面的界面 二、安装visual studio (一)更改安装路径 首先&am…

如何提升RAG检索的准确率及答案的完整性?

RAG(检索增强生成),重点在于检索,即通过解析文档,然后使用嵌入模型进行向量化,通过欧式距离、向量积乘、最近临等算法来计算向量的相似度,找到与提问语义相似的上下文。然后通过将上下文提交给大…

【LeetCode】每日一题 2024_9_15 与车相交的点(差分)

前言 每天和你一起刷 LeetCode 每日一题~ LeetCode 启动! 今天的题目曾经的我做过了 . . . 又是复习的一天 题目:与车相交的点 代码与解题思路 func numberOfPoints(nums [][]int) (ans int) { diff : [102]int{}for _, p : range nums {diff[p[0]]d…

基于java网吧管理系统设计与实现

博主介绍:专注于Java .net php phython 小程序 等诸多技术领域和毕业项目实战、企业信息化系统建设,从业十五余年开发设计教学工作 ☆☆☆ 精彩专栏推荐订阅☆☆☆☆☆不然下次找不到哟 我的博客空间发布了1000毕设题目 方便大家学习使用 感兴趣的可以…

c++基础入门二

C基础入门(二) 一、函数重载 在自然语言中,一句话或者一个词有不同的意思。例如:国乒和别人比赛是“谁也赢不了”,而国足和别人比赛是“谁也赢不了” 函数重载:是函数的一种特殊情况,C允许在同一作用域中声明几个功…

开放系统,面向各类业务需求可提供定制化服务的智慧物流开源了。

智慧物流视频监控平台是一款功能强大且简单易用的实时算法视频监控系统。它的愿景是最底层打通各大芯片厂商相互间的壁垒,省去繁琐重复的适配流程,实现芯片、算法、应用的全流程组合,从而大大减少企业级应用约95%的开发成本。构建基于Ai技术的…

c++中的二叉搜索树

一概念: 静图展示: 动图展示: ①左子树不为空,则左子树节点值小于根节点值。 ②右子树不为空,则右子树节点值大于根节点值。 ③左右子树均为二叉搜索树。 ④对于它可以插入相等的也可以插入不相等的,这里如果插入的…

JavaSE语法阶段复习知识整理3之封装

文章目录 一、封装1.1 封装的概念1.2 访问限定符1.3封装扩展之包 二、static成员2.1static关键字的引入2.2静态成员变量初始化2.3访问静态成员变量2.4用实际问题加深静态成员变量的理解2.5静态成员变量的总结要点2.6静态成员方法的总结要点 三、代码块3.1普通代码块3.2构造代码…

QXDM 如何更新软件?

如何更新QXDM等高通软件?之前做过这个事情,但过几个月给别人讲的时候就忘记了,特做如下记录。 一. 背景知识: 1. QXDM 依赖于Qualcomm package Managers 3(QPM in short)。 目前的时间是2024年9月15日,但不知从何…

学习笔记JVM篇(一)

1、类加载的过程 加载->验证->准备->解析->初始化->使用->卸载 2、JVM内存组成部分(HotSpot) 名称作用特点元空间(JDK8之前在方法区)用于存储类的元数信息,例如名称、方法名、字段等;…

[苍穹外卖]-09Spring Task定时任务

Spring Task spring Task是spring框架提供的任务调度工具,可以按照约定的时间自动执行某个代码逻辑 只要是需要定时处理的场景都可以使用Spring Task定时任务框架 cron表达式就是一个字符串,可以定义任务触发的时间 构成规则: 分为6或7个域, 由空格隔开,每个域代表一个含义每…

Java 全面指南:从入门到精通

目录 1. 引言 Java 的背景 Java 的起源及历史发展 主要的应用场景 Java 的核心特性 面向对象 跨平台性(JVM 的角色) 自动内存管理与垃圾回收机制 Java 版本与发展历程 Java SE 8, 11, 17 等主要版本特性 新增功能概述(如 Lambda 表…

SpringBoot新技能:零停机更新代码

在个人或者企业服务器上,总归有要更新代码的时候,普通的做法必须先终止原来进程,因为新进程和老进程端口是一个,新进程在启动时候,必定会出现端口占用的情况,但是,还有黑科技可以让两个SpringBo…

【机器学习】--- 深度学习中的注意力机制

深度学习中的注意力机制 在深度学习领域,注意力机制(Attention Mechanism)已经成为近年来最受瞩目的研究热点之一。它不仅提升了现有模型的性能,更启发了全新的网络结构,如Transformer模型。注意力机制被广泛应用于自…

c语言中的局部跳转以及全局跳转

一、前言 在c语言中,当我们在处理某些异常情况的时候,经常会使用goto语句来进行跳转。goto用起来很方便,但可能很多人都不知道,goto只能在一个函数里面跳转,并不能够跨函数跳转。本文将介绍能够跨函数跳转的接口setjm…