Кэш

Кэш

Дмитрий Бахтенков

Введение

Что такое кэш?

В общем случае, кэш - это некоторый промежуточный буфер для быстрого доступа к часто используемым данным. Его цель - ускорить получение данных за счёт хранения копии часто используемых данных в некотором расположении, к которому можно получить доступ быстрее, чем к основному источнику данных. Кэши бывают как аппаратные - например CPU Cache, который хранит копии часто используемых данных из основной памяти; так и программные - когда приложение сохраняет в оперативной памяти некоторые данные, чтобы не делать запрос в БД.

Принцип работы кэша

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

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

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

А сейчас докинем скучных терминов.

  • Попадание кэша (Cache Hit) - ситуация, при которой данные успешно найдены в кэше (и нам не надо их доставать из основного источника)
  • Промах кэша (Cache Miss) - ситуация, при которой данные не найдены в кэше (и нам нужно обращаться к основному источнику данных).

Освобождение кэша (Cache Eviction)

Cache Eviction — это процесс удаления элементов из кэша, когда кэш достигает определенного предела емкости.

Существует несколько политик (правил) очищения кэша, которые могут применяться в зависимости от конкретной реализации кэша и потребностей пользователя:

  • Least Recently Used (LRU): эта политика удаляет из кэша элементы, которые дольше всего не запрашивались. Это популярная политика, потому что она проста и эффективна и, как правило, хорошо работает для кэшей, доступ к которым осуществляется предсказуемым образом.
  • Least Frequently Used (LFU): эта политика удаляет из кэша наименее часто используемые элементы. Эта политика может быть эффективной для кэшей, к которым обращаются нерегулярно, но может быть не столь эффективной для кэшей, к которым обращаются часто.
  • Time-based eviction: эта политика удаляет элементы из кэша по истечении определенного времени. Это может быть полезно для кэшей, хранящих данные со сроком действия, например данные сеанса авторизации или данные из внешних API.

Освобождение кэша — важный аспект управления кэшем, поскольку он помогает гарантировать, что кэш использует ресурсы эффективно и что наиболее важные данные доступны в кэше.

Инвалидация кэша (Cache Invalidation)

Инвалидация кэша - это процесс удаления данных из кэша связанный с изменением состояния данных в приложении. Например:

  1. Мы добавили в кэш информацию о пользователе нашей системы
  2. Пользователь изменил иформацию о себе, например отредактировал имя
  3. В кэше всё ещё лежат старые данные, при этом в основной базе данных - уже новые

Эту проблему решает инвалидация. Мы сообщаем кэшу, что данные с таким-то ключом (в случае из примера ключом может быть идентификатор пользователя) нужно удалить, чтобы при следующем запросе в него подтянулись актуальные данные.

Ивалидация бывает двух видов:

  1. По времени - данные уничтожаются по истечении какого-то времени. Например, мы кэшируем данные на пять минут, и через эти пять минут они будут удалены из кэша и получены заново (со всеми обновлениями).
  2. По тегам или ключам - мы помечаем данные в кэше некоторыми тегами. Далее, когда происходит какое-то событие (редактирование записи) мы удаляем из кэша данные по тегам или ключу, связанным с этим объектом (например по идентификатору сущности).

Типы кэша

Существуют различные типы кэша, которые используются в различных ситуациях / платформах. Мы не будем рассматривать типы, относящиеся к аппаратному обеспечению - только то, что связано с ПО.

In-Memory Cache

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

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

Реализуется этот тип кэша обычно как некоторая статическая структура данных (обычно словарь) в приложении, которая используется как “мешок” для всяких данных.

Веб-кэш (Web Cache)

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

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

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

Веб-кэш — важный инструмент для повышения производительности веб-сайтов и уменьшения объема данных, передаваемых через Интернет. Кроме того, это экономичное решение, поскольку оно снижает пропускную способность, необходимую для доставки контента пользователям.

Кэш баз данных (Database Cache)

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

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

Кэши базы данных могут быть реализованы различными способами, например, с использованием выделенного сервера кэширования, такого как Memcached, или с помощью уровня кэширования, встроенного в систему управления базами данных (СУБД), например Redis или Oracle. При выборе решения для кэширования базы данных важно учитывать такие факторы, как масштабируемость, надежность и простота интеграции с базой данных.

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

Кэш в распределённых приложениях

Современные облачные сервисы часто являются распределёнными - это означает, что у нас развёрнуто несколько копий одного и того же приложения, и нагрузка по ним распределяется равномерно через некоторый балансировщик нагрузки. Это позволяет приложению выдерживать большие нагрузки, а также усиливает надёжность (если одна копия упала, запросы пользователей можно перенаправить на остальные) и открывает дополнительные возможности по работе с сервисом, например Blue-Green Deployment.

У распределённых приложений есть как плюсы, так и минусы - об этом поговорим в другой раз. Сейчас же мы сосредоточимся на вопросах кэша в таких системах.

Если в нашем приложении используется In-Memory кэш, например библиотека для C# - Microsoft.Extensions.Caching.Memory, то у нас получается отдельный кэш для каждой копии приложения. С таким подходом мы имеем один важный недостаток:

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

Схематично это будет выглядеть примерно так:

И тут к нам на помощь приходят различные in-memory СУБД, которые позволяют вынести кэш из уровня приложения на уровень отельного сервиса: Redis Cache, Apache Ignite и другие.

Такой подход имеет следующие преимущества:

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

Оценка эффективности кэша и типичные ошибки

Основная цель кэширования - повышение производительности программы. Для максимальной эффективности необходимо кэшировать наиболее часто используемые данные, при этом не допуская избыточности данных в кэше - чтобы он не скушал всю оперативку на сервере 🙂

Для оценки эффективности кэша используются две основные метрики:

  1. Отношение количества попаданий на количество промахов кэша - отражает то, насколько часто используются данные из кэша, а не из основного источника.
  2. Общий размер кэша. Тут всё просто - если кэш очень маленький, значит можно кэшировать что-то ещё. Если кэш огромный - кажется, у нас избыточное кэширование.

Основные ошибки при кэшировании:

  1. Кэшировать элементы, которые слишком часто обновляются. В таком случае слишком часто будет происходить инвалидация кэша, и кэширование просто потеряет смысл
  2. Использование кэша вместо оптимизации программы. Если в системе изначально есть проблемы с производительностью, то, вероятно, кэш будет не лучшим решением. Он лишь оттянет время, которое придётся потратить на общую оптимизацию системы, выступит как заглушка.

Делаем выводы

Сегодня мы узнали о концепции кэша и его использовании в приложениях.

С вами был Flex Code.

Report Page