.NET Core 2.2下IMemoryCache在服务器端无法命中、反复触发SQL查询的问题排查
.NET Core 2.2 下IMemoryCache服务器端失效的常见原因
核心前提:
IMemoryCache是进程内缓存,缓存数据存储在当前应用进程的私有内存空间,不跨进程、不跨服务器、不跨应用实例共享
高概率直接触发问题的原因
多进程/多实例部署导致缓存隔离
如果服务器端部署时存在以下配置,会直接导致缓存看似完全失效:- IIS应用程序池开启Web Garden(最大工作进程数>1),多个
w3wp.exe进程并行处理请求,每个进程的内存空间完全隔离,各自维护独立的MemoryCache实例。请求被负载分发到不同进程时,会因为其他进程没有缓存值直接触发数据库查询 - 多服务器负载均衡、容器化多Pod/多实例部署时,每个实例的进程内存独立,单实例写入的缓存对其他实例不可见
这类场景下本地单进程调试运行时不会触发,部署后因为多实例分流,从数据库监控视角就会看到SQL被反复执行,表现为缓存完全不生效
- IIS应用程序池开启Web Garden(最大工作进程数>1),多个
应用进程频繁重启/回收导致缓存清空
IMemoryCache的生命周期完全依附于当前应用进程,进程销毁后所有缓存值会被清空。如果服务器端存在以下配置,会导致缓存反复丢失:- IIS应用程序池配置了空闲超时(默认20分钟无请求即回收进程),如果站点访问量低、请求间隔大于空闲超时时间,每次请求到达时进程已经被回收,缓存早已清空,会直接触发查库
- 应用程序池配置了固定时间间隔回收、内存阈值触发回收,或程序未捕获的异常导致进程崩溃重启,都会清空所有缓存值
本地Debug调试时进程会一直保持运行,不会自动回收,因此能正常命中缓存
依赖注入配置错误导致缓存实例非单例
.NET Core 内置的AddMemoryCache()方法默认会将IMemoryCache注册为单例服务。如果服务器端的依赖注入配置被修改为非单例生命周期,例如:// 错误的注册方式,会导致每次解析拿到新的缓存实例 services.AddScoped<IMemoryCache, MemoryCache>(); services.AddTransient<IMemoryCache, MemoryCache>();跨请求时每次解析得到的都是全新的MemoryCache对象,之前写入的缓存值完全无法被后续请求读取,表现为缓存完全不生效。本地Debug时因为请求量小、服务解析频率低,可能短时间内复用同一个实例不会暴露问题。
代码实现层面的潜在隐患
- 当前实现中使用
new Task(() => {...}).Start()实现的后台刷新逻辑是未被宿主托管的裸任务:- 这类任务不受ASP.NET Core生命周期管理,请求结束后可能被线程池提前终止,或在进程回收时直接丢失,导致缓存无法按时刷新
- 任务内部使用
.Result同步等待异步请求,存在线程阻塞、死锁的风险,一旦抛异常会被静默吞掉,导致缓存刷新失败
- 异常处理逻辑缺失:当前代码只写了try块未展示catch分支的处理逻辑,如果写缓存、拉取数据的过程中抛出异常,异常被静默捕获后会导致缓存值永远无法写入,后续每次请求都会触发查库
内容的提问来源于stack exchange,提问作者tknkrtl
相关产品推荐
相关产品推荐

