C#中使用Redis學習二 在.NET4.5中使用redis hash操作

風靈使發表於2018-04-25

摘要

上一篇講述了安裝redis客戶端和伺服器端,也大體地介紹了一下redis。本篇著重講解.NET4.0 和 .NET4.5中如何使用redis和C# redis操作雜湊表。並且會將封裝的一些程式碼貼一下。在講解的過程中,我打算結合redis操作命令一起敘述,算是作為對比吧。這樣也能讓讀者清楚了 解,所分裝的程式碼對應的redis的哪一些操作命令。

hash雜湊表簡介

這裡僅僅是對雜湊表作簡單概念級介紹(摘自csdn),如果需要,自己去研究。

1、雜湊表的概念

雜湊表(Hash Table)也叫雜湊表,是根據關鍵碼值(Key Value)而直接進行訪問的資料結構。它通過把關鍵碼值對映到雜湊表中的一個位置來訪問記錄,以加快查詢的速度。這個對映函式就做雜湊函式,存放記錄的陣列叫做雜湊表。

2、雜湊表查詢的時間複雜度

雜湊表儲存的是鍵值對,其查詢的時間複雜度與元素數量多少無關,雜湊表在查詢元素時是通過計算雜湊碼值來定位元素的位置從而直接訪問元素的,因此,雜湊表查詢的時間複雜度為O(1)。

如何在.NET4.0/4.5中安裝redis元件?

在上一篇博文中,安裝好的redis伺服器端,要記得開啟服務。然後再在.NET4.5(.NET4.0同理)專案中新增對redis操作的dll檔案的引用。引用的步驟如下:

第一步:右鍵專案中的引用,選擇“管理NuGet程式包”;

第二步:在搜尋欄中輸入“Redis client for the Redis NoSQL DB”,聯機查詢;如下圖:

聯機搜尋結構中的第一個(如上圖紅色區域的元件,其版本號為4.0.35)就是要安裝的元件了。接下來我們就繼續點選“安裝”按鈕,進入下載元件,等下載完成後,繼續選擇“接受條款”,然後繼續安裝。安裝過程中會出現下圖情況:

這句紅色錯誤的意思是我們安裝的ServiceStack.Interfaces 4.0.35版本與當前的.NET4.5框架中元件不相容。這說明我們需要降低或是提高.NET版本解決此問題。我安裝的是.NET4.5,所以我只能降 低版本。降低.NET版本的方法大家應該都知道的。我就在累述一次,希望大家不要嫌煩,畢竟還有新手在。方法:右鍵專案檔案選擇“屬性”==》“應用程 序”==》“目標框架”,然後選擇各個版本去嘗試之前的兩步操作,直到可以安裝為止。

我試過了,.NET4.0也會遇到同樣問題,直到.NET3.5才可以。當然此時安裝的ServiceStack.Interfaces 版本是1.0.0.0 ,這樣我們再把.NET版本恢復4.5即可使用了。,其實是變相在.NET4.0/4.5下使用redis客戶端。不知道各位有沒有遇到這樣的問題,還是 直接拷貝別人的dll檔案。當自己親自去操作的時候,才會發現,其實就算是安裝一個元件都可能會出現各種各樣的問題。所以,要想了解全過程的話,我們還是 要身體力行的啊。好了,這樣就說明在.NET4.5下安裝好了redis client了。

實戰:在專案中運用redis程式碼詳解

這部分主要是講解怎樣連線到redis伺服器。其中包含很多配置,我就用程式碼去說明一切了。配置檔案如下程式碼:

<configSections>
   <section name="RedisConfig" type="RedisDemo.Common.Redis.RedisConfigInfo, RedisDemo"/>
</configSections>
<RedisConfig WriteServerList="127.0.0.1:6379" ReadServerList="127.0.0.1:6379" MaxWritePoolSize="60" MaxReadPoolSize="60" AutoStart="true" LocalCacheTime="180" RecordeLog="false"/>

在這裡對RedisConfig這段配置檔案的屬性作下說明。

WriteServerList:可寫的Redis連結地址。

ReadServerList:可讀的Redis連結地址。

MaxWritePoolSize:最大寫連結數。

MaxReadPoolSize:最大讀連結數。

AutoStart:自動重啟。

LocalCacheTime:本地快取到期時間,單位:秒。

RecordeLog:是否記錄日誌,該設定僅用於排查redis執行時出現的問題,如redis工作正常,請關閉該項。

RedisConfigInfo類是記錄redis連線資訊,此資訊和配置檔案中的RedisConfig相呼應。cs程式碼如下:(這段程式碼不是我自己寫的,但是我覺得應該這樣設計。所以,copy了一下別人的程式碼。)

using System.Configuration;

namespace RedisDemo.Common.Redis
{
    public sealed class RedisConfigInfo : ConfigurationSection
    {
        public static RedisConfigInfo GetConfig()
        {
            var section = (RedisConfigInfo)ConfigurationManager.GetSection("RedisConfig");
            return section;
        }
        public static RedisConfigInfo GetConfig(string sectionName)
        {
            var section = (RedisConfigInfo)ConfigurationManager.GetSection("RedisConfig");
            if (section == null)
            {
                throw new ConfigurationErrorsException("Section " + sectionName + " is not found.");
            }
            return section;
        }
        /// <summary>
        /// 可寫的Redis連結地址
        /// </summary>
        [ConfigurationProperty("WriteServerList", IsRequired = false)]
        public string WriteServerList
        {
            get
            {
                return (string)base["WriteServerList"];
            }
            set
            {
                base["WriteServerList"] = value;
            }
        }        
        /// <summary>
        /// 可讀的Redis連結地址
        /// </summary>
        [ConfigurationProperty("ReadServerList", IsRequired = false)]
        public string ReadServerList
        {
            get
            {
                return (string)base["ReadServerList"];
            }
            set
            {
                base["ReadServerList"] = value;
            }
        }        
        /// <summary>
        /// 最大寫連結數
        /// </summary>
        [ConfigurationProperty("MaxWritePoolSize", IsRequired = false, DefaultValue = 5)]
        public int MaxWritePoolSize
        {
            get
            {
                var maxWritePoolSize = (int)base["MaxWritePoolSize"];
                return maxWritePoolSize > 0 ? maxWritePoolSize : 5;
            }
            set
            {
                base["MaxWritePoolSize"] = value;
            }
        }        
        /// <summary>
        /// 最大讀連結數
        /// </summary>
        [ConfigurationProperty("MaxReadPoolSize", IsRequired = false, DefaultValue = 5)]
        public int MaxReadPoolSize
        {
            get
            {
                var maxReadPoolSize = (int)base["MaxReadPoolSize"];
                return maxReadPoolSize > 0 ? maxReadPoolSize : 5;
            }
            set
            {
                base["MaxReadPoolSize"] = value;
            }
        }        
        /// <summary>
        /// 自動重啟
        /// </summary>
        [ConfigurationProperty("AutoStart", IsRequired = false, DefaultValue = true)]
        public bool AutoStart
        {
            get
            {
                return (bool)base["AutoStart"];
            }
            set
            {
                base["AutoStart"] = value;
            }
        }        
        /// <summary>
        /// 本地快取到期時間,單位:秒
        /// </summary>
        [ConfigurationProperty("LocalCacheTime", IsRequired = false, DefaultValue = 36000)]
        public int LocalCacheTime
        {
            get
            {
                return (int)base["LocalCacheTime"];
            }
            set
            {
                base["LocalCacheTime"] = value;
            }
        }
        /// <summary>
        /// 是否記錄日誌,該設定僅用於排查redis執行時出現的問題,如redis工作正常,請關閉該項
        /// </summary>
        [ConfigurationProperty("RecordeLog", IsRequired = false, DefaultValue = false)]
        public bool RecordeLog
        {
            get
            {
                return (bool)base["RecordeLog"];
            }
            set
            {
                base["RecordeLog"] = value;
            }
        }
    }
}

RedisManager類主要是建立連結池管理物件的。

using System.Linq;
using ServiceStack.Redis;
using System.Collections.Generic;

namespace RedisDemo.Common.Redis
{
    public class RedisManager
    {
        /// <summary>
        /// redis配置檔案資訊
        /// </summary>
        private static readonly RedisConfigInfo RedisConfigInfo = RedisConfigInfo.GetConfig();

        private static PooledRedisClientManager _prcm;

        /// <summary>
        /// 靜態構造方法,初始化連結池管理物件
        /// </summary>
        static RedisManager()
        {
            CreateManager();
        }

        /// <summary>
        /// 建立連結池管理物件
        /// </summary>
        private static void CreateManager()
        {
            var writeServerList = SplitString(RedisConfigInfo.WriteServerList, ",");
            var readServerList = SplitString(RedisConfigInfo.ReadServerList, ",");

            _prcm = new PooledRedisClientManager(writeServerList,readServerList,
                             new RedisClientManagerConfig
                             {
                                 MaxWritePoolSize = RedisConfigInfo.MaxWritePoolSize,
                                 MaxReadPoolSize = RedisConfigInfo.MaxReadPoolSize,
                                 AutoStart = RedisConfigInfo.AutoStart,
                             });
        }

        private static IEnumerable<string> SplitString(string strSource, string split)
        {
            return strSource.Split(split.ToArray());
        }

        /// <summary>
        /// 客戶端快取操作物件
        /// </summary>
        public static IRedisClient GetClient()
        {
            if (_prcm == null)
            {
                CreateManager();
            }
            return _prcm.GetClient();
        }

    }
}

實戰:封裝redis對雜湊表操作的程式碼

實戰中,我們操作redis最好還是要封裝提煉一下的。提煉的目的是為了以後程式碼的重用。程式碼封裝重用的好處我就不多說了,這個不是本次主要討論的。下面是我所用專案中分裝的程式碼,其中有的部分我修改過了,貼出來讓大家看下。個人感覺不錯:^_^

RedisOperatorBase類,是redis操作的基類,繼承自IDisposable介面,主要用於釋放記憶體。

using System;
using ServiceStack.Redis;

namespace RedisDemo.Common.Redis.RedisOperator
{
    public abstract class RedisOperatorBase : IDisposable
    {
        protected IRedisClient Redis { get; private set; }
        private bool _disposed = false;
        protected RedisOperatorBase()
        {
            Redis = RedisManager.GetClient();
        }        
        protected virtual void Dispose(bool disposing)
        {
            if (!this._disposed)
            {
                if (disposing)
                {
                    Redis.Dispose();
                    Redis = null;
                }
            }
            this._disposed = true;
        }
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
        /// <summary>
        /// 儲存資料DB檔案到硬碟
        /// </summary>
        public void Save()
        {
            Redis.Save();
        }
        /// <summary>
        /// 非同步儲存資料DB檔案到硬碟
        /// </summary>
        public void SaveAsync()
        {
            Redis.SaveAsync();
        }
    }
}

HashOperator類,是操作雜湊表類。繼承自RedisOperatorBase類,程式碼中有詳細註釋,理解起來一目瞭然。

using System;
using System.Collections.Generic;
using ServiceStack.Text;

namespace RedisDemo.Common.Redis.RedisOperator
{
    public class HashOperator : RedisOperatorBase
    {
        public HashOperator() : base() { }
        /// <summary>
        /// 判斷某個資料是否已經被快取
        /// </summary>
        public bool Exist<T>(string hashId, string key)
        {
            return Redis.HashContainsEntry(hashId, key);
        }
        /// <summary>
        /// 儲存資料到hash表
        /// </summary>
        public bool Set<T>(string hashId, string key, T t)
        {
            var value = JsonSerializer.SerializeToString<T>(t);
            return Redis.SetEntryInHash(hashId, key, value);
        }
        /// <summary>
        /// 移除hash中的某值
        /// </summary>
        public bool Remove(string hashId, string key)
        {
            return Redis.RemoveEntryFromHash(hashId, key);
        }
        /// <summary>
        /// 移除整個hash
        /// </summary>
        public bool Remove(string key)
        {
            return Redis.Remove(key);
        }
        /// <summary>
        /// 從hash表獲取資料
        /// </summary>
        public T Get<T>(string hashId, string key)
        {
            string value = Redis.GetValueFromHash(hashId, key);
            return JsonSerializer.DeserializeFromString<T>(value);
        }
        /// <summary>
        /// 獲取整個hash的資料
        /// </summary>
        public List<T> GetAll<T>(string hashId)
        {
            var result = new List<T>();
            var list = Redis.GetHashValues(hashId);
            if (list != null && list.Count > 0)
            {
                list.ForEach(x =>
                {
                    var value = JsonSerializer.DeserializeFromString<T>(x);
                    result.Add(value);
                });
            }
            return result;
        }
        /// <summary>
        /// 設定快取過期
        /// </summary>
        public void SetExpire(string key, DateTime datetime)
        {
            Redis.ExpireEntryAt(key, datetime);
        }
    }
}

實戰:redis操作hash雜湊表的增刪改查

本來打算這部分把我demo中的操作程式碼貼出來的,想想,全是程式碼,看著都煩,還不如講解一下這部分的操作對應於redis客戶端操作命令呢。還有一個原因 就是,上面都已經對hash操作進行了分裝,其實如果貼程式碼也就是呼叫封裝的程式碼罷了。感覺沒啥意思,相信大家都會呼叫。沒啥好講的。那麼接下來我就說 下,上面封裝的程式碼與客戶端操作的對應關係。

Exist<T>方法:對應於redis操作的hexists。返回欄位是否是 key 指定的雜湊集中存在的欄位。true:存在 false:不存在

Set<T>方法:對應於redis操作的hget。設定 key 指定的雜湊集中指定欄位的值。如果 key 指定的雜湊集不存在,會建立一個新的雜湊集並與 key 關聯。如果欄位在雜湊集中存在,它將被重寫。

Remove(string hashId, string key)方法:對應於redis操作的hdel。從 key 指定的雜湊集中移除指定的域。在雜湊集中不存在的域將被忽略。如果 key 指定的雜湊集不存在,它將被認為是一個空的雜湊集,該命令將返回false

Remove(string key)方法:對應於redis操作的del。直接刪除key

Get<T>方法:對應於redis操作的hget。返回該欄位所關聯的值。

GetAll<T>方法:對應於redis操作的hvals。獲取雜湊集中的值的列表,當 key 指定的雜湊集不存在時返回空列表。

SetExpire方法:對應於redis操作的expire。設定快取過期。

總結

redis操作很多很多,其實說是封裝,也只是封裝其中的一些常用操作。有興趣的朋友可以用反編譯工具去看下原始碼,就可以知道所有操作對應於redis 操作命令了。其實我個人覺得,使用C#操作redis只是語言需要,我們還是要學習它的客戶端操作的。開發中,很多時候除錯很慢的,我們可以直接通過 redis客戶端操作去找,這樣效率會更高一點。而且當操作命令熟練的時候,你會發現,客戶端操作比除錯操作快很多很多。所以,建議大家多多使用客戶端操 作。剛開始會感覺很多需要記,熟能生巧,多操作,自然會很好。

相關文章