Шаблон программирования отдельно от кэша

Кэш Azure для Redis

Загрузите данные по запросу из хранилища данных в кэш. Этот шаблон может повысить производительность, а также помочь поддерживать согласованность между данными, хранящимися в кэше и в хранилище данных.

Контекст и проблема

Приложения используют кеш для улучшения повторного доступа к информации, содержащейся в хранилище данных. Тем не менее нецелесообразно ожидать, что кэшированные данные всегда будут полностью согласованы с данными в хранилище данных. Приложения должны реализовать стратегию, которая позволяет убедиться, что данные в кэше максимально актуальны, а также определить и обработать ситуации, возникающие, когда данные в кэше устарели.

Решение

Многие коммерческие системы кэширования предоставляют операции сквозного чтения, сквозной записи или записи с задержкой. В этих системах приложение получает данные, ссылаясь на кэш. Если данные находятся не в кэше, они извлекаются из хранилища данных и добавляются в кэш. Все изменения данных, хранящиеся в кэше, также автоматически записываются обратно в хранилище данных.

Если в кэше не предоставляется эта функция, это нужно сделать в приложениях, которые используют кэш для хранения данных.

Приложение может эмулировать функциональные возможности сквозного чтения кэширования путем реализации стратегии "кэш на стороне". Эта стратегия загружает данные в кэш по запросу. На рисунке показано использование шаблона "кэш на стороне" для хранения данных в кэше.

Использование шаблона

Если приложение обновляет сведения, оно может следовать стратегии сквозной записи путем внесения изменений в хранилище данных и объявления соответствующего элемента в кеше недопустимым.

Если далее требуется элемент, который использует стратегию "кэш на стороне", это приведет к извлечению обновленных данных из хранилища данных и добавления их обратно в кэш.

Проблемы и рекомендации

При принятии решения о реализации этого шаблона необходимо учитывать следующие моменты.

Время существования кэшированных данных. Многие кэши реализуют политику срока действия, которая объявляет данные недействительными и удаляет их из кэша, если они недоступны в течение определенного времени. Чтобы стратегия "кэш на стороне" была эффективной, убедитесь, что политика срока действия соответствует шаблону доступа к приложениям, использующим данные. Не устанавливайте слишком короткий срок, так как это может привести к тому, что приложения будут получать данные из хранилища данных и добавлять их в кэш постоянно. Однако не устанавливайте и слишком длительный срок,чтобы кэшированные данные не устарели. Помните, что кэширование является наиболее эффективным для относительно статических или часто читаемых данных.

Исключение данных. Большинство кэшей имеют ограниченный размер в сравнении с исходным хранилищем данных. При необходимости они будут исключать данные. Большинство кэшей применяют политику "наиболее давно использовавшийся" для выбора исключаемых элементов, но ее можно изменить. Чтобы убедиться, что кэш является экономичным, настройте свойства глобального срока действия и другие свойства кэша, а также свойство срока действия каждого кэшированного элемента. Не всегда целесообразно применять политику глобального исключения для каждого элемента кэша. Например, если извлечение кэшированного элемента из хранилища данных является затратным процессом, будет разумно хранить этот элемент в кеше за счет более часто используемых, но менее затратных элементов.

Подготовка кэша. Многие решения предварительно заполняют кэш данными, которые могут потребоваться приложению в процессе обработки запуска. Шаблон "Кэш на стороне" по-прежнему можно использовать, если срок действия некоторых из этих данных истекает или данные исключаются.

Согласованность. Реализация шаблона "Кэш на стороне" не гарантирует согласованность между хранилищем данных и кэшем. Элемент в хранилище данных можно изменить в любое время, используя внешний процесс, и эти изменения могут не отражаться в кэше до следующей загрузки элемента. В системе, реплицирующей данные в хранилищах данных, эта проблема может стать серьезной, если синхронизация выполняется часто.

Локальное (выполняющееся в памяти) кэширование. Кэш может быть локальным для экземпляра приложения и хранящимся в памяти. Кэш на стороне можно использовать в этой среде, если приложение многократно получает доступ к одним и тем же данным. Тем не менее локальный кэш является закрытым, и поэтому разные экземпляры приложения могут иметь копию одних и тех же кэшированных данных. Эти данные могут быстро стать несогласованными между кэшами, из-за чего может возникнуть необходимость завершения срока хранения данных, которые находятся в частном кэше, и обновлять их чаще. В таких случаях рекомендуется изучить использование механизма общего или распределенного кэширования.

Когда следует использовать этот шаблон

Используйте этот шаблон в следующих случаях:

  • В кэше не предоставляются собственные операции сквозного чтения и записи.
  • Запрос ресурса невозможно спрогнозировать. Этот шаблон позволяет приложениям загружать данные по запросу. Он не делает предположений о том, какие данные потребуются приложению.

Этот шаблон будет неприменим в следующих случаях:

  • Если кэшированный набор данных является статическим. Если данные будут соответствовать свободному месту в кэше, заполняя кэш данными во время запуска и применяя политику, которая предотвращает истечение срока действия данных.
  • Для кэширования данных о состоянии сеанса в веб-приложении, размещенном на веб-ферме. В этой среде следует избегать зависимостей, основанных на сходстве между клиентом и сервером.

Проектирование рабочей нагрузки

Архитектор должен оценить, как шаблон cache-Aside можно использовать в проектировании рабочей нагрузки для решения целей и принципов, описанных в основных принципах Платформы Azure Well-Architected Framework. Например:

Принцип Как этот шаблон поддерживает цели основных компонентов
Решения по проектированию надежности помогают рабочей нагрузке стать устойчивой к сбоям и обеспечить восстановление до полнофункционального состояния после сбоя. Кэширование создает данные реплика и с ограниченными возможностями можно использовать для сохранения доступности часто доступных данных, если исходное хранилище данных временно недоступно. Кроме того, если в кэше возникла ошибка, рабочая нагрузка может вернуться к хранилищу данных источника.

- ИЗБЫТОЧНОСТЬ RE:05
Эффективность производительности помогает рабочей нагрузке эффективно соответствовать требованиям путем оптимизации масштабирования, данных, кода. Более высокую производительность рабочей нагрузки можно получить при использовании кэша для данных с большим объемом чтения, которые не изменяются часто, и ваша рабочая нагрузка предназначена для того, чтобы обеспечить определенный объем устаревших данных.

- Производительность данных PE:08
- Оптимизация непрерывной производительности PE:12

Как и любое решение по проектированию, рассмотрите любые компромиссы по целям других столпов, которые могут быть представлены с этим шаблоном.

Пример

В Microsoft Azure можно использовать Кэш Azure для Redis для создания распределенного кэша, который может использоваться несколькими экземплярами приложения.

В следующем примере кода используется клиент StackExchange.Redis, который является клиентской библиотекой Redis , написанной для .NET. Чтобы подключиться к экземпляру Кэш Azure для Redis, вызовите статический 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;
}

В примерах используется Кэш Azure для Redis для доступа к хранилищу и получения информации из кэша. Дополнительные сведения см. в статье "Использование Кэш Azure для Redis" и "Создание веб-приложения с помощью Кэш Azure для Redis".

В методе 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 (Руководство по обеспечению согласованности данных). Облачные приложения обычно используют данные, которые распределены по хранилищам данных. Управление и поддержание согласованности данных в этой среде — это важнейший аспект системы, в частности проблемы параллелизма и доступности, которые могут возникнуть. В этом руководстве описаны проблемы согласованности в распределенных данных и способы реализации окончательной согласованности в приложении для обеспечения доступности данных.