Redis分佈式緩存系列(五)- Redis中的ZSet類型

本系列將和你們分享Redis分佈式緩存,本章主要簡單介紹下Redis中的ZSet類型,以及如何使用Redis解決實時排行榜問題。html

Sorted Sets是將 Set 中的元素增長了一個權重參數 score,使得集合中的元素可以按 score 進行有序排列。redis

ZSet類型最大的特色就是有序、去重,以及交集、並集的使用。 數據庫

存儲形式:key--SortList<value>緩存

首先先給你們Show一波Redis中與ZSet類型相關的API:數據結構

using System.Collections.Generic;

namespace TianYa.Redis.Service
{
    /// <summary>
    /// Sorted Sets是將 Set 中的元素增長了一個權重參數 score,使得集合中的元素可以按 score 進行有序排列
    /// 一、帶有權重的元素,好比一個遊戲的用戶得分排行榜
    /// 二、比較複雜的數據結構,通常用到的場景不算太多
    /// </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 其它
    }
}

使用以下:dom

/// <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來解決實時排行榜的問題spa

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線程

一、此處Console.Read()不能刪除,沒有加Console.Read()的話程序就會離開using的範圍,對象就會被Dispose(),這是using方法主動調用的。code

二、Task.Run()新線程內部使用新線程外部的變量,只要不是使用using的,那麼新線程中使用的外部變量是不會被提前Dispose的。例如:若是線程是死循環,就會一直在佔有對象,就不會被回收的。

運行結果以下所示:

至此本文就所有介紹完了,若是以爲對您有所啓發請記得點個贊哦!!!

 

Demo源碼:

連接:https://pan.baidu.com/s/1CAqIJ7gupa6gChmu0wh6OA 
提取碼:05em

此文由博主精心撰寫轉載請保留此原文連接:https://www.cnblogs.com/xyh9039/p/14008074.html

相關文章
相關標籤/搜索