memorycache需配置sizelimit和过期策略,避免内存泄漏;redis序列化须显式配置jsonserializeroptions防异常;混合使用时须保证失效同步,防御穿透雪崩需空值缓存+随机key+本地加载锁。

MemoryCache 用法和常见误用
直接 new MemoryCache(new MemoryCacheOptions()) 就能用,但多数人没配过期策略或大小限制,导致内存悄悄涨满。
-
MemoryCache是进程内缓存,重启就丢,适合临时、轻量、单机场景 - 必须设置
SizeLimit和Size(比如用SetSize(1)),否则Remove不会触发自动淘汰 - 过期时间分两种:
AbsoluteExpirationRelativeToNow(绝对时间)和SlidingExpiration(访问后重置),别混用;SlidingExpiration在高频读场景下可能让数据永远不淘汰 - 缓存值如果是引用类型,别直接返回
cache.Get<t>("key")</t>后修改内部字段——Get返回的是原对象引用,改了会影响后续所有读取
Redis 缓存写入时序列化踩坑点
用 StackExchange.Redis + System.Text.Json 默认序列化,很容易遇到 null 引用或类型不匹配错误。
- 默认
JsonSerializerOptions不处理循环引用,实体类有导航属性时直接Serialize会抛System.Text.Json.JsonException - 缓存
DateTime时注意时区:.NET 6+ 默认序列化为 ISO 8601 带偏移,但老客户端可能只认"yyyy-MM-ddTHH:mm:ss"格式,反序列化失败 - 别把
IDictionary<string object></string>这种弱类型直接塞进 Redis——object会被序列化成null或空对象,读出来是null而不是预期字典 - 推荐显式指定
JsonSerializerOptions:new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull }
MemoryCache 和 Redis 混合使用的典型模式
本地缓存 + 分布式缓存组合不是简单“先查本地再查 Redis”,关键在失效同步和并发写控制。
- 写操作必须走“先删 Redis,再删 MemoryCache,最后写 DB”,否则本地缓存可能长期脏读
- 读操作建议用“双检锁”:
Get本地缓存 → 空则加锁 → 再查本地 → 还空才查 Redis → 写回本地 + 设置短滑动过期(比如 10 秒) - 不要给 MemoryCache 设置比 Redis 更长的过期时间,否则 Redis 已失效,本地还挂着旧值
- 用
MemoryCacheEntryOptions.PostEvictionCallbacks监听本地缓存被删事件,主动触发 Redis 清理(仅限需要强一致的少数 key)
缓存穿透和雪崩的实际防御手段
缓存穿透不是加个空值就能解决,雪崩也不是统一过期时间改随机就行。
- 空值缓存必须带过期时间(比如 2 分钟),且用
string.Empty或专用占位对象(如NullUser.Instance),不能缓存null - 热点 key 雪崩:对 Redis 中的 key 加随机后缀(如
"user:123:cache_v2"),更新时原子切换 key 名,避免全量失效 - MemoryCache 本身不支持分布式锁,做本地防击穿得用
ConcurrentDictionary<string task>></string>缓存正在加载的 Task,避免重复查询 DB - Redis 连接泄漏比缓存逻辑更致命:StackExchange.Redis 的
IDatabase是线程安全的,别每次请求都GetDatabase(),复用单例实例











