ASP.NET Core 中的快取
快取的基本概念
快取是分散式系統中的重要元件,主要解決高併發,大資料場景下,熱點資料訪問的效能問題。提供高效能的資料快速訪問。
快取原理
- 將資料寫入到讀取速度更快的儲存裝置;
- 將資料快取到離應用最近的位置;
- 將資料快取到離使用者最近的位置。
快取設計
- 快取內容 熱點資料,靜態資源
- 快取位置 CDN,反向代理,分散式快取伺服器,本機(記憶體,硬碟)
CDN
:存放HTML、CSS、JS等靜態資源;反向代理
:動靜分離,只快取使用者請求的靜態資源;分散式快取
:快取資料庫中的熱點資料;本地快取
:快取應用字典等常用資料。
- 過期策略 固定時間,相對時間
- 同步機制 實時寫入,非同步重新整理
分散式快取 Memcache 與 Redis 的比較
- 資料結構:Memcache只支援key value儲存方式,Redis支援更多的資料型別,比如Key value、hash、list、set、zset;
- 多執行緒:Memcache支援多執行緒,Redis支援單執行緒;CPU利用方面Memcache優於Redis;
- 持久化:Memcache不支援持久化,Redis支援持久化(快照和AOF日誌兩種持久化方式);
- 記憶體利用率:Memcache高,Redis低(採用壓縮的情況下比Memcache高)。使用簡單的key-value儲存的話,Memcached的記憶體利用率更高,而如果Redis採用hash結構來做key-value儲存,由於其組合式的壓縮,其記憶體利用率會高於Memcache。
- 過期策略:Memcache過期後,不刪除快取,會導致下次取資料資料的問題,Redis有專門執行緒,清除快取資料;
快取穿透,快取擊穿,快取雪崩解決方案
- 快取穿透
快取穿透是指查詢一個一定不存在的資料。由於會頻繁的請求資料庫,對資料庫造成訪問壓力。
解決方法:- 對結果為空的資料也進行快取,不過設定它的過期時間會很短,最長不超過五分鐘。
- 一定不存在的key,採用布隆過濾器,建立一個大的Bitmap中,查詢時通過該bitmap過濾。
- 快取雪崩
快取雪崩是指在我們設定快取時採用了相同的過期時間,導致快取在某一時刻同時失效,請求全部轉發到DB,DB瞬時壓力過重雪崩。
解決方法:- 通過加鎖或者佇列來控制讀資料庫寫快取的執行緒數量。比如對某個key只允許一個執行緒查詢資料和寫快取,其他執行緒等待。
- 分散快取失效時間,比如在設定過期時間的時候增加一個隨機數儘可能的保證快取不會大面積的同時失效。
- 快取擊穿
快取擊穿是指對於一些設定了過期時間的key,如果這些key可能會在過期後的某個時間點被超高併發地訪問。這個和快取雪崩的區別在於這裡針對某一key快取,前者則是很多key。
解決方法:- 使用互斥鎖來解決問題,通俗的描述就是,一萬個使用者訪問了,但是隻有一個使用者可以拿到訪問資料庫的許可權,當這個使用者拿到這個許可權之後重新建立快取,這個時候剩下的訪問者因為沒有拿到許可權,就原地等待著去訪問快取。
資料一致性
資料不一致的幾種情況:
- 資料庫有資料,快取沒有資料;
- 資料庫有資料,快取也有資料,資料不相等;
- 資料庫沒有資料,快取有資料。
目前比較常用的資料快取策略的是Cache Aside Pattern,更新快取是先把資料存到資料庫中,成功後,再讓快取失效。
這種策略下不一致產生的原因只有更新資料庫成功,但是刪除快取失敗。
解決方案:
- 對刪除快取進行重試.
- 定期全量更新快取。
- 合理設定快取過期時間。
使用內建 MemoryCache
ASP.NET Core 支援多種不同的快取。包括記憶體快取,分散式快取(Redis 和 SQL Server)。Github 開源地址 Libraries for in-memory caching and distributed caching.
IMemoryCache是把資料儲存在Web伺服器的記憶體中。
-
在 ConfigureServices 中呼叫 AddMemoryCache 通過依賴關係注入引用服務。
public void ConfigureServices(IServiceCollection services) { services.AddMemoryCache(); services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); }
-
在控制器類中用構造器注入的方式建立 IMemoryCache 的物件。
using Microsoft.Extensions.Caching.Memory; public class ValuesController : ControllerBase { private IMemoryCache _cache; public ValuesController(IMemoryCache cache) { _cache = cache; } }
- 一些常用操作
-
建立快取
Set()
DateTime cacheEntry1 = DateTime.Now; var cacheEntryOptions = new MemoryCacheEntryOptions().SetAbsoluteExpiration(TimeSpan.FromSeconds(3)); _cache.Set("cache1", cacheEntry1, cacheEntryOptions);
GetOrCreate()
GetOrCreateAsync
var cacheEntry = _cache.GetOrCreate("cache1", entry => { entry.SetAbsoluteExpiration(TimeSpan.FromSeconds(3)); return DateTime.Now; });
-
獲取快取
Get()
var cacheEntry = this._cache.Get<DateTime?>("cache1");
TryGetValue()
DateTime cacheEntry; if (!_cache.TryGetValue("cache1", out cacheEntry)) { // Key not in cache, so get data. cacheEntry = DateTime.Now; var cacheEntryOptions = new MemoryCacheEntryOptions() .SetAbsoluteExpiration(TimeSpan.FromSeconds(3)); _cache.Set("cache1", cacheEntry, cacheEntryOptions); }
-
刪除快取
Remove()
_cache.Remove("cache1");
-
-
其他知識點
ICacheEntry成員:Key
快取keyValue
快取值AbsoluteExpiration
絕對過期時間,為null則條件無效AbsoluteExpirationRelativeToNow
相對當前時間的絕對過期時間(使用TimeSpan),為null條件無效SlidingExpiration
滑動過期時間ExpirationTokens
提供用來自定義快取過期PostEvictionCallbacks
快取失效回撥Priority
快取項優先順序(在快取滿載的時候絕對清除的順序)Size
代表快取資料的大小,在記憶體快取中一般為null
- 絕對到期(指定在一個固定的時間點到期)
- 滑動到期(在一個時間長度內沒有被命中則過期,如果命中則順延)
- 到期Token(自定義過期)
使用分散式快取 Redis
- Nuget 安裝 Microsoft.Extensions.Caching.Redis
-
ConfigureServices 方法裡面新增服務 AddDistributedRedisCache
public void ConfigureServices(IServiceCollection services) { services.AddDistributedRedisCache(options => { options.Configuration = "localhost"; options.InstanceName = "Instance1"; }); services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); }
- 常用操作
RedisCache 實現了 IDistributedCache 介面,提供了常用的新增、檢索和刪除操作。Get
,GetAsync
採用字串鍵並以byte[]形式檢索快取項(如果在快取中找到)Set
,SetAsync
使用字串鍵向快取新增項byte[]形式Refresh
,RefreshAsync
根據鍵重新整理快取中的項,並重置其可調過期超時值(如果有)Remove
,RemoveAsync
根據鍵刪除快取項
var now = DateTime.Now; var cacheValue = System.Text.Encoding.UTF8.GetBytes(now.ToString()); var options = new DistributedCacheEntryOptions().SetAbsoluteExpiration(TimeSpan.FromSeconds(3)); _cache.Set("cache1", cacheValue, options); _cache.Refresh("cache1"); var value = _cache.Get("cache1"); var nowString = System.Text.Encoding.UTF8.GetString(value); _cache.Remove("cache1");
由於自帶的 RedisCache 繼承 IDistributedCache 介面並沒有提供 Redis的一些高階特性比如Hash, List, Set等。
使用 Stackexchange.Redis 自己封裝一個 RedisHelper 類
基於Stackexchange.Redis封裝一個簡單RedisHelper類:
RedisHelper 類
public class RedisHelper
{
private readonly RedisOptions _options;
private readonly Lazy<ConnectionMultiplexer> _connectionMultiplexer;
public RedisHelper(IOptions<RedisOptions> optionsAccessor)
{
if (optionsAccessor == null)
{
throw new ArgumentNullException(nameof(optionsAccessor));
}
_options = optionsAccessor.Value;
_connectionMultiplexer = new Lazy<ConnectionMultiplexer>(CreateConnectionMultiplexer);
}
public IDatabase GetDatabase()
{
return _connectionMultiplexer.Value.GetDatabase();
}
private ConnectionMultiplexer CreateConnectionMultiplexer()
{
if (_options.ConfigurationOptions != null)
{
return ConnectionMultiplexer.Connect(_options.ConfigurationOptions);
}
else
{
return ConnectionMultiplexer.Connect(_options.Configuration);
}
}
}
RedisOptions 配置類
public class RedisOptions : IOptions<RedisOptions>
{
/// <summary>
/// The configuration used to connect to Redis.
/// </summary>
public string Configuration { get; set; }
/// <summary>
/// The configuration used to connect to Redis.
/// This is preferred over Configuration.
/// </summary>
public ConfigurationOptions ConfigurationOptions { get; set; }
/// <summary>
/// The Redis instance name.
/// </summary>
public string InstanceName { get; set; }
RedisOptions IOptions<RedisOptions>.Value
{
get { return this; }
}
}
RedisHelperServiceCollectionExtensions 擴充套件類
public static class RedisHelperServiceCollectionExtensions
{
public static IServiceCollection AddRedisHelper(this IServiceCollection services, Action<RedisOptions> setupAction)
{
if (services == null)
{
throw new ArgumentNullException(nameof(services));
}
if (setupAction == null)
{
throw new ArgumentNullException(nameof(setupAction));
}
services.AddOptions();
services.Configure(setupAction);
services.AddSingleton<RedisHelper>();
return services;
}
}
在 ConfigureServices 裡面新增服務引用
public void ConfigureServices(IServiceCollection services)
{
var redisOptions = Configuration.GetSection("RedisOptions").Get<RedisOptions>();
services.AddRedisHelper(options => {
options.Configuration = redisOptions.Configuration;
options.InstanceName = redisOptions.InstanceName;
});
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}
在 Controller 裡面使用
public class ValuesController : ControllerBase
{
private readonly RedisHelper _redisHelper;
public ValuesController(RedisHelper redisHelper)
{
_redisHelper = redisHelper;
}
// GET api/values
[HttpGet]
public ActionResult<IEnumerable<string>> Get()
{
_redisHelper.GetDatabase().StringSet("test_key_2", "test_value_2", TimeSpan.FromSeconds(60));
return new string[] { "value1", "value2" };
}
}
網上一些開源的專案提供了Redis支援:
參考
- 快取技術的詳解
- 快取更新的套路
- Cache in ASP.NET Core
- 擁抱.NET Core系列:MemoryCache 快取過期
- 使用Redis實現分散式快取
- 來源:https://www.cnblogs.com/royzshare/p/9474740.html
相關文章
- ASP.NET Core - 快取之分散式快取ASP.NET快取分散式
- ASP.NET Core - 快取之記憶體快取(下)ASP.NET快取記憶體
- ASP.NET Core - 快取之記憶體快取(上)ASP.NET快取記憶體
- Redis 入門與 ASP.NET Core 快取RedisASP.NET快取
- Asp.Net Core中利用過濾器控制Nginx的快取時間ASP.NET過濾器Nginx快取
- .net 溫故知新【14】:Asp.Net Core WebAPI 快取ASP.NETWebAPI快取
- 用ASP.NET Core 2.1 建立規範的 REST API -- 快取和併發ASP.NETRESTAPI快取
- ASP.NET Core與Redis搭建一個簡易分散式快取ASP.NETRedis分散式快取
- .Net Core快取元件(MemoryCache)【快取篇(二)】快取元件
- 【asp.net core 系列】14 .net core 中的IOCASP.NET
- 聊聊ASP.NET Core中的配置ASP.NET
- ASP.NET Core獲取請求完整的UrlASP.NET
- .NET Core ResponseCache【快取篇(一)】快取
- ASP.NET Core 中的管道機制ASP.NET
- ASP.NET Core 中的依賴注入ASP.NET依賴注入
- ASP.NET Core 學習筆記 第四篇 ASP.NET Core 中的配置ASP.NET筆記
- Glide中的快取IDE快取
- Asp.Net Core webapi+net6 使用資源篩選器(過濾器) 做快取ASP.NETWebAPI過濾器快取
- ASP.NET Core 依賴注入中的ScopeASP.NET依賴注入
- ASP.NET Core中的響應壓縮ASP.NET
- ASP.NET Core中的資料保護ASP.NET
- ASP.NET Core 學習筆記 第五篇 ASP.NET Core 中的選項ASP.NET筆記
- ASP.NET Core - 配置系統之配置讀取ASP.NET
- 在 ASP.NET Core 中禁用HTTPSASP.NETHTTP
- 由ASP.NET Core讀取Response.Body引發的思考ASP.NET
- .Net Core快取元件(MemoryCache)原始碼解析快取元件原始碼
- HTTP 快取中的 VaryHTTP快取
- ASP.NET Core 中的物件對映之 AutoMapperASP.NET物件APP
- 玩轉ASP.NET Core中的日誌元件ASP.NET元件
- ASP.NET Core 中基於策略的授權ASP.NET
- 【譯】ASP.NET Core 6 中的效能改進ASP.NET
- Asp.Net Core 中的HTTP協議詳解ASP.NETHTTP協議
- jwt-在asp.net core中的使用jwtJWTASP.NET
- Asp.net core中RedisMQ的簡單應用ASP.NETRedisMQ
- 修改Ehcache快取中取到的值,快取中的值也被修改了快取
- ASP.NET Core ----ASP.NET Core中使用Code FirstASP.NET
- ASP.NET 6.0 Core 遷移 ASP.NET Core 7.0ASP.NET
- 深入探究ASP.NET Core讀取Request.Body的正確方式ASP.NET