当前位置: 首页 > news >正文

【ASP.NET Core】内存缓存(MemoryCache)原理、应用及常见问题解析

系列文章目录

链接: 【ASP.NET Core】REST与RESTful详解,从理论到实现
链接: 【ASP.NET Core】深入理解Controller的工作机制


文章目录

  • 系列文章目录
  • 前言
  • 一、ASP.NET Core中的内存缓存——MemoryCache
    • 1.1 内存缓存的结构
    • 1.2 MemoryCache的注册
    • 1.3 MemoryCache的配置项
      • 1.3.1 缓存时间的过期策略
      • 1.3.2 缓存的优先级
    • 1.4 MemoryCache的基本操作与扩展
  • 二、内存缓存中碰到的各类问题
    • 2.1 缓存穿透
    • 2.2 缓存击穿
    • 2.3 缓存雪崩
    • 2.4 缓存键重复
  • 总结


前言

不知道大家在发现执行SQL查询时,相同的查询再次执行所消耗的时间远少于第一次。这是因为数据库维护一套用于缓存SQL语句解析和优化后的执行计划的机制,避免对重复SQL语句进行重复的解析生成抽象语法树、语义检查、优化等耗时操作。
还有就是在浏览器打开网页的时候,通过网络监视窗,我们也能发现很多请求来自于【memory cache】,字面意义上的一种缓存。也是通过缓存来降低HTTP请求。

这就是缓存的魔力,在系统中简单又有效的工具,投入小但收效甚大。本文将聚焦于ASP.NET Core中的内存缓存,讨论它如何在我们的程序中起到提升应用性能的作用。


提示:以下是本篇文章正文内容,下面案例可供参考

一、ASP.NET Core中的内存缓存——MemoryCache

1.1 内存缓存的结构

在ASP.NET Core中,内存缓存(MemoryCache)一种将数据存储在应用程序进程内存中的机制,其中数据是以一种键值对的形式存储。

1.2 MemoryCache的注册

在ASP.NET Core中,我们一般采用Program文件里注册服务,在程序里通过依赖注入的方式使用MemoryCache。AddMemoryCache是一个MemoryCacheServiceCollectionExtensions下的静态扩展方法里是将MemoryCache以单例模式注册到IOC容器里,并且通过AddOptions添加选项模式的依赖,支持配置的动态更新和验证。
Program.cs 注册

builder.Services.AddMemoryCache();

MemoryCacheServiceCollectionExtensions扩展方法

public static IServiceCollection AddMemoryCache(this IServiceCollection services)
{ThrowHelper.ThrowIfNull(services);services.AddOptions();services.TryAdd(ServiceDescriptor.Singleton<IMemoryCache, MemoryCache>());return services;
}public static IServiceCollection AddOptions(this IServiceCollection services)
{System.ThrowHelper.ThrowIfNull(services, "services");services.TryAdd(ServiceDescriptor.Singleton(typeof(IOptions<>), typeof(UnnamedOptionsManager<>)));services.TryAdd(ServiceDescriptor.Scoped(typeof(IOptionsSnapshot<>), typeof(OptionsManager<>)));services.TryAdd(ServiceDescriptor.Singleton(typeof(IOptionsMonitor<>), typeof(OptionsMonitor<>)));services.TryAdd(ServiceDescriptor.Transient(typeof(IOptionsFactory<>), typeof(OptionsFactory<>)));services.TryAdd(ServiceDescriptor.Singleton(typeof(IOptionsMonitorCache<>), typeof(OptionsCache<>)));return services;
}

1.3 MemoryCache的配置项

1.3.1 缓存时间的过期策略

MemoryCache中的过期分两种,其一是绝对过期 (Absolute Expiration),另一个是滑动过期 (Sliding Expiration)。

  • 绝对过期 :SetAbsoluteExpiration(DateTimeOffset/TimeSpan)
    缓存项在指定确切时间点后失效。适用于数据在特定时间点后一定过期的场景。当使用 DateTimeOffset 时,缓存项在指定确切时间点后失效。使用 TimeSpan 时,缓存项在当前时间基础上加上指定的时间间隔后失效。

  • 滑动过期 :SetSlidingExpiration(TimeSpan)
    缓存项在指定的时间间隔内没有被访问则失效。每次访问(读取)都会重置这个倒计时。适用于访问频繁的数据,如用户会话数据。但是需要保证过期永不失效的问题。

如果一个缓存项一直被频繁访问,那么这个缓存项就会一直被续期而不过期。可以对一个缓存项同时设定滑动过期时间和绝对过期时间,并且把绝对过期时间设定的比滑动过期时间长,这样缓存项的内容会在绝对过期时间内随着访问被滑动续期,但是一旦超过了绝对过期时间,缓存项就会被删除。这种就是绝对过期和滑动过期组合使用的方式。

1.3.2 缓存的优先级

缓存项的优先级(CacheItemPriority)是控制缓存清理策略的重要参数。当内存不足时,优先级较低的项会被优先回收,以腾出空间给更重要的数据。
通过CacheItemPriority枚举型定义四种类型的优先级Low,Normal(默认),High,NeverRemove(永不删除)
Controller里示例

[HttpGet("{id}")]
public async Task<ActionResult<Movie?>> Movies(int id)
{//_logger.LogInformation("1");_logger.LogDebug("开始获取数据");Movie? movie = await _memoryCache.GetOrCreateAsync($"Movie{id}", async (e) =>{e.SetPriority(CacheItemPriority.High);//缓存优先级高e.SetAbsoluteExpiration(TimeSpan.FromMinutes(10));//绝对过期时间-10分钟e.SetSlidingExpiration(TimeSpan.FromMinutes(5));//缓存滑动过期时间-5分钟//e.SetAbsoluteExpiration(TimeSpan.FromSeconds(Random.Shared.Next(1000)));return await _movieAssert.GetMovieAsync(id);});if (movie is null){return NotFound("没有数据");}return movie;
}

1.4 MemoryCache的基本操作与扩展

通过观察Microsoft.Extensions.Caching.Memory下的IMemoryCache接口。我们发现创建缓存实体(CreateEntry),获取缓存统计信息(GetCurrentStatistics),移除指定缓存(Remove),和视图获取指定缓存(TryGetValue)方法。
并且在Microsoft.Extensions.Caching.Memory命名空间下,还有一个CacheExtensions静态扩展方法,提供更加丰富的操作方法。比如

  1. TryGetValue:试图获取某个key的TItem值,用out修饰。并且返回bool值。在该扩展方法中使用IMemoryCache的TryGetValue获取值。如果值为空附默认值并返回false,如果值存在并且类型匹配,给TItem赋值返回true。

    public static bool TryGetValue<TItem>(this IMemoryCache cache, object key, out TItem? value)
    {if (cache.TryGetValue(key, out object value2)){if (value2 == null){value = default(TItem);return true;}if (value2 is TItem val){value = val;return true;}}value = default(TItem);return false;
    }
  2. GetOrCreateAsync:该方法通过试图获取key对应的缓存值,如果缓存不存在则新建一个新的缓存。通过上文封装的静态方法TryGetValue判断缓存是否存在。如果不存在就通过IMemoryCache的CreateEntry创建缓存,并且调用委托执行工厂方法。最终返回缓存值,保证即使是不存在的数据也有一个对应的缓存,防止缓存穿透。

    public static async Task<TItem?> GetOrCreateAsync<TItem>(this IMemoryCache cache, object key, Func<ICacheEntry, Task<TItem>> factory, MemoryCacheEntryOptions? createOptions)
    {if (!cache.TryGetValue(key, out object value)){using ICacheEntry entry = cache.CreateEntry(key);if (createOptions != null){entry.SetOptions(createOptions);}value = (entry.Value = await factory(entry).ConfigureAwait(continueOnCapturedContext: false));}return (TItem)value;
    }

CacheExtensions还有Get和Set方法,获取和写入缓存,以及一系列重载。

二、内存缓存中碰到的各类问题

2.1 缓存穿透

频繁查询不合规的数据,但实际数据库里并不存在,每一次查询都会引起数据库的查询操作,给数据库造成特别大的性能压力,导致每次请求都绕过缓存直接访问数据库。

解决方法:把“查不到”也当成一个数据放入缓存,用封装好的GetOrCreateAsync,因为它把null值也当成合法的缓存值
在上面的GetOrCreateAsync源码分析中已经分析了GetOrCreateAsync是如何解决缓存穿透的问题

2.2 缓存击穿

某个热点Key在缓存中突然失效,比如过期了或者被删除了,导致大量并发请求瞬间失去缓存保护,直接涌向数据库,造成数据库短时间内压力剧增的现象。

解决方法:不设置过期时间,通过后台定时任务主动更新缓存。或者是使用Lock锁,当缓存失效时保证只有一条请求能实际执行数据的获取,其他请求等待该请求更新缓存后再获取数据。

2.3 缓存雪崩

缓存雪崩是缓存项集中过期导致原本由缓存承接的大量请求瞬间涌向数据库,数据库因无法承受压力而崩溃的现象。

解决方法:将过期时间打散,比如在设置基础过期时间上添加一段时间范围的随机数,以此保证缓存不会在同一个时间点过期。

2.4 缓存键重复

键需要精心设计以确保唯一性和可读性。避免键冲突。


总结

文章围绕ASP.NET Core 中 MemoryCache 展开,先介绍其结构、注册方式、配置项(过期策略、优先级)及基本操作与扩展方法,后阐述内存缓存常见问题(穿透、击穿、雪崩)及解决办法。

http://www.lryc.cn/news/588286.html

相关文章:

  • 位置编码类型彩色图解
  • 张艺兴探班RED女团一周年舞台,见证21岁的梦想落地生根
  • 代码随想录算法训练营第二十天
  • 一文读懂现代卷积神经网络—稠密连接网络(DenseNet)
  • Journal of Engineering Mechanics and Machinery,工程力学期刊,1-2天录用,7天出版,即将送检!
  • 自定义类型 - 联合体与枚举(百度笔试题算法优化)
  • 前端将传回的List数据组织成树形数据并展示
  • 用于监测线性基础设施的分布式声学传感:现状与趋势
  • 深度剖析:动态接口代理核心原理与高级应用
  • APP测试之Monkey压力测试
  • Relocations in generic ELF (EM: 40)
  • Qt小组件 - 2(布局)瀑布流布局,GridLayout,FlowLayout
  • 虚拟列表组件如果滑动速度过快导致渲染性能问题
  • UART寄存器介绍
  • 前端学习5:Float学习(仅简单了解,引出flex)
  • 015 程序地址空间入门
  • Life:Internship in OnSea Day 22
  • 某ctv视频下载逆向思路
  • 云原生技术与应用-Containerd容器技术详解
  • Git LFS 操作处理Github上传大文件操作记录
  • Spring Boot 双数据源配置
  • RPC vs RESTful架构选择背后的技术博弈
  • SecretFlow 隐语 (2) --- 隐语架构概览
  • 【安卓笔记】进程和线程的基础知识
  • Ubuntu20.05上安装Clang 15
  • 【安卓笔记】线程基本使用:锁、锁案例
  • 新型eSIM攻击技术可克隆用户资料并劫持手机身份
  • linux 内核: 访问当前进程的 task_struct
  • [论文阅读] 人工智能 + 软件工程 | 用大语言模型+排名机制,让代码评论自动更新更靠谱
  • android Perfetto cpu分析教程及案例