因MemoryCache鬧了個笑話

前言

是這麼一回事:

我正在苦思一個業務邏輯,捋着我還剩不多的秀髮,一時陷入冥想中……

突然聊天圖標一頓猛閃,打開一看,有同事語音;

大概意思是:同事把項目中Redis部分緩存換成MemoryCache/Memcached,還強調MemoryCache/Memcached的效率是Redis的2~5倍;

當時我想到的是Memcached,聽到的似乎也是,心想:怎麼可能,就算有性能差,也不至於那麼多;

因爲當時同事代碼還沒提交,然後就陷入討論ing,最後還是沒聊通,我就跑到同事那當面溝通(要去打架嗎,不不不,文明人);

溝通中…..,好幾分鐘過去了,突然同事說:他用的是微軟的MemoryCache

雖然從讀音上我還沒區分出來,但一聽微軟,我就感覺我成笑話啦;

然後趕緊讓同事打開代碼,我擦,真成笑話啦,還理直氣壯的溝通了好幾十分鐘。

爲什麼會那麼“理直氣壯”?

  • Memcached(聽錯的)性能高於Redis的2~5倍產生重大懷疑,經驗告訴我不可能,除非Redis用法有問題;

  • 同事把Redis換成Memcached(聽錯的),那肯定不行的,前期的技術選型,Memcached不太符合項目應用場景;

最後因爲MemoryCache成就了一場笑話,那MemoryCacheMemcached有什麼區別呢?

  • MemoryCache不是分佈式緩存,是基於程序存儲在內存中的;是微軟封裝好的內存緩存庫,合理利用CPU,性能好;由於基於程序分配內存,使用時避免了網絡通訊的消耗。

  • Memcached是分佈式緩存,是存儲在公共機器上的,供不同程序使用的,存在一定的網絡傳輸消耗。

這樣比較感覺有點勉強,雖然Memcached是分佈式的,但也是基於內存的,在數據存儲內存的邏輯還是不同的,不過這裏不打算講解源碼,我要說應用,哈哈哈。

附加-爲什麼Redis讓同事感覺性能不好

真實場景是這樣的,客戶端開啓多線程頻繁讀取Redis數據,當訪問比較多時,導致Redis讀取數據超過20毫秒,對於Web項目來說其實這還好,20毫秒的響應用戶根本無法感知。但對於一個高性能要求的服務程序來說,對通訊要求就比較高,所以簡單分析了一下拖慢的原因,大概以下兩點:

  • 客戶端增多,導致一個常用Key對應的數據變大(其實不大,只是相對大,稍微影響了讀取速度,毫秒級別);

    解決方案:同事使用MemoryCache多做一層緩存,將這個常用Key直接存在內存中,提高讀取性能;

  • 使用類似於Keys * 的命令頻繁獲取數據,導致有些命令執行在20毫秒左右(慢日誌中可以看到);

    解決方案:改用Scan類似命令獲取數據;

  • Redis自身的持久化耗時;

    解決方案:適當調整Redis持久化策略,讓持久化頻率沒那麼高;

正文

迴歸正題,既然說到MemoryCache,就來簡單聊聊,主要分享在項目實戰中如何使用;

主要依賴包:Microsoft.Extensions.Caching.Memory

MemoryCache的使用很簡單,就是在調用方法設置和獲取值就對啦;來直接看Demo吧;

1. 控制檯Demo

其實有很多程序是基於後臺服務運行的,並不都是Web,所以寫了一個控制檯的Demo,方便小夥伴參考;

1.1 引入相關包,項目中使用了Autofac作爲依賴注入和其切面編程,則需要引入相關的依賴包,項目結構和包引入如下圖:

1.2 編寫示例代碼及註冊相關服務
  • IUserService就是簡單接口;


    接口和方法上標註的Intercept和MyCache特性不是必須的,接下來會說;

  • UserService對接口的實現;

  • MyCacheAttribute自定義特性,用於標識,裏面沒有任何邏輯處理;



  • MyInterceptor自定義攔截器,面向切面的邏輯代碼在這裏處理;



    代碼完了,就開始使用Autofac註冊服務,進行使用啦,如下:



注:Autofac不是必須的,根據自己需要進行選擇使用,這裏是爲了要使用Autofac的切面編程功能。

1.3 兩種方式進行緩存處理

通常在非Web程序中,有以下兩種方式進行緩存處理:

代碼嵌入到業務邏輯,在真實業務邏輯處進行緩存獲取或設置;


這樣很大一個缺點是每一個緩存的數據都需要手動到指定業務邏輯中添加緩存處理,代碼後期不好維護,緩存功能的開啓和關閉也不好控制,需要修改代碼進行滿足。

面向切面編程,無需嵌入多餘代碼到業務中

通過面向切面的思想,以動態代理的原理攔截方法,在方法前後進行處理,如下:


緩存邏輯直接放在攔截器中處理即可,如下:


註冊服務時,開啓Autofac的面向切面功能即可


運行看效果,第二次都是從緩存中獲取數據,美美噠:

注:推薦使用面向切面的形式進行處理,這樣緩存功能可插拔,代碼維護性也好。

2.WebApiDemo(項目名稱爲:MemoryCacheWebApiDemo)

在WebApi中使用就比較簡單啦,關於MemoryCache的依賴包已經集成在框架中,如果需要使用,直接註冊服務就可以用啦;通常在WebApi中進行緩存處理的方式有三種:

  • 中間件形式:通過自定義中間件進行緩存邏輯操作;

  • 過濾器形式:使用MVC相關過濾器進行緩存邏輯操作;

  • 業務層面向切面形式:面向切面的方式,在業務層做緩存,集成Autofac即可,和控制檯Demo的面向切面一樣;

這裏就用過濾器的形式進行Demo演示,走起來~~~

2.1 編寫過濾器

緩存如果能在最前面處理,就優先在最前面,所以使用的ResourceFilter過濾器;關於過濾器之前寫過一篇文章很詳細(跟我一起學.NetCore之MVC過濾器,這篇看完走路可以仰着頭走),這裏就不贅述了。

然後在Startup.cs中註冊服務和將自定義過濾器註冊即可,如下:

然後啓動運行,多次請求調試看看效果(小夥伴自己在過濾器中調試即可);

在WebApi中也可以使用業務層面向切面的方式進行相關業務處理,集成Autofac即可;小夥伴可以參照這篇文章(跟我一起學.NetCore之Asp.NetCore中集成Autofac擴展)。關於AOP面向切面編程這塊,後續單獨整理一篇分享;

源碼地址:https://github.com/zyq025/DotNetCoreStudyDemo

總結

在一些開發項目中,可能會使用Dictionary進行數據緩存,如果是這樣,可以嘗試使用MemoryCache,性能合理利用CPU,而且還是線程安全的;另外在高併發場景,可以用其作爲多級緩存,因爲MemoryCache還能設置過期時間,搭配Redis配合使用,效果槓槓的。

一個被程序搞醜的帥小夥,關注"Code綜藝圈",跟我一起學~~~


本文分享自微信公衆號 - 一線碼農聊技術(dotnetfly)。
如有侵權,請聯繫 [email protected] 刪除。
本文參與“OSC源創計劃”,歡迎正在閱讀的你也加入,一起分享。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章