zoukankan      html  css  js  c++  java
  • Redis分布式缓存系列(五)- Redis中的ZSet类型

    本系列将和大家分享Redis分布式缓存,本章主要简单介绍下Redis中的ZSet类型,以及如何使用Redis解决实时排行榜问题。

    Sorted Sets是将 Set 中的元素增加了一个权重参数 score,使得集合中的元素能够按 score 进行有序排列。

    ZSet类型最大的特点就是有序、去重,以及交集、并集的使用。 

    存储形式:key--SortList<value>

    首先先给大家Show一波Redis中与ZSet类型相关的API:

    using System.Collections.Generic;
    
    namespace TianYa.Redis.Service
    {
        /// <summary>
        /// Sorted Sets是将 Set 中的元素增加了一个权重参数 score,使得集合中的元素能够按 score 进行有序排列
        /// 1、带有权重的元素,比如一个游戏的用户得分排行榜
        /// 2、比较复杂的数据结构,一般用到的场景不算太多
        /// </summary>
        public class RedisZSetService : RedisBase
        {
            #region 添加
    
            /// <summary>
            /// 添加setId/value,默认分数是从1.多*10的9次方以此递增的,自带自增效果
            /// </summary>
            public bool AddItemToSortedSet(string setId, string value)
            {
                return base._redisClient.AddItemToSortedSet(setId, value);
            }
    
            /// <summary>
            /// 添加setId/value,并设置value的分数
            /// </summary>
            public bool AddItemToSortedSet(string setId, string value, double score)
            {
                return base._redisClient.AddItemToSortedSet(setId, value, score);
            }
    
            /// <summary>
            /// 为setId添加values集合,values集合中每个value的分数设置为score
            /// </summary>
            public bool AddRangeToSortedSet(string setId, List<string> values, double score)
            {
                return base._redisClient.AddRangeToSortedSet(setId, values, score);
            }
    
            /// <summary>
            /// 为setId添加values集合,values集合中每个value的分数设置为score
            /// </summary>
            public bool AddRangeToSortedSet(string setId, List<string> values, long score)
            {
                return base._redisClient.AddRangeToSortedSet(setId, values, score);
            }
    
            #endregion 添加
    
            #region 获取
    
            /// <summary>
            /// 获取setId的所有集合
            /// </summary>
            public List<string> GetAllItemsFromSortedSet(string setId)
            {
                return base._redisClient.GetAllItemsFromSortedSet(setId);
            }
    
            /// <summary>
            /// 获取setId的所有集合,倒叙输出
            /// </summary>
            public List<string> GetAllItemsFromSortedSetDesc(string setId)
            {
                return base._redisClient.GetAllItemsFromSortedSetDesc(setId);
            }
    
            /// <summary>
            /// 获取集合,带分数
            /// </summary>
            public IDictionary<string, double> GetAllWithScoresFromSortedSet(string setId)
            {
                return base._redisClient.GetAllWithScoresFromSortedSet(setId);
            }
    
            /// <summary>
            /// 获取setId集合中值为value的下标值
            /// </summary>
            public long GetItemIndexInSortedSet(string setId, string value)
            {
                return base._redisClient.GetItemIndexInSortedSet(setId, value);
            }
    
            /// <summary>
            /// 倒叙排列获取setId集合中值为value的下标值
            /// </summary>
            public long GetItemIndexInSortedSetDesc(string setId, string value)
            {
                return base._redisClient.GetItemIndexInSortedSetDesc(setId, value);
            }
    
            /// <summary>
            /// 获取setId集合中值为value的分数
            /// </summary>
            public double GetItemScoreInSortedSet(string setId, string value)
            {
                return base._redisClient.GetItemScoreInSortedSet(setId, value);
            }
    
            /// <summary>
            /// 获取setId集合中所有数据的总数
            /// </summary>
            public long GetSortedSetCount(string setId)
            {
                return base._redisClient.GetSortedSetCount(setId);
            }
    
            /// <summary>
            /// setId集合数据从分数为fromScore到分数为toScore的数据总数
            /// </summary>
            public long GetSortedSetCount(string setId, double fromScore, double toScore)
            {
                return base._redisClient.GetSortedSetCount(setId, fromScore, toScore);
            }
    
            /// <summary>
            /// 获取setId集合从高分到低分排序数据,分数从fromScore到分数为toScore的数据
            /// </summary>
            public List<string> GetRangeFromSortedSetByHighestScore(string setId, double fromScore, double toScore)
            {
                return base._redisClient.GetRangeFromSortedSetByHighestScore(setId, fromScore, toScore);
            }
    
            /// <summary>
            /// 获取setId集合从低分到高分排序数据,分数从fromScore到分数为toScore的数据
            /// </summary>
            public List<string> GetRangeFromSortedSetByLowestScore(string setId, double fromScore, double toScore)
            {
                return base._redisClient.GetRangeFromSortedSetByLowestScore(setId, fromScore, toScore);
            }
    
            /// <summary>
            /// 获取setId集合从高分到低分排序数据,分数从fromScore到分数为toScore的数据,带分数
            /// </summary>
            public IDictionary<string, double> GetRangeWithScoresFromSortedSetByHighestScore(string setId, double fromScore, double toScore)
            {
                return base._redisClient.GetRangeWithScoresFromSortedSetByHighestScore(setId, fromScore, toScore);
            }
    
            /// <summary>
            /// 获取setId集合从低分到高分排序数据,分数从fromScore到分数为toScore的数据,带分数
            /// </summary>
            public IDictionary<string, double> GetRangeWithScoresFromSortedSetByLowestScore(string setId, double fromScore, double toScore)
            {
                return base._redisClient.GetRangeWithScoresFromSortedSetByLowestScore(setId, fromScore, toScore);
            }
    
            /// <summary>
            /// 获取setId集合数据,下标从fromRank到下标为toRank的数据
            /// </summary>
            public List<string> GetRangeFromSortedSet(string setId, int fromRank, int toRank)
            {
                return base._redisClient.GetRangeFromSortedSet(setId, fromRank, toRank);
            }
    
            /// <summary>
            /// 获取setId集合倒叙排列数据,下标从fromRank到下标为toRank的数据
            /// </summary>
            public List<string> GetRangeFromSortedSetDesc(string setId, int fromRank, int toRank)
            {
                return base._redisClient.GetRangeFromSortedSetDesc(setId, fromRank, toRank);
            }
    
            /// <summary>
            /// 获取setId集合数据,下标从fromRank到下标为toRank的数据,带分数
            /// </summary>
            public IDictionary<string, double> GetRangeWithScoresFromSortedSet(string setId, int fromRank, int toRank)
            {
                return base._redisClient.GetRangeWithScoresFromSortedSet(setId, fromRank, toRank);
            }
    
            /// <summary>
            ///  获取setId集合倒叙排列数据,下标从fromRank到下标为toRank的数据,带分数
            /// </summary>
            public IDictionary<string, double> GetRangeWithScoresFromSortedSetDesc(string setId, int fromRank, int toRank)
            {
                return base._redisClient.GetRangeWithScoresFromSortedSetDesc(setId, fromRank, toRank);
            }
    
            #endregion 获取
    
            #region 删除
    
            /// <summary>
            /// 删除setId集合中值为value的数据
            /// </summary>
            public bool RemoveItemFromSortedSet(string setId, string value)
            {
                return base._redisClient.RemoveItemFromSortedSet(setId, value);
            }
    
            /// <summary>
            /// 删除下标从minRank到maxRank的setId集合数据
            /// </summary>
            public long RemoveRangeFromSortedSet(string setId, int minRank, int maxRank)
            {
                return base._redisClient.RemoveRangeFromSortedSet(setId, minRank, maxRank);
            }
    
            /// <summary>
            /// 删除分数从fromScore到toScore的setId集合数据
            /// </summary>
            public long RemoveRangeFromSortedSetByScore(string setId, double fromScore, double toScore)
            {
                return base._redisClient.RemoveRangeFromSortedSetByScore(setId, fromScore, toScore);
            }
    
            /// <summary>
            /// 删除setId集合中分数最大的数据
            /// </summary>
            public string PopItemWithHighestScoreFromSortedSet(string setId)
            {
                return base._redisClient.PopItemWithHighestScoreFromSortedSet(setId);
            }
    
            /// <summary>
            /// 删除setId集合中分数最小的数据
            /// </summary>
            public string PopItemWithLowestScoreFromSortedSet(string setId)
            {
                return base._redisClient.PopItemWithLowestScoreFromSortedSet(setId);
            }
    
            #endregion 删除
    
            #region 其它
    
            /// <summary>
            /// 判断setId集合中是否存在值为value的数据
            /// </summary>
            public bool SortedSetContainsItem(string setId, string value)
            {
                return base._redisClient.SortedSetContainsItem(setId, value);
            }
    
            /// <summary>
            /// 为setId集合值为value的数据,分数加incrementBy,返回相加后的分数
            /// </summary>
            public double IncrementItemInSortedSet(string setId, string value, double incrementBy)
            {
                return base._redisClient.IncrementItemInSortedSet(setId, value, incrementBy);
            }
    
            /// <summary>
            /// 获取setIds多个集合的交集,并把交集添加到intoSetId集合中,返回交集数据的总数
            /// </summary>
            public long StoreIntersectFromSortedSets(string intoSetId, string[] setIds)
            {
                return base._redisClient.StoreIntersectFromSortedSets(intoSetId, setIds);
            }
    
            /// <summary>
            /// 获取setIds多个集合的并集,并把并集数据添加到intoSetId集合中,返回并集数据的总数
            /// </summary>
            public long StoreUnionFromSortedSets(string intoSetId, string[] setIds)
            {
                return base._redisClient.StoreUnionFromSortedSets(intoSetId, setIds);
            }
    
            #endregion 其它
        }
    }

    使用如下:

    /// <summary>
    /// ZSet:有序、去重,以及交集、并集的使用
    /// 实时排行榜:例如直播刷礼物
    /// </summary>
    public static void ShowZSet()
    {
        using (RedisZSetService service = new RedisZSetService())
        {
            service.FlushAll(); //清理全部数据
    
            //带默认分数
            service.AddItemToSortedSet("advanced", "1");
            service.AddItemToSortedSet("advanced", "2");
            service.AddItemToSortedSet("advanced", "5");
            service.AddItemToSortedSet("advanced", "4");
            service.AddItemToSortedSet("advanced", "7");
            service.AddItemToSortedSet("advanced", "5");
            service.AddItemToSortedSet("advanced", "9");
    
            var result1 = service.GetAllItemsFromSortedSet("advanced");
            var result2 = service.GetAllItemsFromSortedSetDesc("advanced");
    
            //自定义分数
            service.AddItemToSortedSet("Sort", "张三", 10001);
            service.AddItemToSortedSet("Sort", "李四", 10002);
            service.AddItemToSortedSet("Sort", "王五", 10005);
            service.AddItemToSortedSet("Sort", "赵六", 10003);
            service.AddItemToSortedSet("Sort", "钱七", 10004);
            service.AddRangeToSortedSet("Sort", new List<string>() { "老王", "老李", "老孙" }, 11000);
    
            var result3 = service.GetAllWithScoresFromSortedSet("Sort");
            //交集、并集
        }
    }

    运行结果如下所示:

    下面我们就来看下如何使用上面的API来解决实时排行榜的问题

    using System;
    using System.Collections.Generic;
    using System.Threading;
    using System.Threading.Tasks;
    using TianYa.Redis.Service;
    
    namespace MyRedis.Scene
    {
        /// <summary>
        /// 实时排行榜
        /// 使用Redis-IncrementItemInSortedSet
        /// 刷礼物:维度很多,平台/房间/主播/日/周/月/年 => 排行(数据库只存刷礼物的流水,不需要存排名什么的,计算排名交给Redis)
        /// 多个维度就是多个ZSet,刷礼物的时候保存数据库并更新Redis
        /// 刷礼物时增加Redis分数,就可以实时获取最新的排行
        /// </summary>
        public class RankManager
        {
            /// <summary>
            /// 模拟直播平台用户
            /// </summary>
            private static List<string> _listUser = new List<string>()
            {
                "张三","李四","王五","赵六","钱七","周八"
            };
    
            public static void Show()
            {
                using (RedisZSetService service = new RedisZSetService())
                {
                    service.FlushAll(); //清理全部数据
    
                    //模拟给TianYa刷礼物
                    Task.Run(() =>
                    {
                        while (true)
                        {
                            foreach (var user in _listUser)
                            {
                                Thread.Sleep(100);
                                service.IncrementItemInSortedSet("TianYa", user, new Random().Next(1, 100)); //表示在原来刷礼物的基础上增加礼物
                            }
    
                            Thread.Sleep(10 * 1000);
                        }
                    });
    
                    //查看实时排行榜
                    Task.Run(() =>
                    {
                        while (true)
                        {
                            Thread.Sleep(6 * 1000);
    
                            Console.WriteLine("*****************当前排行*****************");
                            int i = 1;
                            foreach (var item in service.GetRangeWithScoresFromSortedSetDesc("TianYa", 0, 9)) //排行前10名
                            {
                                Console.WriteLine($"第{i++}名 {item.Key} 分数{item.Value}");
                            }
                        }
                    });
    
                    Console.Read(); //不能删除
                }
            }
        }
    }

    PS

    1、此处Console.Read()不能删除,没有加Console.Read()的话程序就会离开using的范围,对象就会被Dispose(),这是using方法主动调用的。

    2、Task.Run()新线程内部使用新线程外部的变量,只要不是使用using的,那么新线程中使用的外部变量是不会被提早Dispose的。例如:如果线程是死循环,就会一直在占有对象,就不会被回收的。

    运行结果如下所示:

    至此本文就全部介绍完了,如果觉得对您有所启发请记得点个赞哦!!!

    Demo源码:

    链接:https://pan.baidu.com/s/1B_XUM4Eqc81CJdjufOWS9A 
    提取码:a78n

    此文由博主精心撰写转载请保留此原文链接:https://www.cnblogs.com/xyh9039/p/14008074.html

    版权声明:如有雷同纯属巧合,如有侵权请及时联系本人修改,谢谢!!!

  • 相关阅读:
    Java中HashMap底层实现原理(JDK1.8)源码分析
    java io系列01之 "目录"
    数据结构与算法系列 目录
    Java 集合系列目录(Category)
    ls参数
    在PATH路径中添加新的路径
    目录相关的操作
    chmod
    属性类型
    ls -al
  • 原文地址:https://www.cnblogs.com/xyh9039/p/14008074.html
Copyright © 2011-2022 走看看