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 staticasync 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結構物件消失了.其餘資料結構自行測試.最好在設定前判斷對應的物件存不存在,雖然我試過了,消失了還可以繼續設定