Ответ 1
Я честно не могу решить, является ли это вопросом SO или вопросом MSO, но:
Переход к другой системе никогда не быстрее, чем запрос к локальной памяти (при условии, что она активирована); простой ответ: мы используем оба! Поэтому мы используем:
- локальная память
- else проверить redis и обновить локальную память
- else извлечь из источника и обновить redis и локальную память
Это, как вы говорите, вызывает проблему недействительности кеша - хотя на самом деле это не критично в большинстве мест. Но для этого - события redis (pub/sub) позволяют легко передавать широковещательные ключи, которые меняются на все узлы, поэтому они могут отбрасывать свою локальную копию - значение: в следующий раз, когда это необходимо, мы возьмем новую копию из redis, Следовательно, мы передаем имена ключей, которые изменяются в отношении одного имени канала события.
Инструменты: redis на сервере ubuntu; BookSleeve как оболочка redis; protobuf-net и GZipStream (включены/отключены автоматически в зависимости от размера) для упаковки данных.
Итак: события redis pub/sub используются для отмены кэша для заданного ключа от одного node (тот, который знает, что состояние изменилось) сразу (в значительной степени) до все.
Что касается отдельных процессов (из комментариев), вы используете какую-либо модель разделяемой памяти для нескольких разных процессов, загружающих одни и те же данные?): Нет, мы этого не делаем. В каждом ящике веб-уровня действительно есть только один процесс (любого уровня), в котором есть многоуровневая аренда, поэтому внутри одного процесса у нас может быть 70 сайтов. По причинам, связанным с наследием (например, "это работает и не требует исправления" ), мы в первую очередь используем кеш http с идентификатором сайта как частью ключа.
Для нескольких массивно важных для данных частей системы у нас есть механизмы для сохранения на диске, чтобы модель памяти могла быть передана между последовательными доменами приложения, так как веб, естественно, перерабатывает (или повторно развертывается) но это не связано с 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
. Для этого мы используем почти-одиночный.