7.4缓存

简介: 缓存是一个用来保存数据的区域,从缓存中读取数据要比从数据源读取数据的速度快很多。如果可以从缓存中获取要获取的数据则称之为“缓存命中”,多次请求命中的请求占全部请求的百分比叫做“命中率”,如果数据源中的数据保存到了缓存后,发生了变化则称之为“缓存数据不一致”

7.4缓存

缓存是一个用来保存数据的区域,从缓存中读取数据要比从数据源读取数据的速度快很多。如果可以从缓存中获取要获取的数据则称之为“缓存命中”,多次请求命中的请求占全部请求的百分比叫做“命中率”,如果数据源中的数据保存到了缓存后,发生了变化则称之为“缓存数据不一致”。

客户端缓存

RFC 7234是HTTP中对缓存进行控制的规范,由cache-control相应报头来控制,如服务器给浏览器的响应头文件中cache-control的值为max-age=60,表示服务器指示浏览器缓存这个响应内容60s。在ASP.Net Core中,只需要给要进行缓存控制的控制器的操作方法添加ResponseCacheAttribute。

[HttpGet]

[ResponseCache(Duration=60)]//缓存60s

publicDateTimeNow()

{

   returnDateTime.Now;

}

默认情况下,[ResponseCache]是通过cache-control响应报文头来控制浏览器,如果浏览器不支持缓存这个设置不会生效。

服务器响应缓存*

安装了“响应缓存中间件”,ASP.NET Core不仅会根据[ResponseCache]来设置响应报文,还会在服务器对响应进行服务端缓存。当在特定的时间内访问服务器相同的请求地址,服务器不会执行操作方法,而是将缓存的数据直接返回给客户端。

使用方法:

  1. 在操作方法上面使用[ResponseCache]
  2. 在Program.cs文件中app.MapControllers之前加上app.UseResponseCaching

注意:如果开启了CORS跨域请求,确保app.UseCors在app.UseResponseCaching之前

像chorme浏览器,如果浏览器中设置禁用浏览器缓存,但是启用了服务器的响应缓存中间件,但服务器的缓存仍然会不起作用。这是因为禁用浏览器缓存后,浏览器会向服务器端的请求头文件中加入cache-control:no-cache,这样服务器也会禁用缓存机制。

建议:不启用“响应缓存中间件”,如果需要在客户端进行缓存,则只需要使用[ResponseCache]即可

内存缓存

内存缓存中保存的是一系列的键值对,不同的缓存内容具有不同的缓存键,每个缓存键对应一个缓存值。内存缓存保存在当前运行网站程序的内存中,和进程相关。使用内存缓存,确保在Program.cs中的builder.Build之前添加builder.Services.AddMemoryCache来讲内存缓存的相关服务注册到容器中。使用内存缓存的时候,主要使用IMemoryCache接口

方法 说明
bool TryGetValue(object key, out object value) 尝试获取键为key的缓存值,如果有,则返回true否则返回false
void Remove(object key) 删除键为key的缓存值
T Set<T>(object key,T value) 设置缓存键key的缓存值为value
T GetOrCreate<T>(object key,Func<ICacheEntry,T> factory) 获取键为key的缓存值,如果缓存中没有,则调用factory指向的函数从数据源获取数据,创建缓存并返回值
Task<T> GetOrCreateAsync<T>(object key,Func<ICacheEntry,Task<T>> factory) 异步的GetOrCreate方法,ICacheEntry可以对缓存项进行详细的设置,比如缓存项被清除的回调、缓存项的优先级

[Route("[controller]/[action]")]

[ApiController]

publicclassTest1Controller : ControllerBase

{

   privatereadonlyILogger<Test1Controller>logger;

   privatereadonlyMyDbContextdbCtx;

   privatereadonlyIMemoryCachememCache;//使用依赖注入的形式使用IMemoryCache

   publicTest1Controller(MyDbContextdbCtx, IMemoryCachememCache, ILogger<Test1Controller>logger)

   {

       this.dbCtx=dbCtx;

       this.memCache=memCache;

       this.logger=logger;

   }

   [HttpGet]

   publicasyncTask<Book[]>GetBooks()

   {

       logger.LogInformation("开始执行GetBooks");

       varitems=awaitmemCache.GetOrCreateAsync("AllBooks", async (e) =>

       {

           logger.LogInformation("从数据库中读取数据");

           returnawaitdbCtx.Books.ToArrayAsync();

       });

       logger.LogInformation("把数据返回给调用者");

       returnitems;

   }

}

过期策略

  • 绝对过期时间

自设置缓存之后的指定时间后,缓存被删除

  • 滑动过期时间

自设置缓存之后的指定时间后,如果对缓存数据没有访问,则删除,如果有访问,则缓存项会以最后一次的时间为准自动续期

//绝对过期时间

logger.LogInformation("开始执行Demo1:"+DateTime.Now);

varitems=awaitmemCache.GetOrCreateAsync("AllBooks", async (e) => {

   e.AbsoluteExpirationRelativeToNow=TimeSpan.FromSeconds(10);//设置绝对过期时间

   logger.LogInformation("从数据库中读取数据");

   returnawaitdbCtx.Books.ToArrayAsync();

});

logger.LogInformation("Demo1执行结束");

//滑动过期时间

logger.LogInformation("开始执行Demo2:"+DateTime.Now);

varitems=awaitmemCache.GetOrCreateAsync("AllBooks2", async (e) => {

   e.SlidingExpiration=TimeSpan.FromSeconds(10);//活动过期时间

   logger.LogInformation("Demo2从数据库中读取数据");

   returnawaitdbCtx.Books.ToArrayAsync();

});

logger.LogInformation("Demo2执行结束");

  • 混合使用过期策略

一般设置绝对过期时间比滑动时间长,绝对时间到期后,无论滑动时间有没有到期,都会删除缓存

//混合使用过期时间策略

logger.LogInformation("开始执行Demo3:"+DateTime.Now);

varitems=awaitmemCache.GetOrCreateAsync("AllBooks3", async (e) => {

   e.SlidingExpiration=TimeSpan.FromSeconds(10);//滑动时间

   e.AbsoluteExpirationRelativeToNow=TimeSpan.FromSeconds(30);//绝对时间

   logger.LogInformation("Demo3从数据库中读取数据");

   returnawaitdbCtx.Books.ToArrayAsync();

});

logger.LogInformation("Demo3执行结束");

混合使用过期策略可以实现不经常被访问的数据不会长时间占内存,而频繁被访问的数据会避免数据不一致的问题

所有的缓存都会出现数据不一致的情况,对应不允许数据不一致的情况,可以直接不用缓存。

缓存穿透

使用IMemoryCache中的Get方法,会根据key来查找缓存项,如果找不到则返回Null

stringcacheKey="Book"+id;//缓存键

Book?b=memCache.Get<Book?>(cacheKey);

if (b==null)//如果缓存中没有数据 如果有恶意者使用不存在的某个Id大量访问,则会不断的查询数据库,导致服务器崩溃,这叫做缓存穿透

{

   //查询数据库,然后写入缓存

   b=awaitdbCtx.Books.FindAsync(id);

   memCache.Set(cacheKey, b);

}

缓存穿透的问题是由于将查不到的数据用Null来表示,如果把“查不到”也当数据放到缓存中,即{key:不存在的某个Id,value:null}。日常开发中使用GetOrCreateAsync方法就可以避免缓存穿透,它将null也作为了合法的缓存值。

logger.LogInformation("开始执行Demo5");

stringcacheKey="Book"+id;

varbook=awaitmemCache.GetOrCreateAsync(cacheKey, async (e) => {

   varb=awaitdbCtx.Books.FindAsync(id);

   logger.LogInformation("数据库查询:{0}", b==null?"为空" : "不为空");

   returnb;

});

logger.LogInformation("Demo5执行结束:{0}", book==null?"为空" : "不为空");

returnbook;

缓存雪崩

一般会是在网站启动的时候将大量的数据放到缓存以提高响应速度,如果这些缓存设定了相同的过期时间,则会同时过期,如果有访问则会导致大量的数据库访问,这样会同时的访问会将数据库服务器压垮。解决这个问题的办法是在基础过期的时间上增加一个随机的过期时间,这样就不会集中到同一时间了。

注意:设置缓存的时候,IQueryable、IEnumerable等类型可能存在延迟加载,当取出这种类型的变量时去执行时,如果他们所需要的对象已经被释放则会执行失败。因此最好将这两种对象转换为数组或者List类型再放到缓存中。

分布式缓存

在分布式系统中,将缓存数据放到专门的缓存服务器中,所有的Web都通过缓存服务器来进行写入和读取。

.net core中使用IDistributedCache接口来进行操作,分布式缓存中提供了DistributedCacheEntryOptions来配置过期时间。不同类型的缓存服务器支持的缓存键和缓存值不相同,所以IDistributedCache统一将string作为key的类型,将byte[]作为值类型。

推荐使用Redis数据库作为缓存服务器,微软也提供了Redis作为缓存服务器的Nuget包Microsoft.Extensions.Caching.StackExchangeRedis。

  1. 在Program.cs的builder.Build之前添加代码进行注册Redis缓存

builder.Services.AddStackExchangeRedisCache(options=>

{

   options.Configuration="localhost";//redis服务器的连接配置

   options.InstanceName="yzk_";//其他程序也许也在使用redis服务器,为避免冲突,增加yzk_前缀

});

  1. 读写redis中的缓存数据

publicclassTest1Controller : ControllerBase

{

   privatereadonlyIDistributedCachedistCache;

   publicTest1Controller(IDistributedCachedistCache)

   {

       this.distCache=distCache;

   }

   [HttpGet]

   publicstringNow()

   {

       strings=distCache.GetString("Now");

       if (s==null)

       {

           s=DateTime.Now.ToString();

           varopt=newDistributedCacheEntryOptions();

           opt.AbsoluteExpirationRelativeToNow=TimeSpan.FromSeconds(30);

           distCache.SetString("Now", s, opt);//将s存放在yzh_Now键对应的值中

       }

       returns;

   }

}

相关文章
|
并行计算 Ubuntu 机器人
计算机视觉教程7-3:Openpose配置与实践
计算机视觉教程7-3:Openpose配置与实践
3794 0
计算机视觉教程7-3:Openpose配置与实践
|
数据采集 JavaScript 前端开发
“所见即所爬”:使用Pyppeteer无头浏览器抓取动态壁纸
“所见即所爬”:使用Pyppeteer无头浏览器抓取动态壁纸
|
人工智能 自然语言处理 算法
AI-Researcher:告别熬夜肝论文!港大开源AI科研神器,从选题到发表全自动
AI-Researcher 是香港大学数据科学实验室推出的开源自动化科研工具,基于大型语言模型(LLM)代理,支持从研究想法到论文发表的全流程自动化,涵盖文献综述、算法设计、实验验证和论文撰写等功能。
2173 8
AI-Researcher:告别熬夜肝论文!港大开源AI科研神器,从选题到发表全自动
|
SQL 监控 IDE
实时开发IDE!数据开发效率开挂
杭州奥零数据科技有限公司成立于2023年,专注于数据中台业务,维护开源项目AllData并提供商业版解决方案。AllData提供数据集成、存储、开发、治理及BI展示等一站式服务,支持AI大模型应用,助力企业高效利用数据价值。
实时开发IDE!数据开发效率开挂
|
算法 Unix Linux
7z——拥有极高压缩比的开源压缩软件
【11月更文挑战第6天】7z 是一种开源压缩软件,具有高压缩比、开源特性、功能丰富、跨平台支持和良好兼容性等特点。它采用先进的压缩算法,支持大文件、加密和多种压缩格式,广泛应用于个人和企业文件管理。
2754 3
|
机器学习/深度学习 监控 机器人
量化交易机器人系统开发逻辑策略及源码示例
量化交易机器人是一种通过编程实现自动化交易决策的金融工具。其开发流程包括需求分析、系统设计、开发实现、测试优化、部署上线、风险管理及数据分析。示例中展示了使用Python实现的简单双均线策略,计算交易信号并输出累计收益率。
|
SQL 监控 Java
nacos常见问题之dubbo+nacos+springboot3的native打包成功后运行出现异常如何解决
Nacos是阿里云开源的服务发现和配置管理平台,用于构建动态微服务应用架构;本汇总针对Nacos在实际应用中用户常遇到的问题进行了归纳和解答,旨在帮助开发者和运维人员高效解决使用Nacos时的各类疑难杂症。
1335 2
|
传感器 数据采集 算法
python实现ModBusRTU客户端方式
python实现基于串口通信的ModBusRTU客户端是一件简单的事情,只要通过pymodbus模块就可以实现。
|
Rust Unix Windows
使用Cargo国内镜像提升Rust开发效率
使用Cargo国内镜像提升Rust开发效率
4237 1