《程序猿之Redis缓存实战 · 集合类型》

news2024/9/30 19:16:43

📢 大家好,我是 【战神刘玉栋】,有10多年的研发经验,致力于前后端技术栈的知识沉淀和传播。 💗

🌻 CSDN入驻不久,希望大家多多支持,后续会继续提升文章质量,绝不滥竽充数,欢迎多多交流。👍

文章目录

    • 集合(Set)

集合(Set)

【简介】

Redis 中的 Set 类型是一种无序集合,集合中的元素唯一,也就是集合中的元素是无重复的,有点类似于 Java 中的 HashSet 。

【应用场景】

1、需要随机获取数据源中的元素的场景

● 举例 :抽奖系统、随机。

● 相关命令:SADD(加入抽奖系统)、SMEMBERS(查看所有抽奖用户)、SPOP(随机获取集合中的元素并移除,适合不允许重复中奖的场景)、SRANDMEMBER(随机获取集合中的元素,适合允许重复中奖的场景)。

2、需要存放的数据不能重复的场景

● 举例:文章点赞、动态点赞等场景。

● 相关命令:SADD(点赞)、SREM(移除点赞)、SISMEMBER(检查用户是否点赞过)、SMEMBERS(获取点赞用户列表)、SCARD(获取点赞用户数量)

Tips:理论上,HashSet 适合的存储场景,这边也适合了。

【应用场景补充】

Redis Set 集合是一种无序且不重复的元素集合,适用于多种应用场景。

1、去重存储:使用 Set 存储唯一的用户 ID、邮箱地址等,确保没有重复数据。

2、标签系统:为用户、文章等对象存储标签,方便快速检索和去重。

3、社交网络:存储用户的好友列表、关注者、粉丝等,确保每个用户的关系是唯一的。

4、推荐系统:存储用户的历史行为、偏好等,便于进行个性化推荐。

5、实时统计:统计在线用户、活跃用户等,使用 Set 可以快速判断用户是否在线或活跃。

6、权限管理:存储用户的角色或权限,确保每个角色或权限是唯一的。

7、交集、并集、差集操作:Redis 提供了对 Set 的集合运算支持,可以方便地进行交集、并集和差集操作。

总之,Redis Set 集合在处理唯一性和集合操作方面非常高效,适用于多种场景。在使用时,需注意内存使用、数据持久化和操作性能等问题,以确保系统的稳定性和高效性。

@Component
public class RedisExample implements CommandLineRunner {

    @Autowired
    private UserService userService;

    @Autowired
    private TagService tagService;

    @Autowired
    private SetOperationsService setOperationsService;

    @Override
    public void run(String... args) throws Exception {
        // 去重存储
        userService.addUserId("user1");
        userService.addUserId("user2");
        userService.addUserId("user1"); // 重复的 ID 不会被添加
        System.out.println("去重存储的用户 ID: " + userService.getUserIds());

        // 标签系统
        tagService.addTag("python");
        tagService.addTag("coding");
        tagService.addTag("python"); // 重复的标签不会被添加
        System.out.println("标签系统中的标签: " + tagService.getTags());

        // 集合操作示例
        redisTemplate.opsForSet().add("setA", "1", "2", "3");
        redisTemplate.opsForSet().add("setB", "3", "4", "5");

        System.out.println("交集: " + setOperationsService.getIntersection("setA", "setB"));
        System.out.println("并集: " + setOperationsService.getUnion("setA", "setB"));
        System.out.println("差集: " + setOperationsService.getDifference("setA", "setB"));
    }
}

@Service
public class UserService {

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    public void addUserId(String userId) {
        redisTemplate.opsForSet().add("user_ids", userId);
    }

    public Set<String> getUserIds() {
        return redisTemplate.opsForSet().members("user_ids");
    }
}

@Service
public class TagService {

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    public void addTag(String tag) {
        redisTemplate.opsForSet().add("tags", tag);
    }

    public Set<String> getTags() {
        return redisTemplate.opsForSet().members("tags");
    }
}

@Service
public class SetOperationsService {

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    public Set<String> getIntersection(String setKey1, String setKey2) {
        return redisTemplate.opsForSet().intersect(setKey1, setKey2);
    }

    public Set<String> getUnion(String setKey1, String setKey2) {
        return redisTemplate.opsForSet().union(setKey1, setKey2);
    }

    public Set<String> getDifference(String setKey1, String setKey2) {
        return redisTemplate.opsForSet().difference(setKey1, setKey2);
    }
}

【注意事项】

  1. 数据类型: Set 集合只能存储字符串类型的元素,如果需要存储其他类型的数据,需要进行序列化和反序列化。
  2. 元素数量: Set 集合的元素数量有限制,具体取决于 Redis 实例的配置。
  3. 性能: Set 集合的添加、删除、判断元素是否存在等操作的性能都非常高,但如果元素数量非常多,可能会影响性能。
  4. 数据持久化: Set 集合的数据默认存储在内存中,如果需要持久化数据,需要配置 Redis 的持久化机制。
  5. 并发访问: Set 集合支持并发访问,但需要考虑并发访问带来的数据一致性问题。

【基础操作】

SADD key member1 [ member2 ]:向集合添加一个或者多个成员
SMEBERS key:返回集合中的所有成员
SISMEMBER key member:判断menber元素是否是集合key的成员
SPOP key:移除并返回集合中的一个随机元素
SREM key member1 [ member2 ]:移除集合中一个或者多个成员
● SADD key member1 [ member2 ]:向集合添加一个或者多个成员
● SCARD key:获取集合的成员数
● SMEBERS key:返回集合中的所有成员
● SPOP key:移除并返回集合中的一个随机元素(用户抽奖只能参加一轮)
● SRANDMEMBER key [ count ]:返回集合中一个或者多个随机数(用户抽奖可以参加多轮)
● SREM key member1 [ member2 ]:移除集合中一个或者多个成员
 ---------------------------------------------------分隔线---------------------------------------------------
● SUNION key1 [ key2 ]:返回所有给定集合的并集
● SINTER key1 [ key2 ]:返回所有给定集合的交集
● SDIFF key1 [ key2 ]:返回给定所有集合的差集
● SUNIONSTORE destination key1 [ key2 ]:所有给定集合的并集存储在destination集合中
● SDIFFSTORE destination key1 [ key2 ]:返回给定所有集合的差集并存储在destination中
● SINTERSTORE destination key1 [ key2 ]:返回给定所有集合的交集并存储在destination中
● SISMEMBER key member:判断menber元素是否是集合key的成员
● SMOVE source destination menber:将member元素从source集合移动到destination集合
● SSCAN key cursor [ MATCH pattem ] [ COUNT count ]:迭代集合中的元素

【代码实战】

命令和代码方法,大部分都是去掉首字母,比如set的sadd,就是add。

当然少部分换了单词。

=== 抽奖:

=== 点赞:

【扩展:实战文章点赞】

前言,下方位图结构也说适合点赞,那么哪个更适合呢?

这里补充一点,使用Set处理,还可以不一定用户ID一定是Long类型。

Tips:哈哈,这么一看,新增加的三个高级数据结构,貌似使用场景相当少。

对于文章点赞功能,使用 Redis 的 Set 结构更适合。

=== Set 结构的优势:
去重: Set 结构天生具有去重功能,可以确保每个用户只点赞一次。
高效查询: 可以使用 SISMEMBER 命令快速判断用户是否点赞过。
高效添加和删除: 可以使用 SADD 和 SREM 命令高效地添加和删除点赞记录。
获取点赞总数: 可以使用 SCARD 命令获取点赞总数。

=== 位图结构的优势:
节省空间: 位图结构可以高效地存储大量布尔值,节省存储空间。
高效统计: 可以使用 BITCOUNT 命令快速统计点赞总数。

=== 为什么 Set 结构更适合:
点赞功能更关注用户是否点赞,而不是点赞总数: Set 结构更适合存储用户点赞状态,而位图结构更适合统计点赞总数。
Set 结构更灵活: 可以使用 Set 结构存储其他信息,例如点赞时间、点赞类型等。
Set 结构更易于扩展: 可以使用 Set 结构实现更复杂的点赞功能,例如取消点赞、点赞排行榜等。

=== 总结:
对于文章点赞功能,使用 Redis 的 Set 结构更适合,因为它更灵活、更易于扩展,并且可以满足点赞功能的基本需求。
@Service
public class ArticleLikeService {

    private static final String ARTICLE_LIKES_PREFIX = "article:likes:";

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    // 点赞
    public void likeArticle(String articleId, String userId) {
        String key = ARTICLE_LIKES_PREFIX + articleId;
        redisTemplate.opsForSet().add(key, userId);
    }

    // 取消点赞
    public void unlikeArticle(String articleId, String userId) {
        String key = ARTICLE_LIKES_PREFIX + articleId;
        redisTemplate.opsForSet().remove(key, userId);
    }

    // 获取点赞用户列表
    public Set<String> getLikes(String articleId) {
        String key = ARTICLE_LIKES_PREFIX + articleId;
        return redisTemplate.opsForSet().members(key);
    }

    // 获取点赞总数
    public Long getLikeCount(String articleId) {
        String key = ARTICLE_LIKES_PREFIX + articleId;
        return redisTemplate.opsForSet().size(key);
    }
}

【扩展:点赞想记录点赞时间】

Gpt给了两个方案,第一种还是用Set,但是存储的是Map。

@Service
public class ArticleLikeService {

    private final RedisTemplate<String, Object> redisTemplate;

    public ArticleLikeService(RedisTemplate<String, Object> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    // 添加点赞
    public void addLike(Long articleId, String userId) {
        String key = "article:likes:" + articleId;
        Map<String, Object> likeData = new HashMap<>();
        likeData.put("userId", userId);
        likeData.put("timestamp", Instant.now().toEpochMilli());
        redisTemplate.opsForSet().add(key, likeData);
    }

    // 取消点赞
    public void removeLike(Long articleId, String userId) {
        String key = "article:likes:" + articleId;
        redisTemplate.opsForSet().remove(key, userId);
    }

    // 判断用户是否点赞
    public boolean isLiked(Long articleId, String userId) {
        String key = "article:likes:" + articleId;
        return redisTemplate.opsForSet().isMember(key, userId);
    }

    // 获取点赞总数
    public Long getLikeCount(Long articleId) {
        String key = "article:likes:" + articleId;
        return redisTemplate.opsForSet().size(key);
    }

    // 获取点赞时间
    public Long getLikeTimestamp(Long articleId, String userId) {
        String key = "article:likes:" + articleId;
        Map<String, Object> likeData = (Map<String, Object>) redisTemplate.opsForSet().member(key, userId);
        if (likeData != null) {
            return (Long) likeData.get("timestamp");
        }
        return null;
    }
}

继续追问:redisTemplate.opsForSet()的值可以直接存储Map对象吗?是会自动转换吗?那是根据什么判断重复,hashCode吗?

你问的很好!RedisTemplate 的 opsForSet() 方法确实可以存储 Map 对象,但它不会自动进行转换。

Redis 的 Set 数据结构只能存储字符串类型的元素。,因此,如果你直接将 Map 对象放入 opsForSet() 方法,Redis 会将它序列化成字符串,然后存储。

判断重复的依据是字符串的比较。 Redis 会使用字符串比较来判断两个元素是否重复,而不是使用 hashCode。

解决方法:
1、手动序列化和反序列化: 你需要在存储 Map 对象之前将其序列化成字符串,并在读取时将其反序列化回 Map 对象。可以使用 Jackson 或 Gson 等序列化库来完成这个过程。
2、使用 opsForHash() 方法: 如果需要存储 Map 对象,建议使用 opsForHash() 方法,它可以存储键值对,并支持直接存储 Map 对象。

总结一下,这种情况还是直接使用Hash处理好了,当然,也可以使用 ZSet 来存储点赞记录,并使用点赞时间作为分数,这样可以方便地获取点赞时间排序的列表。

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

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

相关文章

基于微信小程序的商品展示+ssm论文ppt源码调试讲解

2 系统开发环境 2.1微信开发者工具 微信开发者工具现在已经被小程序开发团队开发运行&#xff0c;目前微信开发者工具任然在不断的完善中&#xff0c;在开发小程序时经常要不断的更新。可以使用微信扫码登陆开发者工具&#xff0c;开发者工具将使用这个微信帐号的信息进行小程…

为VRoidStudio制作的vrm格式模型制作blendshape

零、效果展示 bs视频演示 一、准备相关插件 1、VRoidStudio&#xff08;免费&#xff09; 下载网址&#xff1a;https://vroid.com/en/studio 2、UniVRM&#xff08;免费&#xff09; 下载网址&#xff1a;https://github.com/vrm-c/UniVRM/releases 注意&#xff1a;unity…

Qt --- 常用控件的介绍---Widget属性介绍

一、控件概述 编程&#xff0c;讲究的是站在巨人的肩膀上&#xff0c;而不是从头发明轮子。一个图形化界面上的内容&#xff0c;不需要咱们全都从零区实现&#xff0c;Qt中已经提供了很多内置的控件了&#xff08;按钮&#xff0c;文本框&#xff0c;单选按钮&#xff0c;复选…

yolov8实例分割重要图片

训练分割要准备好数据集和分割预训练权重文件 下面这张图是数据集的格式 下面这张图配置数据集&#xff0c;下面names 要和labelme转txt里配置的一样 下面这张图进行训练&#xff0c;配置一些全局参数 &#xff0c;初始的yolov8s-seg.pt文件需要到github上yolov8开源项目里下 l…

linux部署redis,整合ansible和redis

准备服务器192.168.45.133&#xff0c;192.168.45.135 在135上执行命令yum install -y redis安装redis yum install -y redis 源码安装方法 wget http://download.redis.io/releases/redis-2.8.13.tar.gz tar zxf redis-2.8.13.tar.gz cd redis-2.8.13 make PREFIX/usr/loca…

Cannon-es.js之Distance Constrait模拟布料

本文目录 前言1、Particle2、前置代码准备2.1 代码2.2 效果 3、使用距离约束模拟布料3.1 代码3.2 效果 前言 在现代Web开发中&#xff0c;实现逼真的物理效果对于提升用户体验至关重要。Cannon-es.js&#xff0c;作为Cannon.js的ES6模块版本&#xff0c;凭借其轻量级、高性能和…

selenium测试框架快速搭建详解

一、介绍 Selenium目前主流的web自动化测试框架&#xff1b;支持多种编程语言Java、pythan、go、js等&#xff1b;selenium 提供一系列的api 供我们使用&#xff0c;因此在web测试时我们要点页面中的某一个按钮&#xff0c;那么我们只需要获取页面&#xff0c;然后根据id或者n…

拯救华为秘盒M310

这个盒子当年宣传得比较厉害&#xff0c; 当时确实也没有什么可选的&#xff0c;当年是高价入的,这个盒子有二切一的hdmi切换功能&#xff0c; 这点从今天来看&#xff0c; 都是一个亮点 华为秘盒M310是一款小巧但功能强大的网络机顶盒。它搭载了基于安卓系统的操作平台&#x…

序列化方式四——Hessian

介绍 Hessian是一个轻量级的、基于HTTP的RPC&#xff08;远程过程调用&#xff09;框架&#xff0c;由Resin开源提供。它使用一个简单的、基于二进制的协议来序列化对象&#xff0c;并通过HTTP进行传输。Hessian的设计目标是提供一种高效、可靠且易于使用的远程服务调用机制。…

算法分析——《二分查找》

&#x1f6e9;《二分查找》 &#x1f3a8;题目描述&#xff1a; 给定一个 n 个元素有序的&#xff08;升序&#xff09;整型数组 nums 和一个目标值 target &#xff0c;写一个函数搜索 nums 中的 target&#xff0c;如果目标值存在返回下标&#xff0c;否则返回 -1。 示例 …

厦门大龄自闭症寄宿学校:为孩子们提供全方位关怀和教育

在自闭症儿童的教育与关怀领域&#xff0c;厦门大龄自闭症寄宿学校以其专业性和全面性&#xff0c;为众多家庭提供了宝贵的支持与帮助。而在中国南方的广州&#xff0c;也有一所同样致力于自闭症儿童全方位关怀与教育的机构——星贝育园自闭症儿童寄宿制学校。星贝育园以其独特…

为什么要自定义异常

背景 我们在阅读各类源码时&#xff0c;会注意到几乎各个功能包中&#xff0c;都包含有自定义的异常&#xff1b;那有没有想过&#xff0c;JDK的源码中&#xff0c;已经定义了各种异常体系&#xff0c;为啥后续的一些依赖Jar包中&#xff0c;还要自定义异常呢&#xff1f; 下面…

Java应用程序的服务器有哪些?

1.Tomcat、Jetty 和 JBoss 区别&#xff1f; Apache Tomcat、Jetty 和 JBoss都是用于部署Java应用程序的服务器&#xff0c;它们都支持Servlet、JSP和其他Java EE&#xff08;现在称为Jakarta EE&#xff09;技术。尽管它们有一些相似的功能&#xff0c;但它们之间还是存在一些…

二叉树相关oj题(Java)

一. 检查两颗树是否相同。OJ链接 这里我们考虑两种情况: 1.结构上 2.节点值上 当上面两种情况同时遍历时: 1.如果两颗树的节点都不为空,就判断值 2.如果两棵树种一棵树的节点为空另一棵树的节点不为空,则这两颗肯定不是相同的树 整体来看:要判断两棵树是否相同,得判断根,然后判…

SQLite数据库迁移与备份技术详解

目录 引言 SQLite数据库迁移 迁移概述 迁移步骤 1. 创建目标数据库系统 2. 导出SQLite数据库数据 3. 导入数据到目标数据库 4. 验证数据迁移 迁移注意事项 SQLite数据库定期备份 备份的重要性 备份方法 1. 使用VACUUM命令 2. 使用ATTACH DATABASE和PRAGMA语句 3.…

这些211热度不高,毕业年薪20-40万!自动化考研择校

这些211学校&#xff0c;今年热度不高&#xff0c;就业还意外的好&#xff0c;一定不要错过&#xff01;搭配历年数据&#xff0c;供大家参考~ 目录 ① 华东理工大学 ② 东华大学 ③ 江南大学 ④ 安徽大学 ① 华东理工大学 复试线招生人数 控制学科等级为B&#xff0c;上…

Qt QIntValidator详解

一、介绍 QIntValidator是Qt框架中用于验证整数输入的验证器类。它可以限制用户输入的整数范围&#xff0c;确保输入的整数在指定的范围内。通过QIntValidator&#xff0c;可以轻松地实现整数输入的有效性和范围限制。 二、 常用方法 QIntValidator(QObject *parent Q_NULLPT…

Python编码系列—Python设计模式的选择与权衡:打造高效代码架构

&#x1f31f;&#x1f31f; 欢迎来到我的技术小筑&#xff0c;一个专为技术探索者打造的交流空间。在这里&#xff0c;我们不仅分享代码的智慧&#xff0c;还探讨技术的深度与广度。无论您是资深开发者还是技术新手&#xff0c;这里都有一片属于您的天空。让我们在知识的海洋中…

SigmaStudio中部分滤波器算法有效性频谱分析

一、各类滤波器参数如下图设置 1.1、输入源白噪音经过如下算法处理后Notch\Band Pass\Band Stop&#xff0c;如下频谱分析图 1.2、输入源白噪音经过low pass后处理前后的频谱分析如如下 二、Notch滤波器配置图&#xff0c;如下 2.1、两串联、五个串联和未串联的Notch对白噪音…

券商股大涨,至少17家券商已入局AI人工智能金融大模型

大家好&#xff0c;我是Shelly&#xff0c;一个专注于输出AI工具和科技前沿内容的AI应用教练&#xff0c;体验过300款以上的AI应用工具。关注科技及大模型领域对社会的影响10年。关注我一起驾驭AI工具&#xff0c;拥抱AI时代的到来。 最近&#xff0c;券商股价的大涨成为了财经…