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过滤。
布隆过滤器(Bloom Filter)是1970年由布隆提出的。它实际上是一个很长的二进制向量和一系列随机映射函数。布隆过滤器可以用于检索一> 个元素是否在一个集合中。它的优点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率和删除困难
如果想要判断一个元素是不是在一个集合里,一般想到的是将所有元素保存起来,然后通过比较确定。链表,树等等数据结构都是这种思路. > 但是随着集合中元素的增加,我们需要的存储空间越来越大,检索速度也越来越慢(O(n),O(logn))。不过世界上还有一种叫作散列表(又叫哈> 希表,Hash table)的数据结构。它可以通过一个Hash函数将一个元素映射成一个位阵列(Bit array)中的一个点。这样一来,我们只要看看这个点是不是1就可以知道集合中有没有它了。这就是布隆过滤器的基本思想。
- 缓存雪崩
缓存雪崩是指在我们设置缓存时采用了相同的过期时间,导致缓存在某一时刻同时失效,请求全部转发到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()GetOrCreateAsyncvar 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扩展:
参考
ASP.NET Core 中的缓存的更多相关文章
- ASP.NET Core中的缓存[1]:如何在一个ASP.NET Core应用中使用缓存
.NET Core针对缓存提供了很好的支持 ,我们不仅可以选择将数据缓存在应用进程自身的内存中,还可以采用分布式的形式将缓存数据存储在一个“中心数据库”中.对于分布式缓存,.NET Core提供了针对 ...
- ASP.NET Core 中的SEO优化(1):中间件实现服务端静态化缓存
分享 最近在公司成功落地了一个用ASP.NET Core 开发前台的CMS项目,虽然对于表层的开发是兼容MVC5的,但是作为爱好者当然要用尽量多的ASP.NET Core新功能了. 背景 在项目开发的 ...
- C# 嵌入dll 动软代码生成器基础使用 系统缓存全解析 .NET开发中的事务处理大比拼 C#之数据类型学习 【基于EF Core的Code First模式的DotNetCore快速开发框架】完成对DB First代码生成的支持 基于EF Core的Code First模式的DotNetCore快速开发框架 【懒人有道】在asp.net core中实现程序集注入
C# 嵌入dll 在很多时候我们在生成C#exe文件时,如果在工程里调用了dll文件时,那么如果不加以处理的话在生成的exe文件运行时需要连同这个dll一起转移,相比于一个单独干净的exe,这种形 ...
- ASP.NET Core中使用Cache缓存
ASP.NET Core中使用Cache缓存 缓存介绍: 通过减少生成内容所需的工作,缓存可以显著提高应用的性能和可伸缩性. 缓存对不经常更改的数据效果最佳. 缓存生成的数据副本的返回速度可以比从原始 ...
- ASP.NET Core中的Http缓存
ASP.NET Core中的Http缓存 Http响应缓存可减少客户端或代理对web服务器发出的请求数.响应缓存还减少了web服务器生成响应所需的工作量.响应缓存由Http请求中的header控制. ...
- ASP.NET Core教程:ASP.NET Core中使用Redis缓存
参考网址:https://www.cnblogs.com/dotnet261010/p/12033624.html 一.前言 我们这里以StackExchange.Redis为例,讲解如何在ASP.N ...
- 在ASP.NET Core中使用百度在线编辑器UEditor
在ASP.NET Core中使用百度在线编辑器UEditor 0x00 起因 最近需要一个在线编辑器,之前听人说过百度的UEditor不错,去官网下了一个.不过服务端只有ASP.NET版的,如果是为了 ...
- ASP.NET Core 中文文档 第三章 原理(13)管理应用程序状态
原文:Managing Application State 作者:Steve Smith 翻译:姚阿勇(Dr.Yao) 校对:高嵩 在 ASP.NET Core 中,有多种途径可以对应用程序的状态进行 ...
- ASP.NET Core中的依赖注入(5): ServiceProvider实现揭秘 【总体设计 】
本系列前面的文章我们主要以编程的角度对ASP.NET Core的依赖注入系统进行了详细的介绍,如果读者朋友们对这些内容具有深刻的理解,我相信你们已经可以正确是使用这些与依赖注入相关的API了.如果你还 ...
随机推荐
- spring集成struts2
Struts2前身是WebWork,核心并没有改变,其实就是把WebWork改名为struts2,与Struts1一点关系没有. Struts2中通过ObjectFactory接口实现创建及获取Act ...
- Oracle EBS中有关Form的触发器的执行顺序
http://blog.csdn.net/postfxj/article/details/8135769 触发器执行顺序: 1. 当打开FORM时: (1) PRE-FORM (2) ...
- delphi 动态加载dll
引入文件 DLL比较复杂时,可以为它的声明专门创建一个引入单元,这会使该DLL变得更加容易维护和查看.引入单元的格式如下: unit MyDllImport; {Import unit for MyD ...
- git archive命令详解
git archive可以将加了tag的某个版本打包提取出来,例如: git archive -v --format= > v0..zip --format表示打包的格式,如zip,-v表示对应 ...
- Jersey Client Post Bean参数
代码: public static void main(String[] args) { Student st = new Student("Adriana", "Bar ...
- [翻译]NUnit---Property and Random Attributes(十四)
小记:由于工作琐碎,没得心情翻译而且也在看<CLR vis C#>,所以断更了差不多5个月,现在继续翻译,保证会翻译完成,不会虎头蛇尾. 另:NUnit已经更新到2.6.3版本,虽然正在开 ...
- (一)Mahapps安装与使用
一.Mahapps安装 1.NuGet程序包安装 右击项目-->选择 “管理Nuget程序包”-->搜索“MahApps.Metro” 2.包管理控制台 选择“工具”-->“NuGe ...
- UWP开发---嵌套DataTemplate&模板选择器
对于Json结构体items不完全一致的情况下,在UWP平台是如何处理数据,并通过不同的模板选择,进行显示呢? 一,嵌套Json分析 1,结构 通过抓取index的API(点击请求API)获取到的js ...
- .NET Core中使用Dapper操作Oracle存储过程最佳实践
为什么说是最佳实践呢?因为在实际开发中踩坑了,而且发现网上大多数文章给出的解决方法都不能很好地解决问题.尤其是在获取类型为OracleDbType.RefCursor,输出为:ParameterDir ...
- Socket编程入门
socket基本知识 网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket. 建立网络通信连接至少要一对端口号(socket).socket本质是编程接口(AP ...