一尘不染

堆栈溢出,Redis和缓存失效

redis

现在,Stack
Overflow使用redis,它们是否以相同的方式处理缓存失效?即散列到查询字符串+名称的身份列表(我想这个名称是某种用途或对象类型的名称)。

也许他们然后直接通过id(从一堆数据库索引中绕过,而是使用效率更高的聚集索引)直接从缓存中检索缺少的单个项。那会很聪明(杰夫提到的补液?)。

现在,我正在努力寻找一种简洁地解决所有问题的方法。在我自己进行初次切割之前,是否有任何此类示例可以用来澄清我的想法?

另外,我想知道在使用.net缓存(System.Runtime.Caching或System.Web.Caching)与退出并使用Redis之间的截止点在哪里。还是Redis放慢手脚了?

这是2009年的原始SO问题:

https://meta.stackexchange.com/questions/6435/how-does-stackoverflow-handle-
cache-invalidation

其他几个链接:

https://meta.stackexchange.com/questions/69164/does-stackoverflow-use-
caching-and-if-so-
how/69172#69172

https://meta.stackexchange.com/questions/110320/stack-overflow-db-
performance-and-redis-
cache


阅读 326

收藏
2020-06-20

共1个答案

一尘不染

老实说,我无法确定这是SO问题还是MSO问题,但是:

进入另一个系统 永远不会 比查询本地内存快(只要有密钥)。简单的答案:我们都使用!因此我们使用:

  • 本地记忆
  • 否则检查redis,并更新本地内存
  • 否则从源获取,并更新redis和本地内存

正如您所说,这会导致缓存失效的问题-尽管实际上这在大多数地方并不 重要 。但是,为此-redis事件(pub /
sub)提供了一种简便的方法来广播更改为所有节点的密钥,因此它们可以删除其本地副本-
意思是:下次需要时,我们将从redis中提取新副本。因此,我们广播了针对单个事件通道名称而更改的键名。

工具:在Ubuntu服务器上的Redis;BookSleeve作为Redis包装器;protobuf-
net和GZipStream(根据大小自动启用/禁用)以打包数据。

因此:redis pub / sub事件用于使给定密钥从 一个 节点(知道状态已更改的那个节点)的缓存立即(几乎)失效(到几乎 所有
节点)。

关于不同的进程(从注释中可以看出,“您是否对共享相同数据的多个不同的进程使用任何类型的共享内存模型?”):不,我们不这样做。每个Web层框只有真正承载一个过程(在任何给定层的),与多租户
内, 这一点,所以同样的过程中,我们可能有70个站点。由于遗留原因(即“有效且不需要修复”),我们主要使用带有站点标识的http缓存作为密钥的一部分。

对于系统中少量的大量数据密集型部分,我们具有持久存储到磁盘的机制,以便可以在Web自然回收(或重新部署)时在连续的应用程序域之间传递内存模型。与redis无关。

这是一个相关示例, 展示了其工作方式的 大致 含义-旋转以下内容的多个实例,然后在其中键入一些键名:

static class Program
{
    static void Main()
    {
        const string channelInvalidate = "cache/invalidate";
        using(var pub = new RedisConnection("127.0.0.1"))
        using(var sub = new RedisSubscriberConnection("127.0.0.1"))
        {
            pub.Open();
            sub.Open();

            sub.Subscribe(channelInvalidate, (channel, data) =>
            {
                string key = Encoding.UTF8.GetString(data);
                Console.WriteLine("Invalidated {0}", key);
            });
            Console.WriteLine(
                    "Enter a key to invalidate, or an empty line to exit");
            string line;
            do
            {
                line = Console.ReadLine();
                if(!string.IsNullOrEmpty(line))
                {
                    pub.Publish(channelInvalidate, line);
                }
            } while (!string.IsNullOrEmpty(line));
        }
    }
}

您应该看到的是,当您键入密钥名称时,该名称会立即显示在所有正在运行的实例中,然后实例将转储其密钥的本地副本。显然,在实际使用中,这两个连接需要放置在某个地方并保持打开状态,因此
不会 出现在using语句中。为此,我们使用了几乎一个单例。

2020-06-20