Redis學習系列四Hash(字典)
一、簡介
Redis中的Hash字典相當於C#中的Hashtable,是一種無序字典,記憶體儲存了很對的鍵值對,實現上和Hashtable一樣,都是"陣列+連結串列"二維結構,都是對關鍵字(鍵值)進行雜湊操作,講關鍵字雜湊到Hashtable中的某一個槽位中去,這個過程中如果發生了碰撞,雜湊函式可能將不同的關鍵字雜湊到Hashtable中的同一個槽位中去,通過"連結串列的方式"進行連線。
後續可能會寫一個分類的關於C#中常用演算法的文章,但這裡不想介紹太多.
不同的是.Redis中Hash(字典的值)只能是字串,C#中為Hashtable為object
另外關於Hashtable和List等型別,如果你閱讀原始碼,當它們的實際容量達到初始設定的時候,一般都會建立一個新的物件,list中的原先的兩倍,然後將原先的元素複製到新的物件中,這個過程如果裡面的元素超級多,那麼這個開銷非常大,Hashtable也是如此,Hashtable中的這個過程專業術語叫rehash,而Redis為了避免這個開銷,採用了"漸近式的"rhash操作,"漸進"式rehash操作會在rehash的同時,保留新舊兩個hash結構,查詢時會同時查詢這兩個hash物件,接受在後續的定時任務中循序漸進的將舊hash的內容一點點的遷移到新的hash物件中去.當遷移完成,原先的hash結構會被棄用.對應的記憶體會被回收.
二、Hash(字典)的用途
hash結構可以用來儲存使用者資訊,當然字串也可以,但是他和字串的區別如下:
(1)、如果使用字串儲存,我們需要以使用者Id為鍵,然後將使用者所有的資訊序列化成字串存到Redis中,如果使用者的資訊很多,且如果有些業務我們只需要使用者的部分資訊,那我們不得不將使用者所有的資訊取過來,然後反序列化,將業務需要的資料傳遞過去,這個過程,Redis和客戶端的網路請求流量很客觀,當然訪問量少不需要考慮這些問題,但是如果訪問量大的話,你懂的
(2)、如果使用Hash結構儲存,那麼我們可以使用者結構的單個欄位進行儲存,當我們需要使用者資訊時,就可以進行部分讀取,節省網路流量.
(3)、當然Hash也有缺點,他的儲存消耗要高於字串.
三、實戰
centeros7中啟動Redis
還是接著前面隨筆的程式碼進行擴充套件.
C#控制檯:
給RedisClient.cs檔案擴充套件如下幾個方法:
/// <summary> /// 非同步可批量設定Hash(字典) /// </summary> /// <param name="key"></param> /// <param name="entries"></param>/// <returns></returns> public static async Task HashSetAsync(RedisKey key, HashEntry[] entries) { var db = GetDatabase(); await db.HashSetAsync(key, entries); } /// <summary> /// 非同步根據鍵獲取值 /// </summary> /// <param name="key"></param> /// <returns></returns> public static async Task<RedisValue[]> HashValuesAsync(RedisKey key) { var db = GetDatabase(); return await db.HashValuesAsync(key); } /// <summary> /// 非同步根據鍵獲取鍵值對 /// </summary> /// <param name="key"></param> /// <returns></returns> public static async Task<HashEntry[]> HashGetAllAsync(RedisKey key) { var db = GetDatabase(); return await db.HashGetAllAsync(key); } /// <summary> /// 根據鍵和和鍵值對的鍵獲取某個對應的值 /// </summary> /// <param name="key"></param> /// <param name="field"></param> /// <returns></returns> public static async Task<RedisValue> HashGetAsync(RedisKey key,RedisValue field) { var db = GetDatabase(); return await db.HashGetAsync(key, field); }
注:這裡還提供了刪除Hash集合和給對應的Filed加1的操作,但是個人覺得應用場景不多,一般都是每天跑後臺服務持久化到資料庫中對資料庫進行操作,比較好,所以這裡就沒有擴充套件.
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 = "測試Hash鍵"; var age = new KeyValuePair<RedisValue, RedisValue>("Age", 23); var name = new KeyValuePair<RedisValue, RedisValue>("Name", "小超"); var sex = new KeyValuePair<RedisValue, RedisValue>("Sex", "男"); try { await RedisClient.HashSetAsync(key,new HashEntry[] { age,name, sex }); var entries=await RedisClient.HashGetAllAsync(key); //根據鍵獲取鍵值對 foreach (var item in entries) { Console.WriteLine($"鍵:{item.Name},值:{item.Value}"); } //根據鍵獲取值,如果不需要獲取鍵 Console.WriteLine("只獲取值,不獲取鍵的操作"); var values= await RedisClient.HashValuesAsync(key); foreach (var value in values) { Console.WriteLine($"{value}"); } //根據鍵和和鍵值對集合的鍵獲取某個對應的值 Console.WriteLine("根據鍵和和鍵值對集合的鍵獲取某個對應的值的操作"); var fieldValue = await RedisClient.HashGetAsync(key,"Name"); Console.WriteLine($"獲取鍵為:{key}下的鍵值對集合中的鍵為Name的值:{fieldValue}"); } catch (Exception) { //記錄日誌 Console.WriteLine("Redis,使用異常"); } } class UserInfo { internal string Name { get; set; } internal int Age { get; set; } } }