1. 程式人生 > >Redis學習系列六ZSet(有序列表)及Redis資料結構的過期

Redis學習系列六ZSet(有序列表)及Redis資料結構的過期

 

 一、簡介

ZSet可以說是Redis中最有趣的資料結構了,因為他兼具了Hash集合和Set的雙重特性,也是用的最多的,保證了value值的唯一性的同時,,同時又保證了高效能,最主要的是還可以給每個Value設定Source(權重),那麼我們就可以通過權重進行排序,這在業務上是非常常見的,比如很多地方需要,比如我們需要對所有使用者的數學成績進行排序.對英語等等地例子比比皆是,那麼通過ZSet,你將會得到一個響應速度非常快的過程.下面會介紹.

ZSet的內部原理是通過跳躍列表來實現的,這裡還是不想說太多關於演算法的東西.

 

二、ZSet(有序列表)實戰

下面就通過一個列子來講解,主要是給所有使用者的數學成績進行排序的例子.程式碼開始在前面的隨筆上進行擴充套件.

C#控制檯:

給RedisClient.cs擴充套件如下幾個方法:

  /// <summary> 
        /// 非同步不帶權重的向有序列表批量插入資料
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public static async Task<long> SortedSetAddAsync(RedisKey key, SortedSetEntry[] entries)
        {
            
var db = GetDatabase(); return await db.SortedSetAddAsync(key, entries); } /// <summary> /// 非同步帶權重的向有序列表插入單個元素,不管是否存在已有元素,都執行插入操作 /// </summary> /// <param name="key"></param> /// <returns></returns> public static
async Task<RedisValue> SortedSetAddAsync(RedisKey key, RedisValue value,double source) { var db = GetDatabase(); return await db.SortedSetAddAsync(key, value, source); } /// <summary> /// 非同步按權重(範圍為負無窮大到正無窮大排序)得到所有的元素,返回的元素(不包含權重)集合預設的排序時按權重從低到高,可指定權重 /// </summary> /// <param name="key"></param> /// <param name="start">權重下限</param> /// <param name="stop">權重上限</param> /// <returns></returns> public static async Task<RedisValue[]> SortedSetRangeByScoreAsync(RedisKey key, double start = double.NegativeInfinity, double stop = double.PositiveInfinity) { var db = GetDatabase(); return await db.SortedSetRangeByScoreAsync(key, start, stop); } /// <summary> /// 非同步按權重(範圍為負無窮大到正無窮大排序)得到所有的元素,返回的元素(包含權重)集合預設的排序時按權重從低到高,可指定權重 /// </summary> /// <param name="key"></param> /// <param name="start"></param> /// <param name="stop"></param> /// <returns></returns> public static async Task<SortedSetEntry[]> SortedSetRangeByScoreWithScoresAsync(RedisKey key, double start = double.NegativeInfinity, double stop = double.PositiveInfinity) { var db = GetDatabase(); return await db.SortedSetRangeByScoreWithScoresAsync(key, start,stop); } /// <summary> /// 非同步按權重(範圍為負無窮大到正無窮大排序)得到所有的元素,返回的元素(包含權重)集合預設的排序時按權重從高到低,可指定權重 /// </summary> /// <param name="key"></param> /// <param name="start"></param> /// <param name="stop"></param> /// <returns></returns> public static async Task<SortedSetEntry[]> SortedSetRangeByScoreWithScoresDescendingAsync(RedisKey key, double start = double.NegativeInfinity, double stop = double.PositiveInfinity) { var db = GetDatabase(); return await db.SortedSetRangeByScoreWithScoresAsync(key, start, stop,Exclude.None, Order.Descending); } /// <summary> /// 非同步按權重範圍,刪除對應鍵下的所有元素 /// </summary> /// <param name="key"></param> /// <param name="start"></param> /// <param name="stop"></param> /// <returns></returns> public static async Task<long> SortedSetRemoveRangeByScoreAsync(RedisKey key, double start = double.NegativeInfinity, double stop = double.PositiveInfinity) { var db = GetDatabase(); return await db.SortedSetRemoveRangeByScoreAsync(key, start, stop); } /// <summary> /// 非同步刪除指定鍵下的指定值 /// </summary> /// <param name="key"></param> /// <param name="value"></param> /// <returns></returns> public static async Task<bool> SortedSetRemoveAsync(RedisKey key, RedisValue value) { var db = GetDatabase(); return await db.SortedSetRemoveAsync(key, value); }

還可以繼續擴充套件,個人覺得其它方法沒什麼用,就沒有繼續擴充套件了.

Program.cs程式碼如下:

    class Program
    {
        static Program()
        {
            //鏈式配置Redis
            AppConfiguration.Current.ConfigureRedis<RedisConfig>();
        }

        static void Main(string[] args)
        {
            StringSetGetAsync();
            Console.ReadKey();
        }

        static async void StringSetGetAsync()
        {
            var key = "math";
            var computer = new KeyValuePair<RedisValue, double>("小超的使用者Id", 9.0);
            var english = new KeyValuePair<RedisValue, double>("大超的使用者Id", 8.0);
            var math = new KeyValuePair<RedisValue, double>("中超的使用者Id", 7.0);
            var chinese = new KeyValuePair<RedisValue, double>("大大超的使用者Id", 10.0);
            try
            {
                await RedisClient.SortedSetAddAsync(key, new SortedSetEntry[] { computer, math, english, chinese });

                //模擬重複插入,不會發現插入沒有效果,因為Zset自帶去重功能是Set和Hash的組合體
                await RedisClient.SortedSetAddAsync(key, new SortedSetEntry[] { computer, math, english });

                Console.WriteLine("輸出指定鍵的所有元素(不包含權重),預設按權重從小到大排序");
                //輸出指定鍵的所有元素(不包含權重),預設按權重從小到大排序
                var values =await RedisClient.SortedSetRangeByScoreAsync(key);
                foreach (var val in values)
                {
                    Console.WriteLine("值為:{0}",val);
                }
                Console.WriteLine("輸出指定鍵的所有元素(包含權重),預設按權重從小到大排序");
                //輸出指定鍵的所有元素(包含權重),預設按權重從小到大排序
                var oValues= await RedisClient.SortedSetRangeByScoreWithScoresAsync(key);
                foreach (var oVal in oValues)
                {
                    Console.WriteLine("值為:{0},權重為:{1}",oVal.Element,oVal.Score);
                }

                Console.WriteLine("輸出指定鍵的所有元素(包含權重),按權重從大到小排序");
                //輸出指定鍵的所有元素(包含權重),按權重從大到小排序,權重範圍為7~8
                var lValues = await RedisClient.SortedSetRangeByScoreWithScoresDescendingAsync(key,7,8);
                foreach (var lVal in lValues)
                {
                    Console.WriteLine("值為:{0},權重為:{1}", lVal.Element, lVal.Score);
                }
                try
                {
                    await RedisClient.SortedSetRemoveRangeByScoreAsync(key, 7, 8);
                    Console.WriteLine("刪除key為:{0}下權重為7~8之間的所有元素成功!", key);
                }
                catch (Exception ex)
                {
                    Console.WriteLine("刪除元素髮生了異常,資訊為{0}", ex.Message);
                }
                var extraValues = await RedisClient.SortedSetRangeByScoreWithScoresAsync(key);
                foreach (var eVal in extraValues)
                {
                    Console.WriteLine("值為:{0},權重為:{1}", eVal.Element, eVal.Score);
                }

                //輸出一個指定鍵下的指定值
                try
                {
                    var value = "大大超的使用者Id";
                    await RedisClient.SortedSetRemoveAsync(key, value);
                    Console.WriteLine("刪除key為:{0}下值為:{1}的元素成功!", key, value);
                }
                catch (Exception ex)
                {
                    Console.WriteLine("刪除元素髮生了異常,資訊為{0}", ex.Message);
                }

                var exValues = await RedisClient.SortedSetRangeByScoreWithScoresAsync(key);
                foreach (var exVal in exValues)
                {
                    Console.WriteLine("值為:{0},權重為:{1}", exVal.Element, exVal.Score);
                }

            }
            catch (Exception ex)
            {
                //記錄日誌
                Console.WriteLine(ex.Message);
            }
        }
    }

上面的權重就是實際的分數,ok,是不是很強大!

 

三、給Redis資料結構設定過期時間

到這裡Redis的5大基本資料結構算介紹完了,該講講過期的知識,Redis的所有資料結構都可以設定過期時間,時間一到,Redis會自動刪除相應的物件,注意:Redis的5大基本資料結構基本都是鍵值對的關係,最外部有個鍵來指定整個物件,所以Redis的刪除是爭對該鍵對應的物件的.但是Hash結構中,除了指定外部的鍵還可以指定內部的鍵.向下面這樣:

但是Redis的過期是爭對最外部的鍵的.就是整個資料結構.

注:關於String結構也有點特殊,因為它本身也可以設定過期時間,如果你已經給一個字串設定了過期時間,然後呼叫了過期Api修改它,它原先的過期時間會消失.

給RedisClient.cs擴充套件如下方法:

        /// <summary>
        /// 非同步給指定的鍵的物件設定過期時間
        /// </summary>
        /// <param name="key"></param>
        /// <param name="timeSpan"></param>
        /// <returns></returns>
        public static async Task<bool> KeyExpireAsync(RedisKey key,TimeSpan timeSpan)
        {
            var db = GetDatabase();
            return await db.KeyExpireAsync(key, timeSpan);
        }

Program.cs程式碼如下:

    class Program
    {
        static Program()
        {
            //鏈式配置Redis
            AppConfiguration.Current.ConfigureRedis<RedisConfig>();
        }

        static void Main(string[] args)
        {
            StringSetGetAsync();
            Console.ReadKey();
        }

        static async void StringSetGetAsync()
        {
            var key = "math";
            try
            {
                await RedisClient.KeyExpireAsync(key, TimeSpan.FromMilliseconds(1000));
                Console.WriteLine("給指定的鍵設定過期時間異常成功.");
            }
            catch (Exception ex)
            {
                Console.WriteLine("給指定的鍵設定過期時間異常,資訊為:{0}.", ex.Message);
            }
            
        }
    }

對應鍵為math的ZSet結構物件消失了.其餘資料結構自行測試.最好在設定前判斷對應的物件存不存在,雖然我試過了,消失了還可以繼續設定