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; }
        }
    }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM