国产精品电影_久久视频免费_欧美日韩国产激情_成年人视频免费在线播放_日本久久亚洲电影_久久都是精品_66av99_九色精品美女在线_蜜臀a∨国产成人精品_冲田杏梨av在线_欧美精品在线一区二区三区_麻豆mv在线看

C# .NET 中的緩存實(shí)現(xiàn)

開發(fā) 后端
緩存非常適用于不經(jīng)常更改的數(shù)據(jù)。或者甚至更好,永遠(yuǎn)不會(huì)改變。不斷變化的數(shù)據(jù),比如當(dāng)前機(jī)器的時(shí)間不應(yīng)該被緩存,否則你會(huì)得到錯(cuò)誤的結(jié)果。

[[423388]]

軟件開發(fā)中最常用的模式之一是緩存。這是一個(gè)簡(jiǎn)單但非常有效的概念,這個(gè)想法的核心是記錄過程數(shù)據(jù),重用操作結(jié)果。當(dāng)執(zhí)行繁重的操作時(shí),我們會(huì)將結(jié)果保存在我們的緩存容器中。下次我們需要該結(jié)果時(shí),我們將從緩存容器中拉出它,而不是再次執(zhí)行繁重的操作。

例如,要獲取一個(gè)人的頭像,您可能需要訪問數(shù)據(jù)庫。我們不會(huì)每次都執(zhí)行那次旅行,而是將 Avatar 保存在緩存中,每次需要時(shí)從內(nèi)存中提取它。

緩存非常適用于不經(jīng)常更改的數(shù)據(jù)。或者甚至更好,永遠(yuǎn)不會(huì)改變。不斷變化的數(shù)據(jù),比如當(dāng)前機(jī)器的時(shí)間不應(yīng)該被緩存,否則你會(huì)得到錯(cuò)誤的結(jié)果。

進(jìn)程內(nèi)緩存、持久性進(jìn)程內(nèi)緩存和分布式緩存

有 3 種類型的緩存:

  • In-Memory Cache用于在單個(gè)進(jìn)程中實(shí)現(xiàn)緩存。當(dāng)進(jìn)程終止時(shí),緩存也隨之終止。如果您在多臺(tái)服務(wù)器上運(yùn)行相同的進(jìn)程,您將為每臺(tái)服務(wù)器提供一個(gè)單獨(dú)的緩存。
  • 持久性進(jìn)程內(nèi)緩存是指在進(jìn)程內(nèi)存之外備份緩存。它可能在文件中,也可能在數(shù)據(jù)庫中。這比較困難,但如果您的進(jìn)程重新啟動(dòng),緩存不會(huì)丟失。最適合在獲取緩存項(xiàng)的情況下使用范圍廣泛,并且您的進(jìn)程往往會(huì)重新啟動(dòng)很多。
  • 分布式緩存是指您希望為多臺(tái)機(jī)器共享緩存。通常,它將是多個(gè)服務(wù)器。使用分布式緩存,它存儲(chǔ)在外部服務(wù)中。這意味著如果一臺(tái)服務(wù)器保存了一個(gè)緩存項(xiàng),其他服務(wù)器也可以使用它。像Redis[1]這樣的服務(wù)非常適合這一點(diǎn)。

我們將只討論進(jìn)程內(nèi)緩存。

早期做法

讓我們用 C# 創(chuàng)建一個(gè)非常簡(jiǎn)單的緩存實(shí)現(xiàn):

  1. public class NaiveCache<TItem> 
  2.     Dictionary<object, TItem> _cache = new Dictionary<object, TItem>(); 
  3.  
  4.     public TItem GetOrCreate(object key, Func<TItem> createItem) 
  5.     { 
  6.         if (!_cache.ContainsKey(key)) 
  7.         { 
  8.             _cache[key] = createItem(); 
  9.         } 
  10.         return _cache[key]; 
  11.     } 

用法:

  1. var _avatarCache = new NaiveCache<byte[]>(); 
  2. // ... 
  3. var myAvatar = _avatarCache.GetOrCreate(userId, () => _database.GetAvatar(userId)); 

這個(gè)簡(jiǎn)單的代碼解決了一個(gè)關(guān)鍵問題。要獲取用戶的頭像,只有第一個(gè)請(qǐng)求才會(huì)真正執(zhí)行到數(shù)據(jù)庫的訪問。然后將頭像數(shù)據(jù) ( byte[]) 保存在進(jìn)程內(nèi)存中。對(duì)頭像的所有后續(xù)請(qǐng)求都將從內(nèi)存中提取,從而節(jié)省時(shí)間和資源。

但是,正如編程中的大多數(shù)事情一樣,沒有什么是那么簡(jiǎn)單的。由于多種原因,上述解決方案并不好。一方面,這個(gè)實(shí)現(xiàn)不是線程安全的。從多個(gè)線程使用時(shí)可能會(huì)發(fā)生異常。除此之外,緩存的項(xiàng)目將永遠(yuǎn)留在內(nèi)存中,這實(shí)際上非常糟糕。

這就是我們應(yīng)該從緩存中刪除項(xiàng)目的原因:

1.緩存會(huì)占用大量內(nèi)存,最終導(dǎo)致內(nèi)存不足異常和崩潰。

2.高內(nèi)存消耗會(huì)導(dǎo)致GC 壓力(又名內(nèi)存壓力)。在這種狀態(tài)下,垃圾收集器的工作量超出其應(yīng)有的水平,從而損害了性能。

3.如果數(shù)據(jù)發(fā)生變化,可能需要刷新緩存。我們的緩存基礎(chǔ)設(shè)施應(yīng)該支持這種能力。

為了處理這些問題,緩存框架具有驅(qū)逐策略(又名移除策略)。這些是根據(jù)某些邏輯從緩存中刪除項(xiàng)目的規(guī)則。常見的驅(qū)逐政策有:

  • 無論如何,絕對(duì)過期策略將在固定時(shí)間后從緩存中刪除項(xiàng)目。
  • 如果在固定的時(shí)間段內(nèi)未訪問某個(gè)項(xiàng)目,則滑動(dòng)過期策略將從緩存中刪除該項(xiàng)目。因此,如果我將過期時(shí)間設(shè)置為 1 分鐘,只要我每 30 秒使用一次,該項(xiàng)目就會(huì)一直保留在緩存中。一旦我超過一分鐘不使用它,該物品就會(huì)被驅(qū)逐。
  • 大小限制策略將限制緩存內(nèi)存大小。

現(xiàn)在我們知道我們需要什么,讓我們繼續(xù)尋找更好的解決方案。

更好的解決方案

作為一名博主,令我非常沮喪的是,微軟已經(jīng)創(chuàng)建了一個(gè)很棒的緩存實(shí)現(xiàn)。這剝奪了我自己創(chuàng)建類似實(shí)現(xiàn)的樂趣,但至少我寫這篇博文的工作量減少了。

我將向您展示微軟的解決方案,如何有效地使用它,然后在某些場(chǎng)景中如何改進(jìn)它。

System.Runtime.Caching/MemoryCache 與 Microsoft.Extensions.Caching.Memory

Microsoft 有 2 個(gè)解決方案 2 個(gè)不同的 NuGet 包用于緩存。兩者都很棒。根據(jù) Microsoft 的建議[2],更喜歡使用,Microsoft.Extensions.Caching.Memory因?yàn)樗c Asp.NET Core 集成得更好。它可以很容易地注入[3]到 Asp .NET Core 的依賴注入機(jī)制中。

這是一個(gè)基本示例Microsoft.Extensions.Caching.Memory:

  1. public class SimpleMemoryCache<TItem> 
  2.     private MemoryCache _cache = new MemoryCache(new MemoryCacheOptions()); 
  3.  
  4.     public TItem GetOrCreate(object key, Func<TItem> createItem) 
  5.     { 
  6.         TItem cacheEntry; 
  7.         if (!_cache.TryGetValue(keyout cacheEntry))// Look for cache key
  8.         { 
  9.             // Key not in cache, so get data. 
  10.             cacheEntry = createItem(); 
  11.  
  12.             // Save data in cache. 
  13.             _cache.Set(key, cacheEntry); 
  14.         } 
  15.         return cacheEntry; 
  16.     } 

用法:

  1. var _avatarCache = new SimpleMemoryCache<byte[]>(); 
  2. // ... 
  3. var myAvatar = _avatarCache.GetOrCreate(userId, () => _database.GetAvatar(userId)); 

這和我自己的非常相似NaiveCache,所以有什么改變?嗯,一方面,這是一個(gè)線程安全的實(shí)現(xiàn)。您可以一次從多個(gè)線程安全地調(diào)用它。

第二件事是MemoryCache允許我們之前談到的所有驅(qū)逐政策。下面是一個(gè)例子:

具有驅(qū)逐策略的 IMemoryCache:

  1. public class MemoryCacheWithPolicy<TItem> 
  2.     private MemoryCache _cache = new MemoryCache(new MemoryCacheOptions() 
  3.     { 
  4.         SizeLimit = 1024 
  5.     }); 
  6.  
  7.     public TItem GetOrCreate(object key, Func<TItem> createItem) 
  8.     { 
  9.         TItem cacheEntry; 
  10.         if (!_cache.TryGetValue(keyout cacheEntry))// Look for cache key
  11.         { 
  12.             // Key not in cache, so get data. 
  13.             cacheEntry = createItem(); 
  14.  
  15.             var cacheEntryOptions = new MemoryCacheEntryOptions() 
  16.              .SetSize(1)//Size amount 
  17.              //Priority on removing when reaching size limit (memory pressure) 
  18.                 .SetPriority(CacheItemPriority.High) 
  19.                 // Keep in cache for this time, reset time if accessed. 
  20.                 .SetSlidingExpiration(TimeSpan.FromSeconds(2)) 
  21.                 // Remove from cache after this time, regardless of sliding expiration 
  22.                 .SetAbsoluteExpiration(TimeSpan.FromSeconds(10)); 
  23.  
  24.             // Save data in cache. 
  25.             _cache.Set(key, cacheEntry, cacheEntryOptions); 
  26.         } 
  27.         return cacheEntry; 
  28.     } 

1.SizeLimit被添加到MemoryCacheOptions. 這為我們的緩存容器添加了基于大小的策略。大小沒有單位。相反,我們需要在每個(gè)緩存條目上設(shè)置大小數(shù)量。在這種情況下,我們每次將金額設(shè)置為 1 SetSize(1)。這意味著緩存限制為 1024 個(gè)項(xiàng)目。

2.當(dāng)我們達(dá)到大小限制時(shí),應(yīng)該刪除哪個(gè)緩存項(xiàng)?您實(shí)際上可以使用.SetPriority(CacheItemPriority.High). 級(jí)別為Low、Normal、High和NeverRemove。

3.SetSlidingExpiration(TimeSpan.FromSeconds(2))添加了,它將滑動(dòng)過期時(shí)間設(shè)置為 2 秒。這意味著如果一個(gè)項(xiàng)目在 2 秒內(nèi)未被訪問,它將被刪除。

4.SetAbsoluteExpiration(TimeSpan.FromSeconds(10))添加了,將絕對(duì)過期時(shí)間設(shè)置為 10 秒。這意味著該項(xiàng)目將在 10 秒內(nèi)被驅(qū)逐,如果它還沒有。

除了示例中的選項(xiàng)之外,您還可以設(shè)置一個(gè)RegisterPostEvictionCallback委托,該委托將在項(xiàng)目被驅(qū)逐時(shí)調(diào)用。

這是一個(gè)非常全面的功能集。它讓你想知道是否還有什么要添加的。實(shí)際上有幾件事。

問題和缺失的功能

在這個(gè)實(shí)現(xiàn)中有幾個(gè)重要的缺失部分。

1.雖然您可以設(shè)置大小限制,但緩存實(shí)際上并不監(jiān)控 gc 壓力。如果真的監(jiān)測(cè),壓力大的時(shí)候可以收緊政策,壓力小的時(shí)候可以放松政策。

2.當(dāng)多個(gè)線程同時(shí)請(qǐng)求同一個(gè)項(xiàng)目時(shí),請(qǐng)求不會(huì)等待第一個(gè)完成。該項(xiàng)目將被創(chuàng)建多次。例如,假設(shè)我們正在緩存頭像,從數(shù)據(jù)庫中獲取頭像需要 10 秒。如果我們?cè)诘谝淮握?qǐng)求后 2 秒請(qǐng)求頭像,它將檢查頭像是否已緩存(尚未緩存),并開始另一次訪問數(shù)據(jù)庫。

關(guān)于GC壓力的第一個(gè)問題:可以使用多種技術(shù)和啟發(fā)式方法來監(jiān)控GC壓力。這篇博文與此無關(guān),但您可以閱讀我的文章在 C# .NET 中查找、修復(fù)和避免內(nèi)存泄漏:8 個(gè)最佳實(shí)踐[4]以了解一些有用的方法。

第二個(gè)問題更容易解決。事實(shí)上,這是一個(gè)MemoryCache完全解決它的實(shí)現(xiàn):

  1. public class WaitToFinishMemoryCache<TItem> 
  2.     private MemoryCache _cache = new MemoryCache(new MemoryCacheOptions()); 
  3.     private ConcurrentDictionary<object, SemaphoreSlim> _locks = new ConcurrentDictionary<object, SemaphoreSlim>(); 
  4.  
  5.     public async Task<TItem> GetOrCreate(object key, Func<Task<TItem>> createItem) 
  6.     { 
  7.         TItem cacheEntry; 
  8.  
  9.         if (!_cache.TryGetValue(keyout cacheEntry))// Look for cache key
  10.         { 
  11.             SemaphoreSlim mylock = _locks.GetOrAdd(key, k => new SemaphoreSlim(1, 1)); 
  12.  
  13.             await mylock.WaitAsync(); 
  14.             try 
  15.             { 
  16.                 if (!_cache.TryGetValue(keyout cacheEntry)) 
  17.                 { 
  18.                     // Key not in cache, so get data. 
  19.                     cacheEntry = await createItem(); 
  20.                     _cache.Set(key, cacheEntry); 
  21.                 } 
  22.             } 
  23.             finally 
  24.             { 
  25.                 mylock.Release(); 
  26.             } 
  27.         } 
  28.         return cacheEntry; 
  29.     } 

用法:

  1. var _avatarCache = new WaitToFinishMemoryCache<byte[]>(); 
  2. // ... 
  3. var myAvatar =  
  4.  await _avatarCache.GetOrCreate(userId, async () => await _database.GetAvatar(userId)); 

代碼說明

此實(shí)現(xiàn)鎖定項(xiàng)目的創(chuàng)建。鎖是特定于鑰匙的。例如,如果我們正在等待獲取 Alex 的 Avatar,我們?nèi)匀豢梢栽诹硪粋€(gè)線程上獲取 John 或 Sarah 的緩存值。

字典_locks存儲(chǔ)了所有的鎖。常規(guī)鎖不適用于async/await,因此我們需要使用SemaphoreSlim[5].

如果 (!_cache.TryGetValue(key, out cacheEntry)),有 2 次檢查以查看該值是否已被緩存。鎖內(nèi)的那個(gè)是確保只有一個(gè)創(chuàng)建的那個(gè)。鎖外面的那個(gè)是為了優(yōu)化。

何時(shí)使用 WaitToFinishMemoryCache

這個(gè)實(shí)現(xiàn)顯然有一些開銷。讓我們考慮什么時(shí)候甚至有必要。

在以下情況下使用 WaitToFinishMemoryCache:

  • 當(dāng)項(xiàng)目的創(chuàng)建時(shí)間具有某種成本時(shí),您希望盡可能減少創(chuàng)建。
  • 當(dāng)一個(gè)項(xiàng)目的創(chuàng)建時(shí)間很長時(shí)。
  • 當(dāng)必須確保每個(gè)鍵都創(chuàng)建一個(gè)項(xiàng)目時(shí)。

在以下情況下不要使用 WaitToFinishMemoryCache:

  • 沒有多個(gè)線程訪問同一個(gè)緩存項(xiàng)的危險(xiǎn)。

?您不介意多次創(chuàng)建該項(xiàng)目。例如,如果對(duì)數(shù)據(jù)庫的額外訪問不會(huì)有太大變化。

概括

緩存是一種非常強(qiáng)大的模式,它也很危險(xiǎn),并且有其自身的復(fù)雜性。緩存太多,可能會(huì)導(dǎo)致 GC 壓力,緩存太少會(huì)導(dǎo)致性能問題。而分布式緩存,這是一個(gè)需要探索的全新世界。軟件開發(fā)職業(yè)就這樣,總是有新的東西要學(xué)習(xí)。

References

[1] Redis: https://redis.io/

[2] 建議: https://docs.microsoft.com/en-us/aspnet/core/performance/caching/memory?view=aspnetcore-2.2#systemruntimecachingmemorycache

[3] 容易地注入: https://docs.microsoft.com/en-us/aspnet/core/performance/caching/memory?view=aspnetcore-2.2#using-imemorycache

[4] 在 C# .NET 中查找、修復(fù)和避免內(nèi)存泄漏:8 個(gè)最佳實(shí)踐: https://michaelscodingspot.com/find-fix-and-avoid-memory-leaks-in-c-net-8-best-practices/

 

[5] SemaphoreSlim: https://blog.cdemi.io/async-waiting-inside-c-sharp-locks/

 

責(zé)任編輯:武曉燕 來源: DotNET技術(shù)圈
相關(guān)推薦

2019-07-02 15:21:39

緩存NET單線程

2024-10-16 11:28:42

2011-06-08 13:50:39

C#類型轉(zhuǎn)換

2009-08-12 14:10:37

asp.net分頁代碼

2014-03-11 11:35:00

.NETC#

2009-09-04 15:45:29

C#緩存流

2015-07-28 10:06:03

C#內(nèi)部實(shí)現(xiàn)剖析

2009-09-01 16:29:03

QuickSort C

2024-06-12 07:48:24

C#WebService.NET

2009-08-26 15:25:06

.NET Framew

2009-08-26 15:10:34

脫離.net fram

2009-08-06 16:36:20

研究C#和.Net

2023-09-08 09:12:57

內(nèi)存緩存圖像

2014-04-17 10:37:43

C++.NET Native

2009-08-20 10:13:49

ASP.NET和C#的

2024-07-22 14:34:20

簡(jiǎn)單工廠模式C#

2024-12-20 09:48:47

C#Python代碼

2009-07-15 18:25:52

ASP.NET控件數(shù)組

2009-07-27 14:13:56

調(diào)用c#方法Java scriptASP.NET

2009-08-12 17:19:51

ASP.NET圖片加水
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

伊人久久综合一区二区| 亚洲一区二区日韩| 色美美综合视频| 免费全黄无遮挡裸体毛片| 国产很黄免费观看久久| 韩国一区二区三区美女美女秀| 国产精品1luya在线播放| 亚洲欧洲一区二区三区在线观看| 欧美24videosex性欧美| 日韩一级片在线播放| 欧美私人网站| 91精品国产色综合久久不卡电影 | 激情六月天婷婷| 免费成人在线观看| 午夜一区二区三视频在线观看| 在线不卡视频| 免费看国产精品一二区视频| 亚洲一区二区免费看| 欧美激情第六页| 久久婷婷一区| 四虎影院一区二区三区| 首页欧美精品中文字幕| 亚洲欧洲精品在线观看| 精品一区二区三区在线播放视频| 福利网在线观看| av成人免费在线| 国产精品人人爽人人爽| 成人免费在线视频观看| 在线观看老湿视频福利| 亚洲 欧美综合在线网络| 亚洲成人福利| 精品视频一区二区三区免费| 亚洲国产精品精华素| 亚洲精品天天看| 懂色av色香蕉一区二区蜜桃| 国产91精品久| 欧美精品国产| 中文字幕乱码一区二区三区| 久久青草欧美一区二区三区| 深夜宅男网站免费进入| 制服丝袜国产精品| 涩涩视频在线| 欧美极品少妇全裸体| 97精品国产一区二区三区 | 女仆av观看一区| 国产精品女人网站| 亚洲免费播放| 日本xxxxx18| 国产午夜亚洲精品不卡| 欧美成人影院在线播放| 欧美日韩免费视频| 99九九久久| 欧美洲成人男女午夜视频| 欧美成人精品| 69精品丰满人妻无码视频a片| 国产校园另类小说区| 日本福利片在线| 亚洲精品日韩在线| 奇米色欧美一区二区三区| 欧美日韩国产精品一卡| 久久五月婷婷丁香社区| 久久精品色图| 国产一区二区三区四区福利| 欧美日韩播放| 亚洲一区二区三区在线观看视频| 欧美激情中文不卡| 激情视频在线观看| 久久久久免费精品国产| 国产精品久久久久久模特| 国产在线观看福利| 欧美熟乱第一页| 国产一区2区在线观看| 91亚洲精品一区| 91蝌蚪porny| 国产传媒在线播放| 51ⅴ精品国产91久久久久久| 日韩高清电影一区| eeuss在线观看| 色婷婷综合成人av| 日韩午夜在线电影| 大胆高清日本a视频| 亚洲免费中文字幕| 欧美久久视频| 妞干网免费视频| 亚洲精品一区在线观看香蕉| 国产韩日影视精品| 冲田杏梨av在线| 亚洲国产精品久久久久久| 成人写真视频| 50路60路老熟妇啪啪| 日韩精品自拍偷拍| 久久免费大视频| 冲田杏梨av在线| 国产午夜精品视频免费不卡69堂| 国产精品sm| 最近中文字幕mv2018在线高清| 国产一区二区三区在线看| 99国产精品私拍| 色网在线视频| 久久99国产精品自在自在app| 美腿丝袜亚洲综合| www 日韩| 国产精品流白浆视频| www精品美女久久久tv| 色黄视频在线观看| 久久国产精品精品国产色婷婷| 亚洲一区二区精品视频| 国产欧美啪啪| 国产男女激情视频| 伊人伊成久久人综合网站| 日韩中文字幕91| 日p在线观看| 92看片淫黄大片看国产片| 国产精品乱码久久久久久| 欧美www.| 亚洲精品久久久久久一区二区| 欧美专区在线观看一区| 久久中文亚洲字幕| 石原莉奈一区二区三区高清在线| 欧美乱人伦中文字幕在线| 丰满放荡岳乱妇91ww| 岛国av在线播放| 一区二区精品在线| 亚洲国产精品va在线| 快she精品国产999| 久久精品视频观看| 精品日韩电影| 欧美一级电影网站| 日韩中文字幕亚洲一区二区va在线 | 日韩精品一区二区三区中文在线| 成人性做爰片免费视频| 欧美一区二视频| 国产免费成人| 二区三区四区高清视频在线观看| 亚洲综合在线小说| 91成人在线免费观看| 欧美亚韩一区| 国产九色在线| 91人成网站www| 在线看国产一区二区| 婷婷亚洲五月| 亚洲欧美视频一区二区| 春色成人在线视频| 欧美一区二区三区小说| 性欧美videos另类喷潮| 丁香花在线观看完整版电影| 一本色道久久综合亚洲精品婷婷| 日韩精品中午字幕| 国产一区二区三区蝌蚪| 韩国三级一区| 日韩欧美精品在线观看视频| 91精品国产精品| 亚洲成人精品在线观看| 欧美日韩国产在线一区| 午夜在线小视频| 中文字幕一区二区三区四区五区| 国产亚洲欧美另类中文| 久久精品在线免费观看| 精品久久久久久久久久久下田| 久久久久久青草| 一本一道久久a久久综合精品 | 2018日韩中文字幕| 亚洲理论在线观看| 黄色免费成人| 成人免费网站观看| 成年网站在线免费观看| 91成品人片a无限观看| 日本精品一区二区三区高清 | 96pao国产成视频永久免费| 欧美日韩第一区日日骚| 精品一区二区三区在线播放视频| 麻豆国产一区| 深夜影院在线观看| 伊人天天久久大香线蕉av色| xvideos亚洲| 午夜a成v人精品| 久久99精品视频| 西野翔中文久久精品国产| 麻豆影视在线观看_| jizzjizzxxxx| 成人高h视频在线| 亚洲国产精品久久精品怡红院| 国产日本欧洲亚洲| 亚洲国内精品| 国产精品视频一区二区三区| 午夜影院在线观看视频| 国产在线拍揄自揄拍无码| 性欧美在线看片a免费观看| 欧美日韩三级在线| 99精品视频一区二区三区| 狠狠做六月爱婷婷综合aⅴ| 成人影音在线| 激情亚洲一区二区三区四区| 日韩大片在线观看视频| 日韩激情电影免费看| 亚洲精品理论电影| 亚洲色图欧美在线| 久热精品在线| 粉嫩的18在线观看极品精品| 国产福利视频在线观看|