Cache緩存在電腦領域是一個被普遍使用的概念。硬體中CPU有一級緩存,二級緩存, 瀏覽器中有緩存,軟體開發中也有分散式緩存memcache, redis。緩存無處不在的原因是它能夠極大地提高硬體和軟體的運行速度。在項目開發中,性能慢的地方常常是IO操作頻繁的地方,讀取資料庫是我們常見的消耗性能的 ...
Cache緩存在電腦領域是一個被普遍使用的概念。硬體中CPU有一級緩存,二級緩存, 瀏覽器中有緩存,軟體開發中也有分散式緩存memcache, redis。緩存無處不在的原因是它能夠極大地提高硬體和軟體的運行速度。在項目開發中,性能慢的地方常常是IO操作頻繁的地方,讀取資料庫是我們常見的消耗性能的地方。這個時候,如果將使用頻繁的數據緩存到能夠高速讀取的介質中,下次訪問時,不用再去請求資料庫,直接從緩存中獲取所需的數據,就能夠大大提高性能。這篇文章主要討論的是在.Net開發中,如何使用CacheManager框架方便的管理項目中的緩存。
一,CacheManager介紹以及優點
CacheManager是開源的.Net緩存管理框架。它不是具體的緩存實現,而是在緩存之上,方便開發人員配置和管理各種不同的緩存,為上層應用程式提供統一的緩存介面的中間層。
下麵是CacheManager的一些優點:
- 讓開發人員更容易處理和配置緩存,即使是非常複雜的緩存方案。
- CacheManager能夠管理多種緩存,包含記憶體、appfabric、redis、couchbase、 windows azure cache、memorycache等。
- 提供了額外的功能,如緩存同步、併發更新、事件、性能計數器等…
二,CacheManager開始之旅
CacheManager上手還是非常簡單的。下麵使用記憶體緩存結合CacheManager的一個實例,能夠幫助我們快速的熟悉CacheManager如何使用。
首先在Visual Studio中創建一個Console Application.
使用Nuget為項目添加CacheManager包引用。CacheManager包含了很多的Package. 其中CacheManager.Core是必須的,其它的針對不同緩存平臺上有不同的對應Package.
這個Demo中,我們使用記憶體作為緩存,所以只是需要CacheManager.Core和CacheManager.SystemRuntimeCaching
接著在Main函數中配置好我們的緩存:
1 using System;
2 using CacheManager.Core;
3 namespace ConsoleApplication
4 {
5 class Program
6 {
7 static void Main(string[] args)
8 {
9 var cache = CacheFactory.Build("getStartedCache", settings =>
10 {
11 settings.WithSystemRuntimeCacheHandle("handleName");
12 });
13 }
14 }
15 }
上面代碼中使用CacheFactory創建了一個名稱為getStartedCache的緩存實例,這個緩存實例使用的是SystemRunTime Cache, 記憶體緩存。一個緩存實例是可以配置多個Handle的,我們可以使用記憶體來作為存儲介質,也可以使用Redis分散式緩存作為存儲介質,並且可以同時在一個緩存實例中使用,後面我們再介紹多級緩存的配置和使用。
接下來,我們添加一些測試緩存的代碼
1 static void Main(string[] args)
2 {
4 var cache = CacheFactory.Build("getStartedCache", settings =>
5 {
6 settings.WithSystemRuntimeCacheHandle("handleName");
7 });
8
9 cache.Add("keyA", "valueA");
10 cache.Put("keyB", 23);
11 cache.Update("keyB", v => 42);
12 Console.WriteLine("KeyA is " + cache.Get("keyA")); // should be valueA
13 Console.WriteLine("KeyB is " + cache.Get("keyB")); // should be 42
14 cache.Remove("keyA");
15 Console.WriteLine("KeyA removed? " + (cache.Get("keyA") == null).ToString());
16 Console.WriteLine("We are done...");
17 Console.ReadKey();
18 }
三,CacheManager多級緩存配置
實際開發中,我們常常會需要使用多級緩存。
一種常見的情況是,你有一個分散式式緩存伺服器,例如redis,獨立的緩存伺服器能夠讓我們的多個系統應用程式都能夠共用這些緩存的數據,因為這些緩存項的創建是昂貴的。
和訪問資料庫相比,分散式緩存速度較快,但是和記憶體相比,還是不夠快。因為分散式緩存使用還需要序列化和網路傳輸的時間消耗。
這個時候里,做個分級緩存是個好的解決方案,將記憶體緩存結合分散式緩存使用,使用頻率高的數據直接從記憶體中讀取,這將大大提高應用程式的整體性能。
使用記憶體緩存的讀取速度能夠達到分散式緩存的100倍,甚至更高。
使用CacheManager, 配置多級緩存是一件非常容易的事情
1 var cache = CacheFactory.Build<int>("myCache", settings =>
2 {
3 settings
4 .WithSystemRuntimeCacheHandle("inProcessCache")//記憶體緩存Handle
5 .And
6 .WithRedisConfiguration("redis", config =>//Redis緩存配置
7 {
8 config.WithAllowAdmin()
9 .WithDatabase(0)
10 .WithEndpoint("localhost", 6379);
11 })
12 .WithMaxRetries(1000)//嘗試次數
13 .WithRetryTimeout(100)//嘗試超時時間
14 .WithRedisBackPlate("redis")//redis使用Back Plate
15 .WithRedisCacheHandle("redis", true);//redis緩存handle
16 });
上面代碼中,記憶體緩存和Redis緩存配置部分很容易看明白。但是BackPlate是什麼作用? 接下來,我們看看CacheManager中的BackPlate擋板機制。
四, BackPlate解決分散式緩存中的同步問題
對於大型的軟體系統,常常都是分為很多獨立的子項目,各個子項目為了節約成本或者是方便數據共用,常常會共用同一個分佈緩存伺服器。這樣在使用多級緩存的時候,就有可能出現數據不一致的情況。
假設在系統A中的更新了緩存中的一個數據項,這個時候CacheManager會在A設置的所有的緩存handle中更新數據,這裡也包括了分散式緩存上的數據。但是在系統B中的記憶體緩存中,還是會存在著舊的未更新的數據。當系統B從緩存中取這條記錄的時候,就會出現記憶體緩存和分散式緩存中的數據不一致的情況。
為了防止這一點,緩存管理器有一個功能叫做cachebackplate將嘗試同步多個系統中的緩存。
上面設置的多級緩存中,我們就將redis作為BackPlate的源. 也就是說所有的數據都需要以redis中緩存的數據為藍本。
在設置redis作為BackPlate之後,同樣發生上面的數據不一致的情況的時候,只要redis中的數據被修改了,就會觸發CacheManager更新所有系統中的記憶體緩存中的數據,和redis中的數據保持一致。
同步的工作是如何完成的?
每次一條緩存記錄被刪除或更新的時候,Cache Manager會發送一個消息,讓BackPlate存儲這次的數據變化信息。所有其它的系統將非同步接收這些消息,並將相應地作出更新和刪除操作,保證數據的一致性。
五,ExpirationMode和CacheUpdateMode
涉及到緩存,就必然有緩存過期的問題。CacheManager中提供了一些簡單的緩存過期方式設置。
1 public enum ExpirationMode
2 {
3 None = 0,
4 Sliding = 1,
5 Absolute = 2,
6 }
同時CacheManager還為多級緩存之間設置不同的數據更新策略
1 public enum CacheUpdateMode
2 {
3 None = 0,
4 Full = 1,
5 Up = 2,
6 }
使用Sliding和Up, 我們我可以為多級緩存設置不同的緩存過期時間,這樣使用頻率高的數據就能夠保存在訪問速度更快的記憶體中,訪問頻率次高的放到分散式緩存中。當CacheManager在記憶體中找不到緩存數據的時候,就會嘗試在分散式緩存中找。找到後,根據Up設置,會再將該緩存數據保存到記憶體緩存中。
具體的配置方式如下:
1 var cache = CacheFactory.Build<int>("myCache", settings =>
2 {
3 settings.WithUpdateMode(CacheUpdateMode.Up)
4 .WithSystemRuntimeCacheHandle("inProcessCache")//記憶體緩存Handle
5 .WithExpiration(ExpirationMode.Sliding, TimeSpan.FromSeconds(60)))
6 .And
7 .WithRedisConfiguration("redis", config =>//Redis緩存配置
8 {
9 config.WithAllowAdmin()
10 .WithDatabase(0)
11 .WithEndpoint("localhost", 6379);
12 }).
13 .WithExpiration(ExpirationMode.Sliding, TimeSpan. FromHours (24)))
14 .WithMaxRetries(1000)//嘗試次數
15 .WithRetryTimeout(100)//嘗試超時時間
16 .WithRedisBackPlate("redis")//redis使用Back Plate
17 .WithRedisCacheHandle("redis", true);//redis緩存handle
18
19 });
六,緩存使用分析
在緩存使用中,對於緩存hit和miss數據態比較關係,這些數據能夠幫助我們分析和調整緩存的設置,幫助緩存使用地更加合理。
1 var cache = CacheFactory.Build("cacheName", settings => settings
2 .WithSystemRuntimeCacheHandle("handleName")
3 .EnableStatistics()
4 .EnablePerformanceCounters());
在配置好緩存的Statistic功能後,我們就能夠跟蹤到緩存的使用情況了, 下麵就是分別列印各個緩存handle中的分析數據。
1 foreach (var handle in cache.CacheHandles)
2 {
3 var stats = handle.Stats;
4 Console.WriteLine(string.Format(
5 "Items: {0}, Hits: {1}, Miss: {2}, Remove: {3}, ClearRegion: {4}, Clear: {5}, Adds: {6}, Puts: {7}, Gets: {8}",
6 stats.GetStatistic(CacheStatsCounterType.Items),
7 stats.GetStatistic(CacheStatsCounterType.Hits),
8 stats.GetStatistic(CacheStatsCounterType.Misses),
9 stats.GetStatistic(CacheStatsCounterType.RemoveCalls),
10 stats.GetStatistic(CacheStatsCounterType.ClearRegionCalls),
11 stats.GetStatistic(CacheStatsCounterType.ClearCalls),
12 stats.GetStatistic(CacheStatsCounterType.AddCalls),
13 stats.GetStatistic(CacheStatsCounterType.PutCalls),
14 stats.GetStatistic(CacheStatsCounterType.GetCalls)
15 ));
16 }
七,結尾
緩存是個好東西,用好了能夠極大的提高性能。緩存的使用本身是個很大的話題,這邊文章只是從緩存管理這個角度介紹了CachManager的使用。
下麵是CacheManager相關的資料和鏈接:
官方主頁
源代碼
https://github.com/MichaCo/CacheManager
官方MVC項目的Sample
https://github.com/MichaCo/CacheManager/tree/master/samples/CacheManager.Samples.Mvc
最近在思考不同情況下緩存使用的區別問題。對於互聯網項目來說,數據的一致性要求常常不太高,緩存管理中,關註點可能在緩存的命中率上。對於應用系統,訪問請求不大,但是對於數據的一致性要求較高,緩存中的數據更新策略可能更加重要。
怎樣才是好的適合應用系統的緩存設計呢? 如果大家有興趣,歡迎探討指教。
文章轉載自:http://www.cnblogs.com/JustRun1983/p/CacheManager.html