分布式缓存 #
什么是缓存 #
缓存可以减少生成内容所需的工作,从而显著提高应用程序的性能和可伸缩性。缓存最适用于不经常更改的数据,因为生成成本很高。 通过缓存,可以比从数据源返回的数据的副本速度快得多。应该对应用进行编写和测试,使其不要永远依赖于缓存的数据。
缓存类型 #
内存缓存:顾名思义,就是缓存在应用部署所在服务器的内存中分布式缓存:分布式缓存是由多个应用服务器共享的缓存响应缓存:缓存服务器端Not Modified 的数据
内存缓存使用 #
内存缓存是最常用的缓存方式,具有存取快,效率高特点。内存缓存通过注入IMemoryCache 方式注入即可。
备注
在Mall3s 框架中,内存缓存服务已经默认注册,无需手动注册。
基本使用 #
如,缓存当前时间:
using Mall3s.DynamicApiController;
using Microsoft.Extensions.Caching.Memory;
using System;
namespace Mall3s.Application
{
public class CacheServices : IDynamicApiController
{
private const string _timeCacheKey = "cache_time";
private readonly IMemoryCache _memoryCache;
public CacheServices(IMemoryCache memoryCache)
{
_memoryCache = memoryCache;
}
[ApiDescriptionSettings(KeepName = true)] public DateTimeOffset GetOrCreate()
{
return _memoryCache.GetOrCreate(_timeCacheKey, entry => {
return DateTimeOffset.UtcNow;
});
}
}
}
设置缓存选项 #
内存缓存支持设置缓存时间、缓存大小、及绝对缓存过期时间等
_memoryCache.GetOrCreate(_timeCacheKey, entry =>
{
entry.SlidingExpiration = TimeSpan.FromSeconds(3); // 滑动缓存时间
return DateTimeOffset.UtcNow;
});
await _memoryCache.GetOrCreateAsync(_timeCacheKey, async entry =>
{
// 这里可以使用异步~~
});
}
关于缓存时间 #
只有具有可调过期的缓存项集存在过时的风险。如果访问的时间比滑动过期时间间隔更频繁,则该项将永不过期。
将弹性过期与绝对过期组合在一起,以保证项目在其绝对过期时间通过后过期。绝对过期会将项的上限设置为可缓存项的时间,同时仍允许项在可调整过期时间间隔内未请求时提前过期。
如果同时指定了绝对过期和可调过期时间,则过期时间以逻辑方式运算。如果滑动过期时间间隔或绝对过期时间通过,则从缓存中逐出该项。
如:
_memoryCache.GetOrCreate(_timeCacheKey, entry =>
{
entry.SetSlidingExpiration(TimeSpan.FromSeconds(3)); entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(20); return DateTime.Now;
});
前面的代码保证数据的缓存时间不超过绝对时间。
手动设置缓存选项 #
除了上面的Func<MemoryCacheEntryOptions, object> 方式设置缓存选项,我们可以手动创建并设置,如:
var cacheEntryOptions = new MemoryCacheEntryOptions() .SetSlidingExpiration(TimeSpan.FromSeconds(3));
_memoryCache.Set(_timeCacheKey, DateTimeOffset.UtcNow, cacheEntryOptions);
缓存依赖关系 #
下面的示例演示如何在依赖条目过期时使缓存条目过期。CancellationChangeToken 添加到缓存的项。当Cancel 在上调用时CancellationTokenSource ,将逐出两个缓存项。
public IActionResult CreateDependentEntries()
{
var cts = new CancellationTokenSource();
_cache.Set(CacheKeys.DependentCTS, cts);
using (var entry = _cache.CreateEntry(CacheKeys.Parent))
{
// expire this entry if the dependant entry expires. entry.Value = DateTime.Now;
entry.RegisterPostEvictionCallback(DependentEvictionCallback, this);
_cache.Set(CacheKeys.Child,
DateTime.Now,
new CancellationChangeToken(cts.Token));
}
return RedirectToAction("GetDependentEntries");
}
public IActionResult GetDependentEntries()
{
return View("Dependent", new DependentViewModel
{
ParentCachedTime = _cache.Get<DateTime?>(CacheKeys.Parent), ChildCachedTime = _cache.Get<DateTime?>(CacheKeys.Child), Message = _cache.Get<string>(CacheKeys.DependentMessage)
});
}
public IActionResult RemoveChildEntry()
{
_cache.Get<CancellationTokenSource>(CacheKeys.DependentCTS).Cancel(); return RedirectToAction("GetDependentEntries");
}
private static void DependentEvictionCallback(object key, object value, EvictionReason reason, object state)
{
var message = $"Parent entry was evicted. Reason: {reason}."; ((HomeController)state)._cache.Set(CacheKeys.DependentMessage, message);
}
使用CancellationTokenSource 允许将多个缓存条目作为一个组逐出。using 在上面的代码中,在块中创建的缓存条目using 将继承触发器和过期设置。 了解更多 想了解更多内存中的缓存知识可查阅ASP.NET Core-内存缓存 (opens new window) 章节。
分布式缓存 #
分布式缓存是由多个应用服务器共享的缓存,通常作为外部服务在访问它的应用服务器上维护。
分布式缓存可以提高ASP.NET Core 应用程序的性能和可伸缩性,尤其是在应用程序由云服务或服务器场托管时。
与其他缓存方案相比,分布式缓存具有多项优势,其中缓存的数据存储在单个应用服务器上。
当分布式缓存数据时,数据将(一致性) 跨多个服务器的请求存活在服务器重启和应用部署之间不使用本地内存分布式缓存配置是特定于实现的。本文介绍如何配置SQL Server 和Redis 分布式缓存。第三方实现也可用,例如GitHub 上的NCache (opens new window) (NCache) 。
无论选择哪种实现,应用都会使用接口与缓存交互IDistributedCache 。
使用条件 #
若要使用SQL Server 分布式缓存,则添加Microsoft.Extensions.Caching.SqlServer 包
若要使用Redis 分布式缓存,则添加Microsoft.Extensions.Caching.StackExchangeRedis 包
若要使用NCache 分布式缓存,则添加NCache.Microsoft.Extensions.Caching.OpenSource 包
IDistributedCache #
IDistributedCache 接口提供以下方法来处理分布式缓存实现中的项:
Get/GetAsync :接受字符串键,并检索缓存项作为byte[] 数组(如果在缓存中找到)
Set/SetAsync :使用字符串键将项(作为byte[] 数组) 添加到缓存中
Refresh/RefreshAsync :根据项的键刷新缓存中的项,重置其滑动过期超时(如果有)
Remove/RemoveAsync :根据缓存项的字符串键删除缓存项
分布式内存缓存 #
分布式内存缓存( AddDistributedMemoryCache )是一个框架提供的实现 IDistributedCache ,它将项存储在内存中。**分布式内存缓存不是实际的分布式缓存,缓存项由应用程序实例存储在运行应用程序的服务器上。**分布式内存缓存优点:
用于开发和测试方案。
在生产环境中使用单一服务器并且内存消耗不是问题。实现分布式内存缓存会抽象化缓存的数据存储。如果需要多个节点或容错,可以在将来实现真正的分布式缓存解决方案。 备注 在Mall3s 框架中,分布式内存缓存服务已经默认注册,无需手动调用services.AddDistributedMemoryCache(); 注册。
分布式SQL Server 缓存 #
分布式SQL Server 缓存实现( AddDistributedSqlServerCache ) 允许分布式缓存使用SQL Server 数据库作为其后备存储。
若要在SQL Server 实例中创建SQL Server 缓存的项表,可以使用sql-cache 工具。该工具将创建一个表,其中包含指定的名称和架构。
通过运行命令sql-cache create 创建一个表,提供SQL Server 实例(Data Source) 、数据库(Initial Catalog) 、架构(例如) dbo 和表名称。例如TestCache :
dotnet sql-cache create "Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=DistCache;Integrated Security=True;"
dbo TestCache创建成功后,在Startup.cs 中注册即可:
services.AddDistributedSqlServerCache(options =>
{
options.ConnectionString = _config["DistCache_ConnectionString"];
options.SchemaName = "dbo";
options.TableName = "TestCache";
});
分布式Redis 缓存 #
Redis 是内存中数据存储的开源数据存储,通常用作分布式缓存。在使用时通过services.AddStackExchangeRedisCache() 中注册即可。
这里不细讲Redis 相关内容,后续章节会使用基本例子演示。
Redis 基本配置:
services.AddStackExchangeRedisCache(options =>
{
// 连接字符串,这里也可以读取配置文件
options.Configuration = "192.168.0.11,password=aW1HAyupRKmiZn3Q";
// 键名前缀
options.InstanceName = "Mall3s_";
});
分布式NCache 缓存 #
NCache 是在.NET 和.Net Core 中以本机方式开发的开源内存中分布式缓存。 NCache 在本地工作并配置为分布式缓存群集,适用于在Azure 或其他托管平台上运行的ASP.NET Core 应用。 若要在本地计算机上安装和配置NCache ,请参阅适用于Windows的NCache入门指南。 (opens new window)
NCache 基本配置:
安装Alachisoft.NCache.OpenSource.SDK 包
在ncconf (opens new window) 中配置缓存群集
注册NCache 服务
services.AddNCacheDistributedCache(configuration =>
{
configuration.CacheName = "demoClusteredCache";
configuration.EnableLogs = true; configuration.ExceptionsEnabled = true;
});
分布式缓存使用 #
若要使用IDistributedCache 接口,请IDistributedCache 通过构造函数依赖关系注入。
public class IndexModel : PageModel
{
private readonly IDistributedCache _cache;
public IndexModel(IDistributedCache cache)
{
_cache = cache;
public string CachedTimeUTC { get; set; }
public async Task OnGetAsync()
{
CachedTimeUTC = "Cached Time Expired";
// 获取分布式缓存
var encodedCachedTimeUTC = await _cache.GetAsync("cachedTimeUTC");
if (encodedCachedTimeUTC != null)
{
CachedTimeUTC = Encoding.UTF8.GetString(encodedCachedTimeUTC);
}
}
public async Task<IActionResult> OnPostResetCachedTime()
{
var currentTimeUTC = DateTime.UtcNow.ToString();
byte[] encodedCurrentTimeUTC = Encoding.UTF8.GetBytes(currentTimeUTC);
// 设置分布式缓存
var options = new DistributedCacheEntryOptions() .SetSlidingExpiration(TimeSpan.FromSeconds(20));
await _cache.SetAsync("cachedTimeUTC", encodedCurrentTimeUTC, options);
return RedirectToPage();
}
}
分布式缓存建议 #
确定IDistributedCache 最适合你的应用的实现时,请考虑以下事项:
现有基础结构
性能要求
成本
团队经验
缓存解决方案通常依赖于内存中的存储以快速检索缓存的数据,但是,内存是有限的资源,并且很昂贵。仅将常用数据存储在缓存中。
通常,Redis 缓存提供比SQL Server **缓存更高的吞吐量和更低的延迟。**但是,通常需要进行基准测试来确定缓存策略的性能特征。
当SQL Server 用作分布式缓存后备存储时,对缓存使用同一数据库,并且应用的普通数据存储和检索会对这两种情况的性能产生负面影响。
建议使用分布式缓存后备存储的专用SQL Server 实例。