Шаблон программирования отдельно от кэша
Загрузите данные по запросу из хранилища данных в кэш. Этот шаблон может повысить производительность, а также помочь поддерживать согласованность между данными, хранящимися в кэше и в хранилище данных.
Контекст и проблема
Приложения используют кеш для улучшения повторного доступа к информации, содержащейся в хранилище данных. Однако это нереально ожидать, что кэшированные данные всегда будут согласованы с хранилищем данных. Приложения должны реализовать стратегию, которая помогает обеспечить, чтобы данные в кэше были как можно up-to-date. Стратегия также должна быть в состоянии определить, когда кэшированные данные становятся устаревшими и обрабатывать их соответствующим образом.
Решение
Многие коммерческие системы кэширования предоставляют операции сквозного чтения, сквозной записи или записи с задержкой. В этих системах приложение получает данные, ссылаясь на кэш. Если данные не хранятся в кэше, приложение извлекает его из хранилища данных и добавляет его в кэш. Все изменения данных, хранящиеся в кэше, также автоматически записываются обратно в хранилище данных.
Если в кэше не предоставляется эта функция, это нужно сделать в приложениях, которые используют кэш для хранения данных.
Приложение может эмулировать функциональные возможности сквозного чтения кэширования путем реализации стратегии "кэш на стороне". Эта стратегия загружает данные в кэш по запросу. На рисунке показано использование шаблона "кэш на стороне" для хранения данных в кэше.
- Приложение определяет, хранится ли элемент в кэше, пытаясь прочитать его из кэша.
- Если элемент не находится в кэше (отсутствует кэш), приложение извлекает элемент из хранилища данных.
- Приложение добавляет элемент в кэш, а затем возвращает его вызывающему объекту.
Если приложение обновляет сведения, оно может следовать стратегии сквозной записи путем внесения изменений в хранилище данных и объявления соответствующего элемента в кеше недопустимым.
Когда элемент понадобится еще раз, стратегия в стороне кэша извлекает обновленные данные из хранилища данных и добавляет его в кэш.
Проблемы и рекомендации
При принятии решения о реализации этого шаблона необходимо учитывать следующие моменты.
Время существования кэшированных данных. Многие кэши используют политику истечения срока действия для отмены данных и удаления их из кэша, если доступ к нему не выполняется в течение заданного периода. Чтобы стратегия "кэш на стороне" была эффективной, убедитесь, что политика срока действия соответствует шаблону доступа к приложениям, использующим данные. Не делайте срок действия слишком коротким, так как преждевременное истечение срока действия может привести к постоянному получению данных из хранилища данных и добавлению его в кэш. Однако не устанавливайте и слишком длительный срок,чтобы кэшированные данные не устарели. Помните, что кэширование является наиболее эффективным для относительно статических или часто читаемых данных.
Исключение данных. Большинство кэшей имеют ограниченный размер по сравнению с хранилищем данных, в котором возникают данные. Если кэш превышает его размер, он вытесняет данные. Большинство кэшей принимают политику, которая используется в последнее время для выбора элементов для вытеснения, но она может быть настраиваемой.
Конфигурация. Конфигурацию кэша можно задать как глобально, так и для каждого кэшированного элемента. Одна глобальная политика вытеснения может не соответствовать всем элементам. Конфигурация элемента кэша может быть подходящей, если элемент является дорогостоящим для извлечения. В этой ситуации имеет смысл сохранить элемент в кэше, даже если он получает доступ реже, чем дешевые элементы.
Подготовка кэша. Многие решения предварительно заполняют кэш данными, которые могут потребоваться приложению в процессе обработки запуска. Шаблон "Кэш на стороне" по-прежнему можно использовать, если срок действия некоторых из этих данных истекает или данные исключаются.
Согласованность. Реализация шаблона "Кэш на стороне" не гарантирует согласованность между хранилищем данных и кэшем. Например, внешний процесс может изменять элемент в хранилище данных в любое время. Это изменение не отображается в кэше до тех пор, пока элемент снова не загружается. В системе, которая реплицирует данные в хранилищах данных, согласованность может быть сложной при частой синхронизации.
Локальное (выполняющееся в памяти) кэширование. Кэш может быть локальным для экземпляра приложения и хранящимся в памяти. Кэш на стороне можно использовать в этой среде, если приложение многократно получает доступ к одним и тем же данным. Тем не менее локальный кэш является закрытым, и поэтому разные экземпляры приложения могут иметь копию одних и тех же кэшированных данных. Эти данные могут быстро стать несогласованными между кэшами, из-за чего может возникнуть необходимость завершения срока хранения данных, которые находятся в частном кэше, и обновлять их чаще. В таких случаях рекомендуется изучить использование механизма общего или распределенного кэширования.
Семантическое кэширование. Некоторые рабочие нагрузки могут воспользоваться получением кэша на основе семантического значения, а не точных ключей. Это сокращает количество запросов и маркеров, отправленных в языковые модели. Убедитесь, что кэшированные данные получают преимущества от семантической эквивалентности и не рискуют возвращать несвязанные ответы или содержать частные и конфиденциальные данные. Например, "Что такое моя годовая заработная плата?" семантика похожа на "Что такое моя ежегодная оплата домой?", но если спросили двух разных пользователей, так что ответ не должен быть одинаковым, и вы хотите включить эти конфиденциальные данные в кэш.
Когда следует использовать этот шаблон
Используйте этот шаблон в следующих случаях:
- В кэше не предоставляются собственные операции сквозного чтения и записи.
- Запрос ресурса невозможно спрогнозировать. Этот шаблон позволяет приложениям загружать данные по запросу. Это не делает предположений о том, какие данные приложения требуются заранее.
Этот шаблон будет неприменим в следующих случаях:
- Если данные чувствительны или связаны с безопасностью. Это может быть неуместным для хранения в кэше, особенно если кэш используется для нескольких приложений или пользователей. Всегда перейдите к основному источнику данных.
- Если кэшированный набор данных является статическим. Если данные помещаются в доступное пространство кэша, примените кэш с данными при запуске и примените политику, которая предотвращает истечение срока действия данных.
- Если большинство запросов не столкнулись с попаданием в кэш. В этой ситуации затраты на проверку кэша и загрузку данных в нее могут перевесить преимущества кэширования.
- При кэшировании сведений о состоянии сеанса в веб-приложении, размещенном в веб-ферме. В этой среде следует избегать зависимостей, основанных на сходстве между клиентом и сервером.
Проектирование рабочей нагрузки
Архитектор должен оценить, как можно использовать шаблон Cache-Aside в проектировании для решения целей и принципов, описанных в основных принципах платформы Azure Well-Architected Framework. Например:
Принцип | Как этот шаблон поддерживает цели основных компонентов |
---|---|
Решения по проектированию надежности помогают рабочей нагрузке стать устойчивой к сбоям и обеспечить восстановление до полнофункционального состояния после сбоя. | Кэширование создает репликацию данных и может использоваться для сохранения доступности часто доступных данных, если исходное хранилище данных временно недоступно. Кроме того, если в кэше возникла ошибка, рабочая нагрузка может вернуться к хранилищу данных источника. - ИЗБЫТОЧНОСТЬ RE:05 |
Эффективность производительности помогает рабочей нагрузке эффективно соответствовать требованиям путем оптимизации масштабирования, данных, кода. | Использование кабины кэша повышает производительность для данных с большим объемом чтения, которые редко изменяются и могут терпеть некоторое устаревание. - Производительность данных PE:08 - Оптимизация непрерывной производительности PE:12 |
Как и любое решение по проектированию, рассмотрите любые компромиссы по целям других столпов, которые могут быть представлены с этим шаблоном.
Пример
Рекомендуется использовать Управляемый Redis Azure для создания распределенного кэша, который может совместно использовать несколько экземпляров приложений.
В следующем примере кода используется клиент StackExchange.Redis, который является клиентской библиотекой Redis , написанной для .NET. Чтобы подключиться к экземпляру Управляемого Redis в Azure, вызовите статический ConnectionMultiplexer.Connect
метод и передайте строку подключения. Этот метод возвращает ConnectionMultiplexer
, представляющий подключение. Один из способов совместного использования экземпляра ConnectionMultiplexer
в приложении предполагает наличие статического свойства, которое возвращает подключенный экземпляр (как в приведенном ниже примере). Этот подход помогает потокобезопасно инициализировать только отдельный подключенный экземпляр.
private static ConnectionMultiplexer Connection;
// Redis connection string information
private static Lazy<ConnectionMultiplexer> lazyConnection = new Lazy<ConnectionMultiplexer>(() =>
{
string cacheConnection = ConfigurationManager.AppSettings["CacheConnection"].ToString();
return ConnectionMultiplexer.Connect(cacheConnection);
});
public static ConnectionMultiplexer Connection => lazyConnection.Value;
В методе GetMyEntityAsync
в следующем примере кода показана реализация шаблона "кэш на стороне". Этот метод извлекает объект из кэша, используя подход сквозного чтения.
Объект определяется с помощью целочисленного идентификатора в качестве ключа. Метод GetMyEntityAsync
пытается извлечь из кэша элемент с этим ключом. Если найден соответствующий элемент, кэш возвращает его. Если в кэше нет соответствия, метод GetMyEntityAsync
извлекает объект из хранилища данных, добавляет его в кэш, а затем возвращает его. Код, считывающий данные из хранилища данных, не отображается здесь, так как он зависит от хранилища данных. Кэшированный элемент настроен на истечение срока действия, чтобы предотвратить его устареть, если другая служба или процесс его обновляет.
// Set five minute expiration as a default
private const double DefaultExpirationTimeInMinutes = 5.0;
public async Task<MyEntity> GetMyEntityAsync(int id)
{
// Define a unique key for this method and its parameters.
var key = $"MyEntity:{id}";
var cache = Connection.GetDatabase();
// Try to get the entity from the cache.
var json = await cache.StringGetAsync(key).ConfigureAwait(false);
var value = string.IsNullOrWhiteSpace(json)
? default(MyEntity)
: JsonConvert.DeserializeObject<MyEntity>(json);
if (value == null) // Cache miss
{
// If there's a cache miss, get the entity from the original store and cache it.
// Code has been omitted because it is data store dependent.
value = ...;
// Avoid caching a null value.
if (value != null)
{
// Put the item in the cache with a custom expiration time that
// depends on how critical it is to have stale data.
await cache.StringSetAsync(key, JsonConvert.SerializeObject(value)).ConfigureAwait(false);
await cache.KeyExpireAsync(key, TimeSpan.FromMinutes(DefaultExpirationTimeInMinutes)).ConfigureAwait(false);
}
}
return value;
}
В примерах используется Управляемый Redis Azure для доступа к хранилищу и получения сведений из кэша. Дополнительные сведения см. в статье "Создание управляемого Redis Azure " и использование Azure Redis в .NET Core.
В UpdateEntityAsync
приведенном ниже методе показано, как недопустимый объект в кэше при изменении значения приложения. Этот код обновляет хранилище исходных данных, а затем удаляет кэшированный элемент из кэша.
public async Task UpdateEntityAsync(MyEntity entity)
{
// Update the object in the original data store.
await this.store.UpdateEntityAsync(entity).ConfigureAwait(false);
// Invalidate the current cache object.
var cache = Connection.GetDatabase();
var id = entity.Id;
var key = $"MyEntity:{id}"; // The key for the cached object.
await cache.KeyDeleteAsync(key).ConfigureAwait(false); // Delete this key from the cache.
}
Примечание.
Порядок шагов очень важен. Обновите хранилище данных, прежде чем удалять элемент из кэша. Если сначала удалить кэшированный элемент, то есть небольшое окно времени, когда клиент может получить элемент перед обновлением хранилища данных. В этой ситуации получение приводит к промаху кэша (так как элемент был удален из кэша). Пропуск кэша приводит к тому, что более ранняя версия элемента будет получена из хранилища данных и добавлена обратно в кэш. Результатом являются устаревшие данные кэша.
Связанные ресурсы
При реализации этого шаблона могут быть важны следующие сведения:
Шаблон надежного веб-приложения показывает, как применить шаблон кэша в сторону к веб-приложениям, конвергентным в облаке.
Caching (Кэширование). Здесь предоставлены дополнительные сведения о том, как можно кэшировать данные в облачном решении, а также показаны проблемы, которые следует учитывать при реализации кэширования.
Data Consistency Primer (Руководство по обеспечению согласованности данных). Облачные приложения обычно хранят данные в нескольких хранилищах данных и расположениях. Управление согласованности данных и обеспечение согласованности данных в этой среде является критически важным аспектом системы, особенно с параллелизмом и проблемами доступности, которые могут возникнуть. В этом руководстве описаны проблемы согласованности в распределенных данных и способы реализации окончательной согласованности в приложении для обеспечения доступности данных.
Используйте Управляемый Redis Azure в качестве семантического кэша. В этом руководстве показано, как реализовать семантический кэширование с помощью Управляемого Redis в Azure.