首页 > 解决方案 > 在 Redis 缓存未命中时,获取数据只执行一次

问题描述

我们目前在 .NET Core 微服务中使用 LazyCache,但希望使用分布式缓存,因此我们正在寻找使用 Redis 的解决方案。

我正在研究缓存未命中情况。假设我们有 100 个请求(在同一台服务器上)进来,都需要获取相同的数据。我们有一个对数据库的后台调用,但它是一个复杂的查询,所以我们使用 LazyCache 缓存了结果。如果数据不在缓存中,则 LazyCache 会调用数据库以获取数据并锁定所有其他请求者,直到有响应为止。所以数据库调用只进行一次。

这是 LazyCache 相对于 Microsoft 的传统 MemoryCache 的优势。这篇文章很好地解释了这个问题: https ://blog.novanet.no/asp-net-core-memory-cache-is-get-or-create-thread-safe/

现在我们想对 Redis 做同样的事情,但我想知道这是否可能?我们使用 StackExchange.Redis 客户端。

实际上,我们想要这样做:

public async Task<string> GetOrAddAsync(string key, Func<Task<string>> dataFactory)
{
    var result = await cache.GetStringAsync(key);

    if(result != null)
    {
        return result;
    }

    result = await dataFactory();

    await cache.SetStringAsync(key, result);

    return result;
}

假设我们同时有 100 个请求。在一个线程调用 SetStringAsync 之前,所有其他线程都将调用 dataFactory(); 也可以获取数据。这可能是一个巨大的性能问题。我想看到的是,只有该键的第一个线程调用 dataFactory,但所有其他线程都被锁定在那里并等待第一个请求完成并设置 SetStringAsync。这与 LazyCache 中的行为相同。

这对我来说似乎是一个非常明显的场景,所以我很惊讶我在网上找不到任何关于这个的东西?有谁知道 StackExchange.Redis 客户端可以做到这一点,还是我必须自己实现这个锁定?

标签: multithreadingredisstackexchange.redismemorycachelazycache

解决方案


我想知道这是否可能?

一般不会。这对于内存缓存来说相对容易,但是分布式缓存需要分布式锁。然后可以放弃它,在这种情况下您必须定义某种恢复语义。

总之,这是一个相当大的问题需要解决,并且使用分布式锁,您会增加更多的性能问题(即,每个请求都必须协调分布式锁才能在缓存中插入/删除)。具有讽刺意味的是,如今分布式锁通常使用分布式缓存来实现。

因此,虽然您可以自己实现它,但我建议您在实际场景中进行一些认真的测试,以测试分布式锁定缓存的性能是更好还是更差。

无法预测测试会产生的结果,但我怀疑保留内存缓存会更快(您将dataFactory调用每个节点,而不是每个请求),或者可能是 2 级缓存(两个缓存被检查和更新,但只有内存中的一个被锁定)。


推荐阅读