Redis--Zset有序集合的语法和使用场景举例(朋友圈点赞,排行榜)

文章目录

      • 前言
      • zset命令介绍
      • 使用场景一:朋友圈点赞
      • 使用场景二:排行榜
      • 结尾

前言

  • Redis有五种常用的数据结构,string,hash,list,set,zset。其中zset 类似于java中SortedSet和HashMap的结合体,一方面它是个set,保证了内部value的唯一性,另一方面它可以给每个value赋予一个score,代表了这个value的排序权重,用于排序。zset的主要使用场景有:粉丝列表(score是关注时间),考生成绩表,排行榜等。
  • 下文将介绍zset的使用指令,以及其两个应用场景。

zset命令介绍

  • zset添加数据 :zadd key score value
#向热门影片排行榜添加数据
zadd movies 99.0 "盗梦空间"
zadd movies 98.9 "黑客帝国"
zadd movies 98.8 "侏罗纪公园"
  • 获取zset中的数据
zrange movies 0 -1        # 按score排序列出,参数区间为排名范围
zrevrange movies 0 -1     # 按score逆序列出,参数区间为排名范围
  • 统计zset中数据的数量
zcard movies              # 相当于count()
  • 获取指定value的score值或排序值
zscorce movies "侏罗纪公园"    # 获取指定value的score值,score使用double类型存储,所以存在小数点精度问题
zrank movies "盗梦空间"       # 获取该value在zset中的排名
  • 遍历zset中的数据
zrangebyscore movies 0 99  # 根据分值区间遍历zset
zrangebyscore movies -inf 99.0 withscores     # 根据分值区间(-无穷,8.91]遍历zset,同时返回分值

使用场景一:朋友圈点赞

  • 需求:此处我们要实现的功能是朋友圈点赞,我们需要一个数据结构记录一个朋友圈点赞的用户id列表,同时记录他们点赞的先后顺序。该数据结构需要支持新增点赞信息,取消点赞信息,已经查看点赞用户列表,同时要注意的是,用户不能重复点赞。

  • 实现方案:通过分析需求,我们发现使用Redis的zset正好可以完美实现这个需求,zset有序集合可以向集合添加,删除数据,同时保证集合中的数据不会重复,并且可以使用用户点赞的时间戳进行排序。

  • 代码实现:代码包括新增点赞,取消点赞和查看点赞列表三个核心方法。

    public class RedisLikeDemo {
        public static void main(String[] args) throws InterruptedException {
            Jedis jedis = new Jedis("127.0.0.1");
            //朋友圈文章id为:001
            String postId = "001";
            jedis.del(LIKE_PREFIX+postId);
            //id为0-9的用户点赞
            for (Integer i = 10; i>0; i--){
                //让时间戳错开来,方便观察
                Thread.sleep(1);
                postAddLike(postId,i.toString(),jedis);
            }
            // id为5的用户取消点赞
            postDeleteLike(postId,"5",jedis);
            Long s = jedis.zcard(LIKE_PREFIX +postId);
            System.out.println("朋友圈001号文章的点赞数:"+s);
    
            List<String> likes = getLikes(postId, jedis);
            System.out.println("参与点赞的用户id列表:"+likes);
        }
    
        private static final String LIKE_PREFIX = "like:";
    
        /**
         * 朋友圈点赞
         * postId:朋友圈文章id
         * userId:用户id
         * 将时间戳作为score
         */
        public static void postAddLike(String postId, String userId, Jedis jedis){
            String key = LIKE_PREFIX+postId;
            Long now = System.currentTimeMillis();
            jedis.zadd(key,now.doubleValue(),userId);
        }
        /**
         * 取消点赞
         * 将用户Id从zset中去掉
         */
        public static void postDeleteLike(String postId,String userId,Jedis jedis){
            jedis.zrem(LIKE_PREFIX+postId,userId);
        }
        /**
         * zrevrange操作逆序输出点赞列表
         */
        public static List<String> getLikes(String postId, Jedis jedis){
            String key = LIKE_PREFIX+postId;
            // 获取点赞列表
            Set<Tuple> tuples = jedis.zrevrangeByScoreWithScores(key, "inf", "-inf");
            List<String> collect = tuples.stream().map(Tuple::getElement).collect(Collectors.toList());
            return collect;
        }
    }
    

    在开始之前记得在pom文件中引入jedis依赖

       <dependency>
             <groupId>redis.clientsgroupId>
             <artifactId>jedisartifactId>
             <version>3.3.0version>
             <type>jartype>
             <scope>compilescope>
       dependency>
    
  • 测试结果:我们在main方法中,模拟用户发表id为001的朋友圈,之后有10个用户点赞,又有用户取消点赞的场景。运行main方法,得到点赞列表结果如下:
    Redis--Zset有序集合的语法和使用场景举例(朋友圈点赞,排行榜)_第1张图片

使用场景二:排行榜

  • 需求:实现排行榜功能,要求若分数相同时则按照时间顺序排序。
  • 实现方案:排行榜算是zset的拿手应用了,实现规定的排序要求主要在score上做文章,可以使用一个浮点数来作为score进行排序,浮点数的整数部分为排行榜的分数,小数部分则为时间戳。若整数部分相同,则会比较小数部分,即实现了分数相同比较时间。基于这点score可以设计为:

    score = 分数 + (1-时间戳/1e13) :这样可以实现分数倒序排列时,分数相同的文章,越早发布的在越前面。

  • 代码实现:代码主要是排行榜添加功能,重点在于score的设计。
     public class RedisRankDemo {
         public static void main(String[] args) throws InterruptedException {
             Jedis jedis = new Jedis("127.0.0.1");
             jedis.del(RANK_KEY);
     
             addRank("001",99,System.currentTimeMillis(),jedis);
             Thread.sleep(2);
             addRank("002",99,System.currentTimeMillis(),jedis);
             Thread.sleep(2);
             addRank("003",100,System.currentTimeMillis(),jedis);
             Thread.sleep(2);
             addRank("004",100,System.currentTimeMillis(),jedis);
             System.out.println("排行榜信息:");
             System.out.println(getRankList(jedis));
         }
         private static final String RANK_KEY = "rank";
     
         /**
          * 添加排行榜 要求若分数相同时则按照时间顺序排序。
          * score = 分数 + (1-时间戳/1e13)
          * value为用户id
          */
         public static void addRank(String userId, int score, long times, Jedis jedis) {
             jedis.zadd(RANK_KEY, score + (1 - times / 1e13), userId);
         }
     
         /**
          * 获取排行榜信息,按score逆序排列
          * @param jedis
          * @return
          */
         public static List<String> getRankList(Jedis jedis) {
             return jedis.zrevrangeByScoreWithScores(RANK_KEY, "inf", "-inf").stream().map(Tuple::getElement).collect(Collectors.toList());
         }
    } 
    
  • 测试结果:我们在main方法中,向排行榜中按先后顺序添加用户001(分数为99),用户002(分数为99),用户003(分数为100),用户004(分数为100)。观察排行榜的展示情况,执行结果如下,符合需求:
    Redis--Zset有序集合的语法和使用场景举例(朋友圈点赞,排行榜)_第2张图片

结尾

  • 以上是zset的使用方法和应用场景的举例。在实际工作中,根据具体的业务场景,我们需要灵活地选择合适的数据类型,这也要求我们要对redis的各种数据类型有足够的了解,所以继续好好学习叭。

你可能感兴趣的:(Redis,redis,数据库,缓存)