前言
是這么一回事:
我正在苦思一個業務邏輯,捋着我還剩不多的秀發,一時陷入冥想中......
突然聊天圖標一頓猛閃,打開一看,有同事語音;
大概意思是:同事把項目中Redis部分緩存換成MemoryCache/Memcached,還強調MemoryCache/Memcached的效率是Redis的2~5倍;
當時我想到的是Memcached,聽到的似乎也是,心想:怎么可能,就算有性能差,也不至於那么多;
因為當時同事代碼還沒提交,然后就陷入討論ing,最后還是沒聊通,我就跑到同事那當面溝通(要去打架嗎,不不不,文明人);
溝通中.....,好幾分鍾過去了,突然同事說:他用的是微軟的MemoryCache;
雖然從讀音上我還沒區分出來,但一聽微軟,我就感覺我成笑話啦;
然后趕緊讓同事打開代碼,我擦,真成笑話啦,還理直氣壯的溝通了好幾十分鍾。
為什么會那么“理直氣壯”?
- 對Memcached(聽錯的)性能高於Redis的2~5倍產生重大懷疑,經驗告訴我不可能,除非Redis用法有問題;
- 同事把Redis換成Memcached(聽錯的),那肯定不行的,前期的技術選型,Memcached不太符合項目應用場景;
最后因為MemoryCache成就了一場笑話,那MemoryCache和Memcached有什么區別呢?
- 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綜藝圈",跟我一起學~~~