zoukankan      html  css  js  c++  java
  • 第二节: Redis之Set类型和SortedSet类型的介绍和案例应用

    一. Set类型基础

    1. 类型说明

      1个key→多个value,value的值不重复!

      Set一种无序且元素内容不重复的集合,不用做重复性判断了,和我们数学中的集合概念相同,可以对多个集合求交集、并集、差集,key可以理解成集合的名字。

    注:set 用哈希表来保持字符串的唯一性,没有先后顺序,是按照自己的一个存储方式来遍历,因为没有保存插入顺序。

    2. 常用指令Api说明

    3. 常用Api说明

    (1).SetAdd:添加数据,可以单独1个key-1个value,也可以1个key-多个value添加

    (2).SetLength:求key集合的数量

    (3).SetContains:判断key集合中是否包含指定值

    (4).SetRandomMember:随机获取指定key集合中的一个值或n个值

    (5).SetMembers:获取key中的所有值,数据类型要一致,便于存储

    (6).SetRemove:删除key集合中的指定value(1个或多个)

    (7).SetPop:随机删除指定key集合中的一个值或n个值,并返回这些值。

    (8).SetCombine:求多个元素的交并差

      a.SetOperation.Intersect:交集

      b.SetOperation.Union:并集

      c.SetOperation.Difference:差集

    (9).SetCombineAndStore:把多个元素的交并差,放到一个新的元素集合中

     代码分享:

     1             //1.添加,value不重复,添加多个Marren1也只有一个
     2             //单个
     3             db.SetAdd("Keen", "Marren1");
     4             db.SetAdd("Keen", "Marren2");
     5             db.SetAdd("Keen", "Marren3");
     6             db.SetAdd("Keen", "Marren2");
     7             db.SetAdd("Keen", "Marren1");
     8             //多个
     9             string[] arryList = { "Marren4", "Marren5", "Marren6", "Marren3" };
    10             RedisValue[] valueList = arryList.Select(u => (RedisValue)u).ToArray();
    11             db.SetAdd("Keen", valueList);
    12 
    13             //2. 获取key集合值的数量
    14             long d1 = db.SetLength("Keen");
    15 
    16             //3. 判断key集合中是否包含指定值
    17             bool d2 = db.SetContains("Keen", "Marren2");
    18 
    19             //4. 随机获取key集合中的一个值
    20             var d3 = db.SetRandomMember("Keen");
    21             //随机获取key集合中的3个值
    22             var d33 = db.SetRandomMembers("Keen", 3).Select(u => (string)u).ToList();
    23 
    24             //5. 获取key中的所有值,数据类型要一致
    25             var rValue = db.SetMembers("Keen");
    26             List<string> d4 = new List<string>();
    27             foreach (var item in rValue)
    28             {
    29                 d4.Add(item);
    30             }
    31 
    32             //6. 删除key集合中的指定value
    33             //单个value
    34             bool d5 = db.SetRemove("Keen", "Marren1");
    35             //多个value
    36             string[] dDelList = { "Marren2", "Marren3", };
    37             RedisValue[] DelList = dDelList.Select(u => (RedisValue)u).ToArray();
    38             long d6 = db.SetRemove("Keen", DelList);
    39 
    40             //7. 随机删除key集合中的一个值,并返回该值
    41             var d7 = db.SetPop("Keen");
    42             //随机删除key集合中的2个值,并返回这2个值
    43             var d77 = db.SetPop("Keen", 2).Select(u => (string)u).ToList();
    44 
    45 
    46             //8. 获取几个集合的交集、并集、差集(重点)
    47             //准备数据
    48             db.SetAdd("ypf", "h1");
    49             db.SetAdd("ypf", "h2");
    50             db.SetAdd("ypf", "h3");
    51             db.SetAdd("ypf", "h4");
    52             db.SetAdd("maru", "h4");
    53             db.SetAdd("maru", "h5");
    54             db.SetAdd("maru", "h6");
    55             //下面求两个元素的交并差,也可以求多个
    56             string[] arry = { "maru", "ypf" };
    57             RedisKey[] keyList = arry.Select(u => (RedisKey)u).ToArray();
    58             //交集(共同的部分 h4)
    59             var d8 = db.SetCombine(SetOperation.Intersect, keyList).Select(u => (string)u).ToList();
    60             //并集(加到一起,去重, h1-h6)
    61             var d9 = db.SetCombine(SetOperation.Union, keyList).Select(u => (string)u).ToList();
    62             //差集(差集有两个,上面的是h5 h6, 如果颠倒maru和ypf顺序,差集是h1 h2 h3)
    63             var d10 = db.SetCombine(SetOperation.Difference, keyList).Select(u => (string)u).ToList();
    64 
    65             //获取交集并存到key=ypf1,返回集合元素的个数
    66             long d11 = db.SetCombineAndStore(SetOperation.Intersect, "ypf1", keyList);
    67             //获取并集并存到key=ypf2 ,返回集合元素的个数
    68             long d12 = db.SetCombineAndStore(SetOperation.Union, "ypf2", keyList);
    69             //获取差集并存到key=ypf3 ,返回集合元素的个数
    70             long d13 = db.SetCombineAndStore(SetOperation.Union, "ypf3", keyList);
    View Code

    二. Set类型案例

    1.抽奖

    (1) 背景:用户参与抽奖,抽奖大致分两类:

     A:只抽1次,1次抽n个人。

     B:抽多次,比如三等奖抽3名,二等奖抽2名,一等奖抽1名。

    (2) 技术分析:

    主要利用Set结构元素的不重复性和获取随机数的方法来实现,以“specialPrize”为key,参与用户的id当做value

    A:用户点击参与抽奖则执行SetAdd方法。

    B:可以获取所有参与用户SetMembers 和 判断某个用户是否参与抽奖了SetContains

    C:随机抽一次奖用SetRandomMember或SetRandomMembers,因为不需要删除

          多次抽奖用SetPop,因为抽完要删掉。

    (3) 代码分析

     1           //1.模拟用户参与抽奖
     2             for (int i = 100; i <= 115; i++)
     3             {
     4                 db.SetAdd("specialPrize", i.ToString());
     5             }
     6             //2. 获取所有的参与抽奖的用户
     7             var data = db.SetMembers("specialPrize").Select(u => (string)u).ToList();
     8             string idList = "";
     9             foreach (var item in data)
    10             {
    11                 idList = idList + "," + item;
    12             }
    13             Console.WriteLine($"参与抽奖的用户有:{idList}");
    14             //3. 判断用户103是否参与抽奖了
    15             var data2 = db.SetContains("specialPrize", "103");
    16             if (data2 == true)
    17             {
    18                 Console.WriteLine($"用户103参与了抽奖");
    19             }
    20             else
    21             {
    22                 Console.WriteLine($"用户103没有参与抽奖");
    23             }
    24             //4. 抽奖
    25             //4.1 只抽一次奖,抽奖人数为两名
    26             {
    27                 var d1 = db.SetRandomMembers("specialPrize", 2).Select(u => (string)u).ToList();
    28                 foreach (var item in d1)
    29                 {
    30                     Console.WriteLine($"获奖用户为:{item}");
    31                 }
    32             }
    33             //4.2 抽三次奖
    34             {
    35                 var d1 = db.SetPop("specialPrize", 3).Select(u => (string)u).ToList();
    36                 foreach (var item in d1)
    37                 {
    38                     Console.WriteLine($"三等奖用户为:{item}");
    39                 }
    40                 var d2 = db.SetPop("specialPrize", 2).Select(u => (string)u).ToList();
    41                 foreach (var item in d2)
    42                 {
    43                     Console.WriteLine($"二等奖用户为:{item}");
    44                 }
    45                 var d3 = db.SetPop("specialPrize", 1).Select(u => (string)u).ToList();
    46                 foreach (var item in d3)
    47                 {
    48                     Console.WriteLine($"一等奖用户为:{item}");
    49                 }
    50             }
    View Code

    2. 微信或微博中消息的点赞(或者某篇文章的收藏)

    (1). 背景

      微信朋友圈用户A的某条消息的点赞功能,要实现点赞、取消点赞、获取点赞列表、获取点赞用户数量、判断某用户是否点赞过。

    (2). 技术分析

      利用Set结构, 以用户Id-消息id作为key,点赞过该消息的用户id作为value。

      A:点赞 SetAdd方法

      B:取消点赞 SetRemove方法

      C:获取点赞列表 SetMembers方法

      D:获取点赞用户数量 SetLength方法

      E:判断某用户是否点赞过 SetContains方法

      该案例容易理解,此处不写代码了。

    3.关注模型

    (1).背景

      比如微博关注或者共同好友的问题,以微博关注为例,要实现:同时关注、关注的和、关注A的用户中也关注B的、当A进入B页面,求可能认识的人。

    (2). 技术分析

    利用Set结构,一个博主对应一个Set结构,博主的id作为key,关注该博主的用户id作为value。

    A:关注和取消关注: SetAdd方法 和 SetRemove方法

    B:同时关注:求交集

    C:关注的和:求并集

    D:关注A的用户中也关注B的:遍历A中的用户,利用SetContains判断是否B中也存在

    E:当A进入B页面,求可能认识的人:这里指的是关注B中的用户 扣去 里面也关注A的用户,就是A可能认识的人。

      求差集:B-A

    (3). 代码分享

     1             //关注lmr的用户有:
     2             db.SetAdd("lmr", "小1");
     3             db.SetAdd("lmr", "小2");
     4             db.SetAdd("lmr", "小3");
     5             db.SetAdd("lmr", "小4");
     6             db.SetAdd("lmr", "小5");
     7             db.SetAdd("lmr", "rbp");
     8 
     9             //关注ypf的用户有:
    10             db.SetAdd("ypf", "小4");
    11             db.SetAdd("ypf", "小5");
    12             db.SetAdd("ypf", "小6");
    13             db.SetAdd("ypf", "小7");
    14             db.SetAdd("ypf", "小8");
    15             db.SetAdd("ypf", "rbp");
    16 
    17             //同时关注lmr和ypf的用户有:
    18             string[] arry1 = { "lmr", "ypf" };
    19             RedisKey[] keyList1 = arry1.Select(u => (RedisKey)u).ToArray();
    20             var d1 = db.SetCombine(SetOperation.Intersect, keyList1).Select(u => (string)u).ToList();  //交集
    21             foreach (var item in d1)
    22             {
    23                 Console.WriteLine("同时关注lmr和ypf的用户有:" + item);
    24             }
    25 
    26             //关注lmr和ypf的用户有(需要去重):
    27             string[] arry2 = { "lmr", "ypf" };
    28             RedisKey[] keyList2 = arry2.Select(u => (RedisKey)u).ToArray();
    29             var d2 = db.SetCombine(SetOperation.Union, keyList2).Select(u => (string)u).ToList();  //并集
    30             foreach (var item in d2)
    31             {
    32                 Console.WriteLine("关注lmr和ypf的用户有:" + item);
    33             }
    34 
    35             //关注lmr的人中也关注ypf的有:
    36             var d3 = db.SetMembers("lmr").Select(u => (string)u).ToList();
    37             foreach (var item in d3)
    38             {
    39                 var isExist = db.SetContains("ypf", item);
    40                 if (isExist)
    41                 {
    42                     Console.WriteLine("关注lmr的人中也关注ypf的有:" + item);
    43                 }
    44             }
    45 
    46             //当ypf进入lmr的页面,显示可能认识的人(应该显示:小1,小2,小3)
    47             string[] arry4 = { "lmr", "ypf" };  // lmr-ypf
    48             RedisKey[] keyList4 = arry4.Select(u => (RedisKey)u).ToArray();
    49             var d4 = db.SetCombine(SetOperation.Difference, keyList4).Select(u => (string)u).ToList();  //差集 lmr-ypf
    50             foreach (var item in d4)
    51             {
    52                 Console.WriteLine("当ypf进入lmr的页面,显示可能认识的人:" + item);
    53             } 
    View Code

    4. 利用唯一性,可以统计访问网站的所有IP

    三. SortedSet类型基础

    1. 类型说明

      将Set中的元素增加了一个权重参数 score,使得集合中的元素能够按 score 进行有序排列, 同样元素名称也不能重复 三个字段:key-member-score, key键,member值,score:权重或者打分值

    2. 常用Api

    (1).SortedSetAdd:增加,可以一次增加一个member,也可以一次增加多个member

    (2).SortedSetIncrement 和 SortedSetDecrement:Score值自增或自减,如果不存在这member值,则执行增加操作,并返回当前Score值。

    (3).获取相关

      SortedSetRangeByRank:根据索引获取member值,默认是升序,可以获取指定索引内的member值

      SortedSetRangeByScore:根据score获取member值,默认是升序,可以获取指定score开始和结束的member值,后面的skip和take用于分页

      SortedSetRangeByValue:根据member获取member值,默认是升序,可以获取指定member开始和结束的值,后面的skip和take用于分页

      SortedSetRangeByRankWithScores:获取member和score值,可以只返回 start-stop 这个索引排序内的值(默认升序),后面的skip和take用于分页

      SortedSetScore:获取指定key指定member的score值

      SortedSetLength:获取集合的数量

    (4).删除相关

      SortedSetRemove:删除指定key和指定member,member可以是1个或多个

      SortedSetRemoveRangeByRank:删除指定索引开始到结束

      SortedSetRemoveRangeByScore:删除指定分开始到结束 (5分-8分)

      SortedSetRemoveRangeByValue::删除指定起始值和结束值(这里指定是member)

     代码分享:

      1             //1.增加
      2             //1.1 SortedSetAdd(RedisKey key, RedisValue member, double score),如果member之前存在,则会覆盖前面的score
      3             db.SortedSetAdd("一年级", "ypf1", 1);
      4             for (int i = 2; i < 10; i++)
      5             {
      6                 db.SortedSetAdd("一年级", "ypf" + i, i);
      7             }
      8             //db.SortedSetAdd("一年级", "ypf1", 120);   会覆盖前面的score
      9 
     10             //1.2 score自增(刚开始如果没有这个member,会默认添加进去)
     11             var dd1 = db.SortedSetIncrement("一年级", "ypf", 1);
     12             //1.3 Score自减(刚开始如果没有这个member,会默认添加进去)
     13             var dd2 = db.SortedSetDecrement("一年级", "ypf", 3);
     14 
     15             //2.获取
     16             {
     17                 //2.1 SortedSetRangeByRank:获取的是member值,可以只返回 start-stop 这个排序内的值
     18                 //2.1.1 默认是升序获取所有member值
     19                 var d1 = db.SortedSetRangeByRank("一年级");
     20                 string[] d1Arry1 = d1.Select(u => (string)u).ToArray();
     21 
     22                 //2.1.2 降序获取所有member值
     23                 var d2 = db.SortedSetRangeByRank("一年级", 0, -1, Order.Descending);
     24                 string[] d1Arry2 = d2.Select(u => (string)u).ToArray();
     25 
     26                 //2.1.3 降序获取排名前4的member值
     27                 var d3 = db.SortedSetRangeByRank("一年级", 0, 3, Order.Descending);
     28                 string[] d1Arry3 = d3.Select(u => (string)u).ToArray();
     29             }
     30             {
     31                 //2.2 SortedSetRangeByScore:获取的是member值,可以只返回 start-stop 这个score内的值,后面的skip和take用于分页
     32                 //SortedSetRangeByScore(RedisKey key, double start = double.NegativeInfinity, double stop = double.PositiveInfinity, Exclude exclude = Exclude.None, Order order = Order.Ascending, long skip = 0, long take = -1, CommandFlags flags = CommandFlags.None);
     33                 //2.2.1 默认是升序获取所有member值
     34                 var d1 = db.SortedSetRangeByScore("一年级");
     35                 string[] d1Arry1 = d1.Select(u => (string)u).ToArray();
     36 
     37                 //2.2.2 降序获取所有member值
     38                 var d2 = db.SortedSetRangeByScore("一年级", double.NegativeInfinity, double.PositiveInfinity, Exclude.None, Order.Descending);
     39                 string[] d1Arry2 = d2.Select(u => (string)u).ToArray();
     40 
     41                 //2.2.3 降序获取score在【2,6】分内的值
     42                 var d3 = db.SortedSetRangeByScore("一年级", 2, 6, Exclude.None, Order.Descending);
     43                 string[] d1Arry3 = d3.Select(u => (string)u).ToArray();
     44             }
     45             {
     46                 //2.3 SortedSetRangeByValue:获取的是member值,可以只返回 member开始到结束的值,后面的skip和take用于分页
     47                 //2.3.1 默认是升序获取所有score值
     48                 var d1 = db.SortedSetRangeByValue("一年级");
     49                 string[] d1Arry1 = d1.Select(u => (string)u).ToArray();
     50 
     51                 //2.3.2 降序获取member在【ypf2,ypf6】分内的值
     52                 var d3 = db.SortedSetRangeByValue("一年级", "ypf2", "ypf6", Exclude.None, Order.Descending);
     53                 string[] d1Arry3 = d3.Select(u => (string)u).ToArray();
     54             }
     55 
     56             {
     57                 //2.4 SortedSetRangeByRankWithScores:获取member和score值,可以只返回 start-stop 这个索引排序内的值,后面的skip和take用于分页
     58                 //2.4.1 默认是升序
     59                 SortedSetEntry[] d1 = db.SortedSetRangeByRankWithScores("一年级");
     60                 Dictionary<string, double> dic = new Dictionary<string, double>();
     61                 foreach (var item in d1)
     62                 {
     63                     dic.Add(item.Element, item.Score);
     64                 }
     65                 //2.4.2 降序获取所有
     66                 SortedSetEntry[] d2 = db.SortedSetRangeByRankWithScores("一年级", 0, -1, Order.Descending);
     67                 Dictionary<string, double> dic2 = new Dictionary<string, double>();
     68                 foreach (var item in d2)
     69                 {
     70                     dic2.Add(item.Element, item.Score);
     71                 }
     72                 //2.4.3 降序获取排名前4的member和score值
     73                 SortedSetEntry[] d3 = db.SortedSetRangeByRankWithScores("一年级", 0, 3, Order.Descending);
     74                 Dictionary<string, double> dic3 = new Dictionary<string, double>();
     75                 foreach (var item in d3)
     76                 {
     77                     dic3.Add(item.Element, item.Score);
     78                 }
     79             }
     80             {
     81                 //2.5 获取指定key指定member的score值
     82                 var d1 = db.SortedSetScore("一年级", "ypf2");
     83             }  
     84             //3. 获取集合的数量
     85             long l1 = db.SortedSetLength("一年级");
     86 
     87 
     88             //4. 删除
     89             //{
     90             //    //4.1.SortedSetRemove:删除指定key和指定member,member可以是1个或多个
     91             //    bool num1 = db.SortedSetRemove("一年级", "ypf1");
     92             //    string[] arry1 = { "ypf2", "ypf3", "ypf4" };
     93             //    RedisValue[] newArry1 = arry1.Select(u => (RedisValue)u).ToArray();
     94             //    long num2 = db.SortedSetRemove("一年级", newArry1);
     95             //}
     96 
     97             //{
     98             //    //4.2.SortedSetRemoveRangeByRank:删除指定索引开始到结束
     99             //    long num = db.SortedSetRemoveRangeByRank("一年级", 0, 2);
    100             //}
    101 
    102             //{
    103             //    //4.3.SortedSetRemoveRangeByScore:删除指定分开始到结束  (5分-8分)
    104             //    long num = db.SortedSetRemoveRangeByScore("一年级", 5, 8);
    105             //}
    106 
    107             //{
    108             //    //4.4.SortedSetRemoveRangeByValue:删除指定起始值和结束值(这里指定是member)
    109             //    long num = db.SortedSetRemoveRangeByValue("一年级", "ypf3", "ypf6");
    110             //}

    四. SortedSet案例

    1. 热词搜索

    (1). 需求:

      统计某个网站热词搜索,并实时显示前5名的词及其搜索次数。

    PS:实时显示无非就是每隔几秒查询一次,大部分情况我们不建议直接去Redis里查排名,可以把前五名的相关数据存储到Redis的String结构中, 设置5分钟过期。

    下面的案例不考虑上面的PS情况,不考虑IP限制问题(前面访问量案例做过),仅仅做最简单的热词搜索,刷新显示

    (2).技术分析:

      利用Redis中SortedSet(key-member-score)这一数据结构,利用SortedSetIncrement方法的原子性,每搜索一个词,Score值加1(member不存在则执行的是增加操作), 然后再利用SortedSetRangeByRankWithScores方法获取Score值前五的memeber和Score数据

    代码分享:

            /// <summary>
            /// 热词搜索页面
            /// </summary>
            /// <returns></returns>
            public IActionResult Index()
            {
                //获取排名前5的数据
                var data = _redis.SortedSetRangeByRankWithScores("hotWord", 0, 4, Order.Descending);
                Dictionary<string, double> dic3 = new Dictionary<string, double>();
                foreach (var item in data)
                {
                    dic3.Add(item.Element, item.Score);
                }
                ViewBag.data = dic3;
    
                return View();
            }
    
            /// <summary>
            /// 查询接口
            /// </summary>
            /// <param name="word"></param>
            /// <returns></returns>
            public string HotSearch(string word)
            {
                try
                {
                    _redis.SortedSetIncrement("hotWord", word, 1);
                    return "ok";
                }
                catch (Exception ex)
                {
                    return "error";
                }
            }

    2.宝宝投票

      分析:和上面的热词原理一样,利用SortedSet的1个key对应多个不重复的 member-score,key用来存储一个标记,比如叫做“AllChildren”,代表该标记下存储宝宝的投票情况,member可以存储

    宝宝的标记id,score存储该宝宝的投票数量.

    同样原理:利用SortedSetIncrement进行自增存储,利用SortedSetRangeByRankWithScores获取排名情况

    3.高积分用户排行榜

       类似案例:主播-粉丝刷礼物的排行榜(同时看主播的排行和每个主播下面粉丝的排行), 原理和上面都一样

     

     总结: 

      SortedSet和String利用其自增自减(并返回当前值)原子性均可以实现计数器的作用,String是针对单个,Sorted是针对某个类别下的多个或每一个,并且实现排序功能。 Hash类型也能实现某个类别下多个物品的计数,但它不具有排序功能。

    !

    • 作       者 : Yaopengfei(姚鹏飞)
    • 博客地址 : http://www.cnblogs.com/yaopengfei/
    • 声     明1 : 本人才疏学浅,用郭德纲的话说“我是一个小学生”,如有错误,欢迎讨论,请勿谩骂^_^。
    • 声     明2 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。
     
  • 相关阅读:
    Project项目视图
    Console(控制台视图)
    Layout布局列表
    Layers层列表
    帐户下拉
    TransformGizmo(变换)切换
    Play播放控件
    变换工具
    工具栏
    Help帮助
  • 原文地址:https://www.cnblogs.com/yaopengfei/p/11936144.html
Copyright © 2011-2022 走看看