3

使用CacheManager,您将如何最好地实现这个场景,其中缓存实例包含数据,可能需要很长时间才能从慢速源获取?

我从不希望用户等待缓存填充(我不关心第一次加载)

我可以想到两种方法,但不确定是否可以使用 CacheManager:

“预定”刷新

  • 将缓存实例设置为 60 分钟后过期
  • 每 15 分钟,安排一些东西来刷新缓存实例

到期刷新

  • 当缓存实例过期时,触发一个刷新数据的事件。在数据刷新时(或者如果刷新失败),缓存实例仍然返回“陈旧”数据。

Cachemanager 在技术上是可行的,哪种方法最有效——如果有的话?

4

2 回答 2

2

关于您的第二个选项“到期时刷新”,这不起作用,因为如果项目过期,CacheManager 不会返回缓存值。而且,当你“刷新”缓存时,你最终会从其他调用者那里得到很多缓存未命中。

我的建议:

如果您只有少量的密钥缓存了 60 分钟并且它们都“工作相同”,只需启动一个后台任务,该任务与您的进程异步运行并每 15 分钟刷新一次缓存。

如果您有许多在过期方面可能有很大差异的密钥,您可以缓存 60 分钟过期的数据并存储一个 15 分钟过期的辅助密钥(没有实际值的假密钥)。然后,如果假密钥过期,刷新实际的键/值......至于key假的,例如使用前缀+实际密钥,然后例如监听OnRemove事件。

快速示例程序

internal class Program
{
    private static ICacheManager<object> _cache;

    private static void Main(string[] args)
    {
        _cache = CacheFactory.Build(c => c.WithDictionaryHandle());

        _cache.OnRemoveByHandle += Cache_OnRemoveByHandle;

        for (var i = 0; i < 10; i++)
        {
            _cache.Add("key" + i, "data" + i);
            AddFakeKey("key" + i);
            Thread.Sleep(1000);
        }

        Console.ReadKey();
    }

    private static void AddFakeKey(string forKey)
    {
        _cache.Add(new CacheItem<object>("trigger_" + forKey, "n/a", ExpirationMode.Absolute, TimeSpan.FromSeconds(1)));
    }

    private static void Cache_OnRemoveByHandle(object sender, CacheItemRemovedEventArgs e)
    {
        // Remark: you might get this event triggered for each level of the cache e.Level can be checked to react only on the lowest level...

        Console.WriteLine("OnRemoveByHandle " + e.ToString());
        if (e.Key.StartsWith("trigger_") && e.Reason == CacheItemRemovedReason.Expired)
        {
            var key = e.Key.Substring(8);
            Console.WriteLine("Updating key " + key);

            // updating the key
            _cache.Update(key, _ => "new value");

            // add a new fake key for another round
            AddFakeKey(key);
        }
    }
}

如果启用了键空间通知,这甚至适用于 Redis。

于 2017-12-03T10:21:46.903 回答
1

我没有安静地得到您的“计划”刷新想法,为什么将缓存设置为 60 分钟后过期并每 15 分钟刷新一次?

如果我是你,我会这样做(有点像你的第二个想法):

您第一次填充 CacheManager,然后每 60 分钟创建一个新的 CacheManager 实例(并填充它),如果一切顺利,您替换旧实例(您也将其处理掉)。

我想到了这一点,因为在 CacheManager 中,过期的项目不会被自动删除,所以它们仍然会在那里,如果你决定做类似 [Populate-ClearAll-Populate-..] 的事情,那么你可能会遇到一些问题,所以最安全的方法是在每次刷新时创建一个新实例,以确保您的缓存始终正常工作。

于 2017-12-02T09:50:04.177 回答