You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MemoryCache的PostEvictionCallback何时触发?过期被覆盖如何解决?

.NET MemoryCache 问题解答

回调触发时间判定

首先明确MemoryCache的两个基础逻辑:

  • 对同一个key调用Set方法时,会直接覆盖原有缓存条目,原有条目会立刻触发注册的PostEvictionCallback,驱逐原因为EvictionReason.Replaced。也就是说在13:35到13:54之间的每次IncrementValue调用,都会触发一次旧条目的UpdateDatabase回调。
  • 最后一次更新缓存的时间为13:54,本次Set指定的过期策略是AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(1),所以最后一条缓存的过期时间为13:54加1小时即14:54,该条目自然过期触发的UpdateDatabase回调时间为14:54,不会是14:45。

固定缓存过期时间的修复方案

对于每次调用Set会刷新过期时间的问题,有两种成熟的落地解决方案:

方案1:用可变引用类型包装值,避免重复调用Set(改造成本最低)

该方案不需要修改过期时间逻辑,仅调整缓存存储结构即可,全程仅首次初始化调用一次Set,不会修改原有过期时间,也不会触发重复覆盖导致的驱逐回调,如果不需要每次缓存更新都同步写库,仅需过期时一次性落库,该方案完全符合需求:

// 定义可变的计数包装类
public class CacheCounter
{
    public int Count { get; set; }
}

// 首次初始化缓存逻辑(仅key不存在时执行1次)
public void InitCounter(string key)
{
    var options = new MemoryCacheEntryOptions()
    {
        AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(1)
    };
    options.RegisterPostEvictionCallback(UpdateDatabase);
    _cache.Set(key, new CacheCounter { Count = 0 }, options);
}

// 自增逻辑无需调用Set,直接修改引用对象属性即可
public void IncrementValue(string key)
{
    // 可自行补充key不存在时的初始化兜底逻辑
    var counter = _cache.Get<CacheCounter>(key);
    counter.Count++;
}

// 调整UpdateDatabase的取值逻辑适配新的存储结构
private void UpdateDatabase(object key, object value, EvictionReason reason, object state)
{
    var counter = (CacheCounter)value;
    _repository.AddNew(key.ToString(), counter.Count);
}

方案2:缓存首次过期时间,每次更新时复用该时间

如果必须每次调用Set更新值,可以在首次创建缓存时记录固定的绝对过期时间,后续更新时复用该时间即可避免过期时间被刷新:

public void IncrementValue(string key)
{
    int currentCount = _cache.Get<int>(key);
    // 尝试读取已缓存的过期时间
    if (!_cache.TryGetValue($"{key}_expire", out DateTimeOffset expireTime))
    {
        // 首次创建时计算固定的绝对过期时间,同时缓存该时间
        expireTime = DateTimeOffset.Now.AddHours(1);
        _cache.Set($"{key}_expire", expireTime, new MemoryCacheEntryOptions { AbsoluteExpiration = expireTime });
    }

    var options = new MemoryCacheEntryOptions()
    {
        // 复用首次计算的绝对过期时间,不使用相对当前时间的过期策略
        AbsoluteExpiration = expireTime
    };
    options.RegisterPostEvictionCallback(UpdateDatabase);
    _cache.Set(key, currentCount + 1, options);
}

内容的提问来源于stack exchange,提问作者schweppes0x

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.28 01:54:02