C#.NET MemoryCache 深入解析:本地缓存机制与最佳实践

作者:唐青枫日期:2025/11/6

简介

在许多应用程序中,缓存是提升性能的常见方法,尤其是在访问频繁且不经常变化的数据时。MemoryCache.NET 提供的一个内存缓存实现,它允许在内存中存储数据,以减少对数据库、文件系统或其他远程服务的访问,进而提升系统响应速度。

MemoryCache 的核心优势是:

  • 高效:内存操作非常快速,适合用于缓存短期有效的数据。
  • 轻量:它是一个线程安全的缓存系统,且易于在 .NET 应用中配置和使用。
  • 灵活:支持过期时间、优先级设置等多种功能,能够满足大多数缓存需求。

核心功能

  • 线程安全
    • MemoryCache 是线程安全的,允许多个线程同时访问缓存中的数据。
  • 过期策略
    • 绝对过期:指定一个具体的时间点,缓存项在该时间点后过期。
    • 滑动过期:缓存项最后访问后的指定时间段内过期。
  • 缓存项优先级
    • 可以为缓存项设置优先级,允许缓存管理器在内存不足时根据优先级回收缓存项。
  • 回调:
    • PostEvictionCallback:缓存项移除时触发回调。
    • CacheEntryOptions:支持自定义过期和移除逻辑。
  • 依赖关系:
    • 使用 ChangeToken 支持基于外部信号的缓存失效(如文件更改)。
  • 线程安全:内置并发控制,支持多线程访问。
  • DI 集成:通过 IMemoryCache 接口与 ASP.NET Core DI 无缝集成。
  • 数据大小限制
    • 可以设置缓存的最大容量,以防止占用过多内存。
  • 支持绝对过期和滑动过期组合使用
    • 能灵活配置缓存失效的时间策略。

核心 API

MemoryCache 主要通过 IMemoryCache 接口操作,位于 Microsoft.Extensions.Caching.Memory 命名空间。核心 API 如下:

注意: 旧版本.net 使用 System.Runtime.Caching新版本.net 使用 Microsoft.Extensions.Caching.Memory

API 用法

创建与初始化 MemoryCache

1using System.Runtime.Caching;
2
3// 创建默认的 MemoryCache 实例
4MemoryCache cache = MemoryCache.Default;
5
6// 或者创建带名称的实例
7MemoryCache customCache = new MemoryCache("MyCache");
8

添加缓存项

1CacheItemPolicy policy = new CacheItemPolicy
2{
3    AbsoluteExpiration = DateTimeOffset.Now.AddMinutes(10), // 设置绝对过期时间
4    SlidingExpiration = TimeSpan.FromMinutes(5),           // 设置滑动过期时间
5    Priority = CacheItemPriority.Default,                  // 设置优先级
6    RemovedCallback = args => { Console.WriteLine("缓存项已移除"); } // 过期时执行回调
7};
8
9cache.Add("key", "value", policy); // 向缓存中添加项
10

获取缓存项

1var value = cache.Get("key"); // 获取缓存项
2Console.WriteLine(value); // 输出:value
3

更新缓存项

1cache.Set("key", "newValue", DateTimeOffset.Now.AddMinutes(5)); // 更新缓存项
2

移除缓存项

1cache.Remove("key"); // 移除缓存项
2

使用 TryGetValue 方法检查缓存项

1object value;
2if (cache.TryGetValue("key", out value))
3{
4    Console.WriteLine(value); // 如果存在,打印缓存值
5}
6else
7{
8    Console.WriteLine("缓存项不存在"); // 如果不存在,打印提示
9}
10

获取所有缓存项(遍历)

1foreach (var item in cache)
2{
3    Console.WriteLine($"Key: {item.Key}, Value: {item.Value}");
4}
5

自定义缓存实例

1// 创建带自定义配置的缓存
2var cacheConfig = new NameValueCollection
3{
4    {"cacheMemoryLimitMegabytes", "100"}, // 100MB 内存限制
5    {"physicalMemoryLimitPercentage", "50"}, // 物理内存50%
6    {"pollingInterval", "00:05:00"} // 5分钟检查一次
7};
8
9var customCache = new MemoryCache("MyCustomCache", cacheConfig);
10
11// 使用自定义缓存
12customCache.Set("userData", userProfile, new CacheItemPolicy());
13

优先级策略

1var highPriorityPolicy = new CacheItemPolicy
2{
3    Priority = CacheItemPriority.NotRemovable // 内存不足时不会被移除
4};
5
6var lowPriorityPolicy = new CacheItemPolicy
7{
8    Priority = CacheItemPriority.Default // 默认优先级
9};
10

ASP.NET Core 中通过 DI 使用 IMemoryCache

1using Microsoft.AspNetCore.Mvc;
2using Microsoft.Extensions.Caching.Memory;
3using System;
4
5[ApiController]
6[Route("api/cache")]
7public class CacheController : ControllerBase
8{
9    private readonly IMemoryCache _cache;
10
11    public CacheController(IMemoryCache cache)
12    {
13        _cache = cache;
14    }
15
16    [HttpGet("set")]
17    public IActionResult SetCache()
18    {
19        var key = "myKey";
20        var value = $"Data at {DateTime.Now:HH:mm:ss}";
21        var options = new MemoryCacheEntryOptions
22        {
23            AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(5),
24            SlidingExpiration = TimeSpan.FromSeconds(30)
25        };
26
27        _cache.Set(key, value, options);
28        return Ok($"Cached: {value}");
29    }
30
31    [HttpGet("get")]
32    public IActionResult GetCache()
33    {
34        if (_cache.TryGetValue("myKey", out string value))
35            return Ok($"Cached value: {value}");
36        return NotFound("Cache miss");
37    }
38}
39

注册服务:

1var builder = WebApplication.CreateBuilder(args);
2builder.Services.AddMemoryCache(); // 注册 MemoryCache
3builder.Services.AddControllers();
4

缓存依赖(ChangeToken)

基于外部信号失效(如文件更改):

1using Microsoft.AspNetCore.Mvc;
2using Microsoft.Extensions.Caching.Memory;
3using Microsoft.Extensions.FileProviders;
4using System;
5using System.IO;
6using System.Threading.Tasks;
7
8[ApiController]
9[Route("api/config")]
10public class ConfigController : ControllerBase
11{
12    private readonly IMemoryCache _cache;
13    private readonly PhysicalFileProvider _fileProvider;
14
15    public ConfigController(IMemoryCache cache)
16    {
17        _cache = cache;
18        _fileProvider = new PhysicalFileProvider(Directory.GetCurrentDirectory());
19    }
20
21    [HttpGet]
22    public async Task<IActionResult> GetConfig()
23    {
24        var cacheKey = "configKey";
25        var config = await _cache.GetOrCreateAsync(cacheKey, async entry =>
26        {
27            var file = _fileProvider.GetFileInfo("config.txt");
28            entry.AddExpirationToken(_fileProvider.Watch("config.txt"));
29            entry.SlidingExpiration = TimeSpan.FromMinutes(5);
30            // 读取文件
31            string content = await File.ReadAllTextAsync(file.PhysicalPath);
32            return content;
33        });
34
35        return Ok(config);
36    }
37}
38

说明:

  • 使用 PhysicalFileProvider.Watch 创建 IChangeToken
  • 文件更改时缓存失效,重新加载。

性能优化

避免过多缓存数据

设置合理的缓存大小和最大容量,避免 MemoryCache 占用过多内存。可以通过设置缓存的最大项数或缓存最大内存大小来控制。

1var policy = new CacheItemPolicy
2{
3    AbsoluteExpiration = DateTimeOffset.Now.AddMinutes(10)
4};
5var cache = new MemoryCache("MyCache", new NameValueCollection
6{
7    { "CacheMemoryLimitMegabytes", "100" },  // 限制最大内存为 100 MB
8    { "PhysicalMemoryLimitPercentage", "80" }, // 限制物理内存占用为 80%
9    { "PollingInterval", "00:01:00" } // 每 1 分钟检查一次缓存
10});
11

合理使用过期策略

  • 对于一些短时间有效的数据,建议使用滑动过期(SlidingExpiration),它可以保证数据的最新性。
  • 对于长期不变的数据,可以使用绝对过期(AbsoluteExpiration),避免内存被长时间占用。

避免缓存穿透

  • 在向缓存写入数据之前,确保数据已经在某些存储中存在,避免因缓存项缺失导致每次访问都去请求外部数据库。
  • 如果缓存为空,可以缓存空值,以防止缓存穿透。
1public T GetOrCreate<T>(string key, Func<T> factory, TimeSpan expiration)
2{
3    if (cache.Get(key) is T result) 
4        return result;
5    
6    // 特殊值标记缓存穿透
7    if ("__NULL__".Equals(cache.Get(key)))
8        return default;
9    
10    try
11    {
12        result = factory();
13        if (result == null)
14        {
15            // 缓存空值避免反复查询
16            cache.Set(key, "__NULL__", TimeSpan.FromMinutes(5));
17            return default;
18        }
19        
20        cache.Set(key, result, expiration);
21        return result;
22    }
23    catch
24    {
25        // 异常时设置短期空缓存
26        cache.Set(key, "__NULL__", TimeSpan.FromMinutes(1));
27        throw;
28    }
29}
30

合理设置回调函数

  • 使用 RemovedCallback 来处理过期的缓存项,执行资源清理或日志记录等操作。
1CacheItemPolicy policy = new CacheItemPolicy
2{
3    RemovedCallback = args =>
4    {
5        Console.WriteLine($"缓存项 {args.CacheItem.Key} 被移除");
6    }
7};
8

定期清理

  • 可以通过 PollingInterval 来定期检查缓存并进行清理,避免缓存中存放过期数据。

缓存统计监控

1// 获取缓存统计信息
2var stats = ((MemoryCache)cache).GetCacheStatistics();
3
4Console.WriteLine($"缓存命中率: {stats.CacheHitRatio:P}");
5Console.WriteLine($"总缓存项: {stats.TotalCount}");
6Console.WriteLine($"缓存大小: {stats.CacheSizeKB} KB");
7Console.WriteLine($"内存限制: {stats.MemoryLimitMB} MB");
8

优缺点

优点

  • 高性能:进程内缓存,访问速度快。
  • 轻量简单:无外部依赖,易于集成。
  • 灵活过期:支持绝对、滑动和依赖失效。
  • 异步支持:GetOrCreateAsync 适合异步加载。
  • DI 集成:与 ASP.NET Core 无缝结合。
  • 内存管理:.NET 6+ 支持大小限制和压缩。

缺点

  • 进程内限制:数据不跨进程或实例共享,重启丢失。
  • 内存占用:大数据量可能导致内存压力。
  • 无分布式支持:不适合多实例部署(需用 Redis)。
  • 手动管理:需显式设置键和过期策略。
  • 有限功能:无高级功能(如标签、区域缓存)。

常见使用场景

适用场景

  • 数据缓存
    • Web 应用中缓存数据库查询结果、API 请求结果等,减少数据库查询的压力,提高响应速度。
    • 示例:缓存用户信息、商品列表等。
  • 会话存储
    • 使用缓存来存储用户会话信息,避免每次请求都查询数据库。
    • 示例:存储用户的登录状态或临时数据。
  • 频繁访问的数据
    • 在频繁读取但不常更新的数据上使用缓存来提高性能。
    • 示例:热点新闻、热销商品、排行榜等。
  • API 限流
    • 用于控制 API 调用频率,存储用户请求的时间戳,避免过多的请求对后台服务产生压力。
    • 示例:API 请求次数的缓存,防止暴力请求。
  • 耗时操作结果缓存
    • 对于计算成本较高的操作,可以将结果缓存起来,减少重复计算。
    • 示例:图片处理后的结果、文件读取操作的缓存等。

不适用场景

  • 分布式系统共享数据
  • 大型数据集(大于内存限制)
  • 需要持久化的数据
  • 严格的实时数据一致性要求

与相关技术对比

特性MemoryCacheHttpRuntime.CacheIDistributedCacheRedis
存储位置内存内存分布式存储分布式
应用范围通用通用分布式系统分布式
性能最高中高
持久化
过期策略丰富丰富基础丰富
集群支持
.NET Core

总结

MemoryCache.NET 中轻量高效的进程内缓存组件,适合高频读取、低更新频率的场景,如热点数据、配置或计算结果。它提供灵活的过期策略、回调和依赖失效,支持异步操作和 ASP.NET Core DI。相比 IDistributedCache(如 Redis),它性能更高但限于单实例。

资源和文档


C#.NET MemoryCache 深入解析:本地缓存机制与最佳实践》 是转载文章,点击查看原文


相关推荐


Agent ReAct and Loop
mCell2025/11/1

同步至个人站点:Agent ReAct and Loop Agent ReAct and Loop 我一直在使用 ChatGPT 或通义千问这样的 AI 工具,它们很强大,但多数情况下都是“一问一答”。我提一个问题,它给一个答案。 但我注意到,像 Manus 或 Claude Code CLI 这样的“Agent”(智能体)产品,它们似乎可以自动执行任务。你给它一个目标,它会自己去调用工具、分析结果、继续下一步,直到任务完成。 这到底是怎么做到的?它如何摆脱“一问一答”的限制,实现自动循环?


【STM32】看门狗
IT阳晨。2025/10/30

目录 一、独立看门狗1. IWDG简介2. IWDG工作原理及框图3. IWDG寄存器及函数介绍4. IWDG溢出时间计算5. IWDG配置步骤6. 小实验:独立看门狗喂狗实验 二、窗口看门狗1. WWDG简介2. WWDG工作原理及框图3. WWDG寄存器及函数介绍4. WWDG溢出时间计算5. WWDG配置步骤6. 小实验:窗口看门狗喂狗实验 一、独立看门狗 1. IWDG简介 独立看门狗(Independent Watchdog,通常缩写为IWDG)主要作用是主要用于检


C语言<<超全.超重要>>知识点总结
richxu202510012025/10/27

C语言<<超全.超重要>>知识点总结 #嵌入式[话题]# #电子信息[话题]# #单片机[话题]# #stm32[话题]# #校招[话题]# #秋招[话题]#


GitHub 热榜项目 - 日榜(2025-10-21)
CoderJia_2025/10/24

GitHub 热榜项目 - 日榜(2025-10-21) 生成于:2025-10-21 统计摘要 共发现热门项目:20 个 榜单类型:日榜 本期热点趋势总结 基于本期GitHub热榜项目,AI与机器学习生态系统呈现强劲发展势头,成为技术创新的核心驱动力。Claude cookbooks、系统提示词收集项目和开源NotebookLM实现等AI工具类项目表现突出,体现了大模型应用的快速普及和开发者对AI工程实践的强烈需求。同时,机器人学习框架LeRobot、OCR工具PaddleO


Python 的内置函数 callable
IMPYLH2025/10/22

Python 内建函数列表 > Python 的内置函数 callable def callable(obj): ''' 判断对象是否可调用 :param obj: 一个代对象 :return: 如果 obj 可以调用则返回 True,否则返回 False ''' Python 的内置函数 callable() 用于检查一个对象是否可以被调用(即该对象是否能像函数一样被调用)。该函数返回一个布尔值,如果对象是可调用的则返回 True,否则返回 Fa


第5部分:Netty性能优化与调优策略
lpfasd1232025/10/21

第5部分:Netty性能优化与调优策略 5.1 参数调优 线程数调优 1. EventLoopGroup线程数配置 public class ThreadOptimization { public void configureThreads() { // 获取CPU核心数 int cpuCores = Runtime.getRuntime().availableProcessors(); // Boss线程组:通常1个即可


如何将 iPhone 联系人同步到 Mac
Digitally2025/10/20

将 iPhone 联系人同步到 Mac 有许多好处。也许你换了新手机,想保留联系人列表,或者你只是想在使用 Mac 时访问 iPhone 联系人。听起来可能有些麻烦,但实际上同步联系人非常简单。使用苹果产品的一大好处是它们能够无缝协作。你的 iPhone 和 Mac 可以轻松共享数据,包括联系人。在这篇指南中,我们将向你介绍 6 种将 iPhone 联系人同步到 Mac 的简单方法。让我们开始吧! 方法 1:如何使用 iReaShare iPhone Manager 将 iPhone 联系


Java kubernetes
自由的疯2025/10/19

Kubernetes,通常简称为 K8s,是一个开源的容器编排平台,用于自动化容器化应用的部署、管理和扩展。Kubernetes 最初由 Google 的工程师设计开发,并于2014年开源,随后在2015年捐赠给云原生计算基金会(CNCF)。Kubernetes 的设计基于 Google 在内部使用的大规模容器管理系统 Borg 的经验和教训。 Kubernetes 的主要功能 自动化部署和回滚:Kubernetes 可以自动部署应用程序,并在检测到问题时自动回滚到之前的稳定版本。 自我修复:


基于springboot的民谣网站的设计与实现
舒克日记2025/10/17

作者主页:舒克日记 简介:Java领域优质创作者、Java项目、学习资料、技术互助 项目介绍 系统主要分为管理员角色和用户角色 本系统主要功能需求包括歌曲信息管理、歌曲留言管理等模块。其中,密码信息、用户信息、歌曲信息、歌曲留言信息等都是非常重要的数据记录,在系统设计的过程中,需要进行一定的加密处理,确保数据安全性,切实的保护好用户的重要信息。 环境要求 1.运行环境:最好是java jdk1.8,我们在这个平台上运行的。其他版本理论上也可以。 2.IDE环境:IDEA,Ec


Jmeter 线程组、定时器、监听器、后置处理器常用配置说明
wdlnancy2025/10/16

目录 1、线程组 1.1 多线程组关系 1.2、线程组的设置参数含义 ​编辑1.3 如何查看线程数是否全部启动成功? 2、定时器 2.1 常数吞吐量定时器 3、监听器 3.1 查看结果树 3.1.1 多个线程并发时,查看结果树频繁刷屏,如果有失败,无法停止后看到想看的失败信息,如何解决? ​编辑4.1.2 取样器结果里展示的字段都什么含义 3.2 聚合报告 3.2.1:聚合报告如何查看http请求的平均响应时间: 4、后置处理器 4.1 正则表达式提取器

首页编辑器站点地图

Copyright © 2025 聚合阅读

License: CC BY-SA 4.0