本地缓存不能照搬Redis淘汰策略,因其受GC和堆内存约束,缺乏全局访问视图且易OOM;应设硬上限、用写驱动失效、分层管理TTL,并禁用noeviction。

本地缓存不能照搬 Redis 的淘汰策略
本地缓存(如 MemoryCache 或 Caffeine)和 Redis 的淘汰逻辑本质不同:前者运行在应用进程内,受 GC 和堆内存约束;后者是独立服务,依赖 maxmemory-policy 和近似 LRU/LFU 算法。直接把 allkeys-lru 套用到本地缓存上,会导致两个问题:一是本地缓存没有“访问时间戳全局视图”,expireAfterAccess 在高并发下可能误判冷热;二是本地缓存若未设 maximumSize 硬上限,容易触发 OutOfMemoryError,而 Redis 的 maxmemory 是强制生效的。
实操建议:
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
-
MemoryCache必须显式设置SizeLimit(.NET 6+)或通过MemoryCacheOptions配置最大条目数,避免无节制增长 - 不要依赖
expireAfterWrite单一机制保障一致性——它无法应对 Redis 主动更新后的脏数据窗口 - 对本地缓存只保留「稳定值」:
product_name、role_code这类字段,不缓存stock_count或user_last_login_time
Redis 淘汰策略选型要匹配本地缓存角色
本地缓存是“热点过滤器”,Redis 是“共享真相源”。这意味着 Redis 的淘汰策略必须为本地缓存留出协同空间,而不是各自为政。比如商品详情页场景中,本地缓存只存 sku_id → name 映射(TTL 1 小时),而 Redis 存完整 Product 对象(TTL 6 小时)。此时若 Redis 配 volatile-ttl,反而会提前淘汰掉本地缓存还依赖的 key,造成重复穿透。
实操建议:
- 对「本地缓存有副本」的数据,在 Redis 中统一设较长 TTL,并配
allkeys-lru或allkeys-lfu——优先保热 key,避免按 TTL 误伤 - 对「仅 Redis 承载」的动态数据(如秒杀库存),用
volatile-lru+ 短 TTL,与本地缓存完全隔离 - 禁用
noeviction:它会让写失败直接抛OOM command not allowed when used memory > 'maxmemory',破坏多级缓存的降级路径
写驱动失效比过期时间对齐更可靠
试图让本地缓存 expireAfterWrite(5min) 和 Redis EXPIRE product:123 300 严格对齐,是常见误区。网络延迟、时钟漂移、GC 暂停都会导致窗口错位:要么本地提前失效,频繁打 Redis;要么本地滞后,返回脏数据。真实生产环境里,靠时间同步永远追不上变化节奏。
实操建议:
- 所有 Redis 写操作后,立刻发一条
PUBLISH cache-invalidate "product:123"(注意带业务 key,不是泛化消息) - 每个应用节点订阅
cache-invalidate频道,收到即调用_memoryCache.Remove("product:123") - 避免用
KEYS product:*扫描清理——该命令在 Redis 7.2+ 默认禁用,且 O(n) 复杂度会拖垮实例
缓存穿透/雪崩时本地缓存反而会放大压力
当大量请求击穿本地缓存后,若 Redis 也因淘汰策略激进(如 volatile-random 配短 TTL)而集体失效,所有请求会瞬间涌向数据库。此时本地缓存不仅没起缓冲作用,反而因未命中率飙升,加剧了线程竞争和连接池耗尽。
关键细节:
- 本地缓存应配置
GetOrCreateAsync+lock语义(.NET)或 Caffeine 的refreshAfterWrite,防止缓存击穿时的并发回源 - Redis 的
maxmemory必须设合理值(建议为机器内存的 40%~60%),并配合allkeys-lfu保高频 key,而非盲目用volatile-ttl - 真正危险的是「本地缓存未设上限 + Redis 淘汰策略太激进 + 无熔断兜底」三者叠加,这种情况在秒杀预热阶段最容易暴露
svc-order:cache-invalidate),否则跨服务消息污染会导致不可预测的缓存抖动**。

















