我实在无法确定这是个Stack Overflow问题还是Meta Stack Overflow问题,但是:
跑去另一个系统查询数据绝不会比查询本地内存更快(只要它是有键的);简单来说:我们两种都用!因此我们使用:
- 本地内存
- 否则检查Redis,并更新本地内存
- 否则从源获取,并更新Redis和本地内存
正如你所说,这就导致了缓存失效的问题 - 尽管在大多数地方这并不关键。但对于这个问题,Redis事件(发布/订阅)允许一种容易的方式将正在更改的键广播到所有节点,以便它们可以删除本地副本 - 这意味着:下次需要时,我们将从Redis中获取新副本。因此,我们针对一个单一事件通道名称广播正在更改的键名。
工具:Ubuntu服务器上的Redis;BookSleeve作为Redis包装器;protobuf-net和GZipStream(根据大小自动启用/禁用)用于打包数据。
因此:Redis发布/订阅事件用于立即(几乎)从一个节点(知道状态已更改的节点)向所有节点使给定键的缓存失效。
关于不同进程(来自评论,“您是否使用任何共享内存模型,以便多个不同的进程从相同的数据中提取?”):不,我们不这样做。每个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
语句中。我们使用了一个几乎是单例的对象来实现这一点。
RedisConnection
和RedisSubscriberConnection
类是什么?我需要添加哪个程序集才能访问这两个类? - Monojit Sarkar