C#中普通快取的使用

陳大寶發表於2021-05-05

快取的概念及優缺點在這裡就不多做介紹,當然快取包含多種有普通快取、客戶端快取、DNS快取、反向代理快取以及分散式快取等等。今天主要聊一聊C#通過編碼來實現普通的快取、話不多說直接上程式碼。

 

一、首先,新建控制檯程式(.NET Core)、以下為專案結構

 

 

  1. CacheHelper快取幫助類
  2. DemoTest 為測試有無快取的Demo程式碼
  3. Program 你們懂得 就不多說了

二、編寫快取類

 

 public class CacheHelper
    {
        //快取容器 
        private static Dictionary<string, object> CacheDictionary = new Dictionary<string, object>();
        /// <summary>
        /// 新增快取
        /// </summary>
        public static void Add(string key, object value)
        {
            CacheDictionary.Add(key, value);
        }

        /// <summary>
        /// 獲取快取
        /// </summary>
        public static T Get <T>(string key)
        {
            return (T)CacheDictionary[key];
        }


        /// <summary>
        /// 快取獲取方法
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key">快取字典容器對應key</param>
        /// <param name="func">委託方法 傳入操作物件</param>
        /// <returns></returns>
        public static T GetCache<T>(string key, Func<T> func)
        {
            T t = default(T);
            if (CacheHelper.Exsits(key))
            {
                //快取存在,直接獲取原資料
                t = CacheHelper.Get<T>(key);
            }
            else
            {
                //快取不存在,去生成快取,並加入容器
                t = func.Invoke();
                CacheHelper.Add(key, t);
            }
            return t;
        }

        /// <summary>
        /// 判斷快取是否存在
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public static bool Exsits(string key)
        {
            return CacheDictionary.ContainsKey(key);
        }
public static T GetCache<T>(string key, Func<T> func)我這裡直接使用泛型委託封裝了快取的方法了。當然你也可以不封裝直接使用if else 判斷
if (CacheHelper.Exsits(key)){} else{}
但是實際開發使用快取應該不是一個地方使用快取、頻繁的使用if else 判斷程式碼有點冗餘、所有我就使用採用泛型委託方式封裝方法 這個大家可以自行選擇

  以上一般採用靜態字典新增快取資料、為了有更好的相容性我這裡直接採用泛型(泛型的好處這裡就不多說了、總之效能相對更好、能夠提高程式碼的重用性)

二、編寫有快取和沒有快取方法

 

    public class DemoTest
    {
        /// <summary>
        /// 使用快取測試
        /// </summary>
        /// <param name="count"></param>
        /// <returns></returns>
        public static List<People> CacheTest(int count)
        {
            People people = new People();


            List<People> ListPeople = new List<People>();
            for (int i = count; i < 100000; i++)
            {
                Console.WriteLine($"------第{i}次請求------");
                //int result = DataSource.GetDataByDB(666);

                //key的名字一定要確保請求的準確性 DataSource GetDataByDB 666缺一不可
                string key = "DataSource_GetDataByDB_666";
                ListPeople = CacheHelper.GetCache(key, () => DemoTest.GetListData());
                Console.WriteLine($"第{i}次請求獲得的資料為:{people}");
           
            }
            return ListPeople;
        }

        /// <summary>
        /// 沒有使用快取
        /// </summary>
        /// <param name="count"></param>
        /// <returns></returns>
        public static List<People> NoCacheTest(int count)
        {
            People people = new People();


            List<People> ListPeople = new List<People>();
            for (int i = count; i < 100000; i++)
            {
                Console.WriteLine($"------第{i}次請求------");
                //int result = DataSource.GetDataByDB(666);

                //key的名字一定要確保請求的準確性 DataSource GetDataByDB 666缺一不可
                string key = "DataSource_GetDataByDB_666";

                //if (CacheHelper.Exsits(key))
                //{
                //    //快取存在,直接獲取原資料
                //    result = CacheHelper.Get<int>(key);
                //}
                //else
                //{
                //    //快取不存在,去生成快取,並加入容器
                //    result = 78;
                //    CacheHelper.Add(key, result);
                //}
                ListPeople = GetListData();
                Console.WriteLine($"第{i}次請求獲得的資料為:{people}");
            }
            return ListPeople;

        }

        /// <summary>
        /// 讀取資料來源 這裡模擬資料來源讀取 迴圈3000
        /// </summary>
        /// <returns></returns>
        public static List<People> GetListData()
        {
            List<People> peoplesList = new List<People>();


            for (int i = 0; i < 3000; i++)
            {
                People people = new People()
                {
                    Age = i,
                    Name = "陳大寶" + i.ToString()
                };

                peoplesList.Add(people);
            }return peoplesList;
        }
    }

 

 

 

 三、控制檯上端呼叫

    class Program
    {
        static void Main(string[] args)
        {

            {
                Console.WriteLine("******************快取測試****************");
                Stopwatch stopwatch = new Stopwatch();
                stopwatch.Start();

                var iResulr = DemoTest.CacheTest(0);

                stopwatch.Stop();

                Console.WriteLine("有快取消耗時間為" + stopwatch.ElapsedMilliseconds);
            }
            {
                Console.WriteLine("*****************沒有快取測試****************");
                Stopwatch stopwatch = new Stopwatch();
                stopwatch.Start();

                var iResulr = DemoTest.NoCacheTest(0);

                stopwatch.Stop();

                Console.WriteLine("沒有快取消耗時間為" + stopwatch.ElapsedMilliseconds);
            }
        }
    }

一、迴圈10萬次有快取消耗時間

   二、迴圈10萬次沒有有快取消耗時間

  

  以上為迴圈10萬次驗證後分析:以上消耗時間為毫秒、有快取 22282毫秒大致為0.37分鐘左右。沒有快取 82417毫秒大致為1.37分鐘左右、兩者相差1分鐘左右、那麼有朋友可能會發現你這也沒快多少啊、但是兄弟們實際我們在訪問時一分鐘已經是相當久了、你能想象呼叫一次介面訪問時間如果超過一分鐘是什麼感覺?那毫無疑問肯定得炸毛、而且我們訪問資料實際第一次肯定是要到關係型資料中獲取資料的、如果使用快取直接第二次直接取記憶體快取(不用去再去運算元據庫IO了)、但是不使用快取每次去運算元據庫、當使用者量稍微多一點資料庫它是有效能開銷越來越大、所以快取是非常有效的、也是我們最能直觀立竿見影的效果。

   

總結一下哈:到這裡,快取的使用基本結束了。最好值得一提的是,快取儘量在資料量小、重複查詢量大的情況下使用。因為快取也是要耗記憶體的,畢竟我們伺服器記憶體是有限的!、當然了會有小夥伴會說我們都已經用Redis等等、確實現在快取當然用的比較多是Redis了、Redis好處這裡就不先談了、總之本次文章是讓沒有了解普通快取是如何使用得的小夥伴熟悉並瞭解、其實在這種方式在有些場景也是有它的好處的。

 

相關文章