分佈式緩存學習總結— —竹子整理

1、分佈式緩存簡圖linux

2、爲何使用Memcached分佈式緩存呢?web

3、Memcached基礎原理數據庫

4、Memcache下載與安裝windows

5、MencacheHelper.cs 示例使用 結合Session與項目配置緩存設計模式

6、Redis和Memcache的區別總結緩存

 

1、分佈式緩存簡圖服務器

 

 

2、爲何使用Memcached分佈式緩存呢?數據結構

首先先講講爲什麼要緩存,在數據驅動的web開發中,常常要重複從數據庫中取出相同的數據,這種重複極大的增長了數據庫負載。緩存是解決這個問題的好辦法。可是ASP.NET中的雖然已經能夠實現對頁面局部進行緩存,但仍是不夠靈活。Memcached應運而生。架構

 

1、高併發訪問數據庫的痛楚:死鎖!併發

2、磁盤IO之痛,數據庫讀寫說白了就是跟磁盤打交道,磁盤讀取速度是有限制的,通常高點也就7200

3、多客戶端共享緩存

4Net+Memory  >> IO

5、讀寫性能完美 1s 讀取能夠達到1w次 寫:10w

6、超簡單集羣搭建 Clister

7、開源 Open Source

8、沒有提供主從賦值功能,也沒提供容災等功能(容災:即數據備份能使意外發生後恢復數據,Memcached不會進行備份,因爲是緩存在內存中的,一斷電就會失去數據

 

),因此全部的代碼基本都只考慮性能最佳。如要考慮容災,則可以使用Redis分佈式緩存

9、學習成本很是低,入門很是容易

10、豐富的成功的案例。不少大型公司都是用這個來作分佈式緩存

注:Memcached在企業中通常都是在linux下跑,才能達到性能最佳。

 

3、Memcached基礎原理

底層通訊是使用Socket

能夠將緩存服務器理解爲Socket服務端,將WEB服務器理解爲客戶端。

 

4、Memcache下載與安裝

下載,百度一下或者直接在csdn上搜一下windows Memcache穩定版就行,0積分。

1、下載完後,就這麼個exe

 

2、安裝,敲幾行cmd命令就行,截圖以下,左邊是咱們計算機服務列表,能夠看到,已經啓動了咱們的MemcachedMemcache是這個開源項目的名稱,加個dMemcached是具體這個應用程序,也就是這個exe的名稱)

 

三、如今已經啓動服務了,且將其安裝到windows服務上了,這樣一來,就不用每次手動去啓動了,隨電腦而啓動。

 

如今來測試下,隨便新建個控制檯應用程序

  

 1 using Memcached.ClientLibrary;
 2 using System;
 3 using System.Collections.Generic;
 4 using System.Linq;
 5 using System.Text;
 6 using System.Threading.Tasks;
 7 
 8 namespace 測試
 9 {
10     public class MemcacheTset
11     {
12         public static void Test()
13         {
14             string[] serverlist = { "127.0.0.1:11211" }; //服務器列表,可多個 用逗號隔開     
15             
16             //初始化池
17             SockIOPool pool = SockIOPool.GetInstance();
18 
19             //根據實際狀況修改下面參數
20             pool.SetServers(serverlist);
21             pool.InitConnections = 3;
22             pool.MinConnections = 3;
23             pool.MaxConnections = 5;
24             pool.SocketConnectTimeout = 1000;
25             pool.SocketTimeout = 3000;
26             pool.MaintenanceSleep = 30;
27             pool.Failover = true;
28             pool.Nagle = false;
29             pool.Initialize(); // initialize the pool for memcache servers  
30          
31             //得到客戶端實例
32             MemcachedClient mc = new MemcachedClient();//初始化一個客戶端 
33             mc.EnableCompression = false;
34 
35             Console.WriteLine("---------測試----------");
36             mc.Set("test","my value");//存儲數據到緩存服務器,這裏將字符串"my value"緩存,key
37 
38             if (mc.KeyExists("test"))//測試緩存存在key爲test的項目
39             {
40                 Console.WriteLine("test is Exists");
41                 Console.WriteLine(mc.Get("test").ToString());//在緩存中獲取key爲test的項目
42             }
43             else 
44             {
45                 Console.WriteLine("test not Exists");
46             }
47 
48             Console.ReadLine();
49 
50             mc.Delete("test");//移除緩存中key爲test的項目
51 
52             if (mc.KeyExists("test"))
53             {
54                 Console.WriteLine("test is Exists");
55                 Console.WriteLine(mc.Get("test").ToString());
56             }
57             else
58             {
59                 Console.WriteLine("test not Exists");
60             }
61 
62             Console.ReadLine();
63 
64             SockIOPool.GetInstance().Shutdown();//關閉池,關閉sockets
65         }
66     }
67 }
View Code

若是程序運行正常,說明咱們的Memcache服務已啓動且運行正常。

有一點,別忘咯,就是咱們Memcache的驅動,一樣,百度C# Memcache安裝就OK。

好了,如今咱們就實際來應用下吧。

5、MencacheHelper.cs 示例使用 結合項目配置緩存

對於什麼是項目配置,相信你們確定都熟悉的,就是將業務中不常常更改的數據如系統郵件地址,將其存在數據庫中,方便更改。

而後,回頭看這幾個字,「不常常更改」,咱們就得注意了,這樣的數據,咱們就得想到緩存了,緩存就是用來管理這樣的數據。而後呢,咱們就可使用咱們的Memcache來管理它了。

項目中,咱們能夠新建個MemcacheHelper類來封裝下代碼,我寫了個最簡單的存取刪除。

 1 using Memcached.ClientLibrary;
 2 using System;
 3 using System.Collections.Generic;
 4 using System.Linq;
 5 using System.Text;
 6 using System.Threading.Tasks;
 7 
 8 namespace JOEY.BookShop.Common
 9 {
10     public class MemcacheHelper
11     {
12         private static readonly MemcachedClient mc;
13         static MemcacheHelper()
14         {
15             string[] serverlist = { "127.0.0.1:11211" }; //服務器列表,可多個 用逗號隔開     
16 
17             //初始化池
18             SockIOPool pool = SockIOPool.GetInstance();
19 
20             //根據實際狀況修改下面參數
21             pool.SetServers(serverlist);
22             pool.InitConnections = 3;
23             pool.MinConnections = 3;
24             pool.MaxConnections = 5;
25             pool.SocketConnectTimeout = 1000;
26             pool.SocketTimeout = 3000;
27             pool.MaintenanceSleep = 30;
28             pool.Failover = true;
29             pool.Nagle = false;
30             pool.Initialize(); // initialize the pool for memcache servers  
31 
32             //得到客戶端實例
33             mc = new MemcachedClient();//初始化一個客戶端 
34             mc.EnableCompression = false;
35         }
36 
37         /// <summary>
38         ///39         /// </summary>
40         /// <param name="key"></param>
41         /// <param name="value"></param>
42         public static void Set(string key, object value)
43         {
44             mc.Set(key, value);
45         }
46 
47         public static void Set(string key, object value, DateTime time)
48         {
49             mc.Set(key, value, time);
50         }
51 
52         /// <summary>
53         ///54         /// </summary>
55         /// <param name="key"></param>
56         /// <returns></returns>
57         public static object Get(string key)
58         {
59             if (mc.KeyExists(key))
60             {
61                 return mc.Get(key);
62             }
63             else
64             {
65                 return null;
66             }
67             
68         }
69 
70         /// <summary>
71         /// 刪除
72         /// </summary>
73         /// <param name="key"></param>
74         /// <returns></returns>
75         public static bool Delete(string key)
76         {
77             if (mc.KeyExists(key))
78             {
79                 mc.Delete(key);
80                 return true;
81             }
82             return false;
83         }
84     }
85 }
View Code

 

1 var setting = this.DbSession.SettingsDal.LoadEntities(c => c.Name == "系統郵件地址").FirstOrDefault();
2 string value = setting.Value.Trim();
3 MemcacheHelper.Set("setting_" + key, value);

這樣就將咱們的從數據庫中取出的系統郵件地址存儲到了Memcache中,很方便吧。取的話就是:

object obj = MemcacheHelper.Get("setting_" + "系統郵件地址");

這裏因爲存取的數據均爲字符串,不存在序列化的問題,若是存取的對象類型不是字符串,如某個表Model,那麼就得經過序列化來進行操做,對於序列化,本人是使用Json.Net來操做。

再來個輔助類吧。

 1 using Newtonsoft.Json;
 2 using System;
 3 using System.Collections.Generic;
 4 using System.Linq;
 5 using System.Text;
 6 using System.Threading.Tasks;
 7 
 8 namespace JOEY.BookShop.Common
 9 {
10     /// <summary>
11     /// Json.Net 序列化,對於因爲相互引用類型致使的序列化死循環,可在該對象上加個特性標籤[JsonIgnore] 如在Model中有外鍵,兩個模型間相互引用即形成該問題
12     /// </summary>
13     public class SerializeHelper
14     {
15         /// <summary>
16         /// 傳入對象,序列化成字符串返回
17         /// </summary>
18         /// <param name="obj"></param>
19         /// <returns></returns>
20         public static string SerializeToString(object obj)
21         {
22             return JsonConvert.SerializeObject(obj);
23         }
24         /// <summary>
25         /// 傳入序列化字符串,反序列化成對應對象返回
26         /// </summary>
27         /// <typeparam name="T">泛型,對應對象類型</typeparam>
28         /// <param name="serializeStr">序列化後的字符串</param>
29         /// <returns></returns>
30         public static T DeserializeToObject<T>(string serializeStr)
31         {
32             return JsonConvert.DeserializeObject<T>(serializeStr);
33         }
34     }
35 }
View Code

很容易就能看懂,對吧。固然驅動也是須要的。一樣百度哦。

這裏有個小問題,這個程序集,Newtonsoft.Json在個人MVC項目中自己就存在,而我在其餘項目(即項目Common)中用的時候用的網上下的,選的版本是4.5,因爲MVC項目引用了Common,這樣貌似就出現了版本不一致的狀況,貌似是這樣,會提示錯誤。因而我把MVC中的dll給刪除了,從新加載Common下的dll(如今想一想我爲何不把Common下的刪了,去引用MVC下這個呢- -),這樣一來,又出現一個問題,未能加載文件或程序集「Newtonsoft.Json,Version=4.5.0.0。估計是配置項的緣由,因而,百度了下,在web.config runtime節點下添加了這麼幾行,修改後爲:

<dependentAssembly>
        <assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-8.0.0.0" newVersion="8.0.0.0" />
      </dependentAssembly>

8.0.0.0是當前這個dll的版本,估計是版本更新通知吧。具體爲什麼這樣作是有點迷糊的。誰能指點下呢,感激~~~

6、RedisMemcache的區別總結(摘自百度知道)

1. Redis是什麼

這個問題的結果影響了咱們怎麼用Redis。若是你認爲Redis是一個key value store, 那可能會用它來代替MySQL;若是認爲它是一個能夠持久化的cache, 可能只是它保存一些頻繁訪問的臨時數據。RedisREmote DIctionary Server的縮寫,在Redis在官方網站的的副標題是A persistent key-value database with built-in net interface written in ANSI-C for Posix systems,這個定義偏向key value store。還有一些見解則認爲Redis是一個memory database,由於它的高性能都是基於內存操做的基礎。另一些人則認爲Redis是一個data structure server,由於Redis支持複雜的數據特性,好比List, Set等。對Redis的做用的不一樣解讀決定了你對Redis的使用方式。

互聯網數據目前基本使用兩種方式來存儲,關係數據庫或者key value。可是這些互聯網業務自己並不屬於這兩種數據類型,好比用戶在社會化平臺中的關係,它是一個list,若是要用關係數據庫存儲就須要轉換成一種多行記錄的形式,這種形式存在不少冗餘數據,每一行須要存儲一些重複信息。若是用key value存儲則修改和刪除比較麻煩,須要將所有數據讀出再寫入。Redis在內存中設計了各類數據類型,讓業務可以高速原子的訪問這些數據結構,而且不須要關心持久存儲的問題,從架構上解決了前面兩種存儲須要走一些彎路的問題。

2. Redis不可能比Memcache

不少開發者都認爲Redis不可能比Memcached快,Memcached徹底基於內存,而Redis具備持久化保存特性,即便是異步的,Redis也不可能比Memcached快。可是測試結果基本是Redis佔絕對優點。一直在思考這個緣由,目前想到的緣由有這幾方面。

Libevent。和Memcached不一樣,Redis並無選擇libeventLibevent爲了迎合通用性形成代碼龐大(目前Redis代碼還不到libevent1/3)及犧牲了在特定平臺的很多性能。Redislibevent中兩個文件修改實現了本身的epoll event loop(4)。業界很多開發者也建議Redis使用另一個libevent高性能替代libev,可是做者仍是堅持Redis應該小巧並去依賴的思路。一個印象深入的細節是編譯Redis以前並不須要執行./configure

CAS問題。CASMemcached中比較方便的一種防止競爭修改資源的方法。CAS實現須要爲每一個cache key設置一個隱藏的cas tokencas至關value版本號,每次settoken須要遞增,所以帶來CPU和內存的雙重開銷,雖然這些開銷很小,可是到單機10G+ cache以及QPS上萬以後這些開銷就會給雙方相對帶來一些細微性能差異(5)

3. 單臺Redis的存放數據必須比物理內存小

Redis的數據所有放在內存帶來了高速的性能,可是也帶來一些不合理之處。好比一箇中型網站有100萬註冊用戶,若是這些資料要用Redis來存儲,內存的容量必須可以容納這100萬用戶。可是業務實際狀況是100萬用戶只有5萬活躍用戶,1周來訪問過1次的也只有15萬用戶,所以所有100萬用戶的數據都放在內存有不合理之處,RAM須要爲冷數據買單。

這跟操做系統很是類似,操做系統全部應用訪問的數據都在內存,可是若是物理內存容納不下新的數據,操做系統會智能將部分長期沒有訪問的數據交換到磁盤,爲新的應用留出空間。現代操做系統給應用提供的並非物理內存,而是虛擬內存(Virtual Memory)的概念。

基於相同的考慮,Redis 2.0也增長了VM特性。讓Redis數據容量突破了物理內存的限制。並實現了數據冷熱分離。

4. RedisVM實現是重複造輪子

RedisVM依照以前的epoll實現思路依舊是本身實現。可是在前面操做系統的介紹提到OS也能夠自動幫程序實現冷熱數據分離,Redis只須要OS申請一塊大內存,OS會自動將熱數據放入物理內存,冷數據交換到硬盤,另一個知名的「理解了現代操做系統(3)」的Varnish就是這樣實現,也取得了很是成功的效果。

做者antirez在解釋爲何要本身實現VM中提到幾個緣由(6)。主要OSVM換入換出是基於Page概念,好比OS VM1Page4K, 4K中只要還有一個元素即便只有1個字節被訪問,這個頁也不會被SWAP, 換入也一樣道理,讀到一個字節可能會換入4K無用的內存。而Redis本身實現則能夠達到控制換入的粒度。另外訪問操做系統SWAP內存區域時block進程,也是致使Redis要本身實現VM緣由之一。

5. get/set方式使用Redis

做爲一個key value存在,不少開發者天然的使用set/get方式來使用Redis,實際上這並非最優化的使用方法。尤爲在未啓用VM狀況下,Redis所有數據須要放入內存,節約內存尤爲重要。

假如一個key-value單元須要最小佔用512字節,即便只存一個字節也佔了512字節。這時候就有一個設計模式,能夠把key複用,幾個key-value放入一個key中,value再做爲一個set存入,這樣一樣512字節就會存放10-100倍的容量。

這就是爲了節約內存,建議使用hashset而不是set/get的方式來使用Redis,詳細方法見參考文獻(7)

6. 使用aof代替snapshot

Redis有兩種存儲方式,默認是snapshot方式,實現方法是定時將內存的快照(snapshot)持久化到硬盤,這種方法缺點是持久化以後若是出現crash則會丟失一段數據。所以在完美主義者的推進下做者增長了aof方式。aofappend only mode,在寫入內存數據的同時將操做命令保存到日誌文件,在一個併發更改上萬的系統中,命令日誌是一個很是龐大的數據,管理維護成本很是高,恢復重建時間會很是長,這樣致使失去aof高可用性本意。另外更重要的是Redis是一個內存數據結構模型,全部的優點都是創建在對內存複雜數據結構高效的原子操做上,這樣就看出aof是一個很是不協調的部分。

其實aof目的主要是數據可靠性及高可用性,在Redis中有另一種方法來達到目的:Replication。因爲Redis的高性能,複製基本沒有延遲。這樣達到了防止單點故障及實現了高可用。

小結

要想成功使用一種產品,咱們須要深刻了解它的特性。Redis性能突出,若是可以熟練的駕馭,對國內不少大型應用具備很大幫助。

相關文章
相關標籤/搜索