ASP.NET Core 中必须通过 AddMemoryCache() 注册并注入 IMemoryCache;高并发下唯一防击穿方式是 GetOrCreate();需显式调用 SetSize() 配合 SizeLimit 控制内存;SlidingExpiration 与 AbsoluteExpirationRelativeToNow 不可共存;缓存对象须线程安全。

别用 MemoryCache.Default 或 new MemoryCache()
直接用 MemoryCache.Default(来自 System.Runtime.Caching)或手动 new MemoryCache(new MemoryCacheOptions()),在 ASP.NET Core 项目里等于放弃内存压力响应、统一过期调度和 DI 生命周期管理。这两个写法在高并发或内存紧张时会表现异常:缓存不自动 compact、多个实例间清理逻辑割裂、GC 压力飙升后无预警 OOM。
正确路径只有一条:ASP.NET Core 6+ 必须在 Program.cs 中调用 builder.Services.AddMemoryCache(),然后通过构造函数注入 IMemoryCache。单元测试中若真要手动实例化,必须显式传入带 SizeLimit 和 CompactionPercentage 的 MemoryCacheOptions。
GetOrCreate() 是防击穿的唯一可靠方式
写成 if (!_cache.TryGetValue(...)) { _cache.Set(...) } 看似自然,但高并发下必然触发多次重复加载——比如 10 个请求同时发现 key 缺失,就会并行执行 10 次数据库查询。
GetOrCreate() 把“查 + 存”原子化,内部加锁确保 factory 只执行一次:
var user = _cache.GetOrCreate("user:123", entry => {
entry.SlidingExpiration = TimeSpan.FromMinutes(5);
return LoadFromDb(123); // 这里抛异常,缓存不会写入,调用方直接收到 exception
});- 过期策略必须在 factory 委托内的
entry上设置,外部设置无效 - 如需缓存“查不到”的结果(比如空对象或错误兜底),得在 factory 内
try/catch后返回 - 不要混用
Set()和GetOrCreate()操作同一 key,语义冲突易出错
SizeLimit 不是开关,SetSize() 才是计费单位
只在 DI 注册时设 options.SizeLimit = 100 * 1024 * 1024(约 100MB),对大对象完全无效。所有未调用 SetSize() 的缓存项默认按大小 1 计算——存一个 15MB 的 byte[],缓存系统却只当它占 1 单位,结果就是内存悄悄涨满、GC 频繁、服务卡顿。
必须双配合:
- 注册时设
SizeLimit(估算字节数,非精确值) - 每次
Set()或GetOrCreate()时,对大对象显式调用.SetSize(15 * 1024 * 1024) - 小对象建议统一
.SetSize(1),避免 size 统计失真
漏掉 SetSize() 的对象,哪怕只存一个,也会让整个缓存的容量控制形同虚设。
SlidingExpiration 和 AbsoluteExpirationRelativeToNow 不能共存
运行时报错:ArgumentException: Both sliding and absolute expiration cannot be set。这不是警告,是硬性限制。
选哪个取决于场景:
- 滑动过期适合活跃数据(如用户 session),只要持续访问就一直续命
- 绝对过期适合定时刷新场景(如每小时拉一次配置),到点就清,不管有没有人碰
- 二者二选一;若需“最长存活时间 + 活跃续命”,只能用滑动过期 + 外部定时任务主动
Remove()
另外注意:AbsoluteExpiration 要求传 DateTimeOffset,而 AbsoluteExpirationRelativeToNow 接受 TimeSpan,别传错类型。
最常被忽略的是缓存对象本身的线程安全性——存一个 List<t></t> 进去,外部代码直接 Add(),所有读取方看到的就是被改过的状态。要么存不可变对象,要么深拷贝后再存。


















