Основы кэширования

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

Техники кэширования

Сохранение результатов ввода/вывода (I/O) в высокоскоростной памяти. Для этих целей используются быстрые и более дорогие накопители, объём которых, как правило, существенно меньше по сравнению с основным хранилищем данных. Меньший объем кэша может быть связан не только с высокой стоимостью быстрых накопителей, но и с необходимостью управления большим объемом кэша, что может потребовать дополнительных ресурсов.

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

Сохранение результатов вычислений позволяет избежать повторения трудоемких и дорогостоящих вычислений.

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

Области и способы применения

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

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

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

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

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

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

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

Влияние на нефункциональные требования

Хорошо подобранный набор техник и технологии кэширования помогает удовлетворять следующим нефункциональные требованиям:

  • Производительность. Сокращается время получения данных, увеличивается допустимая нагрузка на систему и скорость обработки запросов.
  • Экономичность. Использование кэширования может помочь снизить затраты на инфраструктуру, уменьшая необходимость в высокопроизводительном оборудовании для баз данных и серверов за счёт сокращения общего количества обращений к ресурсам и оптимизации нагрузки.
  • Доступность. Кэширование увеличивает доступность данных, минимизируя зависимость от внешних систем и сетевых задержек. В случае сетевых проблем или задержек кэшированные данные по-прежнему будут доступны пользователям.
  • Надёжность. Путём кэширования важных данных можно обеспечить их доступность даже в случае сбоев основной системы хранения, что повышает общую надёжность приложения.
  • Отзывчивость. Кэширование улучшает взаимодействие пользователя с системой, обеспечивая мгновенный доступ к данным и ресурсам, что особенно важно для интерактивных приложений и сервисов, требующих высокой отзывчивости интерфейса.

Возможные риски:

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

Разработка стратегии кэширования

Выбор данных для кэширования

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

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

Критерии выбора данных:

  • Анализ частоты доступа к данным. Определение, какие данные запрашиваются наиболее часто, для их приоритетного кэширования.
  • Срок актуальности данных. Определение редко изменяющихся позволяет сократить количество операций инвалидации кэша.
  • Анализ стоимости получения данных. Данные, получение которых требует значительных ресурсов или времени, могут быть приоритетными кандидатами для кэширования.
  • Оценка объема данных. Кэширование данных небольшого размера позволит оптимизировать используемую память. Объемные сущности могут включать данные, которые не всегда нужны, или часть их них изменяется динамично. Такое включение может приводить к увеличению памяти и его более частому, чем необходимо, обновлению.
  • Выбор ключей. Неэффективный выбор ключей может привести к избыточному занимаемому пространству или сложностям при извлечении данных.

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

На заметку: Рекомендуется прочитать статью от Amazon[5], с частными рекомендациями повышения эффективности: «кэшировать (почти) все», прогрев при истечении TTL для высоконагруженных ключей кэша и пр.

Выбор места размещения кэша

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

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

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

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

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

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

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

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

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

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

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

Управление данными кэша

Заполнение при чтении

Кэширование на стороне при чтении (Read Aside) или ленивое кэширование (Lazy Caching). Популярная стратегия заполнения, данные загружаются в кэш только тогда, когда происходит первый запрос на чтение. Сервис сначала проверяет наличие данных в кэше, а при отсутствии получает данные из хранилища, размещает в кэше и возвращает клиенту.

Преимущества:

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

Недостатки:

  • Задержка при запросе данных, отсутствующих в кэше.
  • Необходимо управлять согласованностью. Данные в кэше могут устаревать, требуются механизмы их инвалидации или обновления.
  • Возможен эффект «громоподобного стада»[5], т.е. промах кэша при запросе одного и того же ключа несколькими потоками, после чего выполниться одновременный запрос к источнику данных, что особенно критично для ресурсоемких запросов.
  • Усложняет реализацию, т.к. логика реализуется на стороне приложения.
  • Для статистических данных может быть эффективнее стратегия предварительного заполнения при запуске, при наличии достаточного места в кэше и возможностях инфраструктуры выдерживать пиковые нагрузки.

Сквозное кэширование при чтении (Read Through). Запрос приложения проходит через кэш, если данные отсутствуют, то кэш сам загружает их из источника и возвращает приложению.

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

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

Операции при записи

Кэширование на стороне при записи (Write Aside). Выполняется запись в основное хранилище, а затем заполнение, обновление данных в кэше.

Инвалидация по событию — частный случай стратегии, когда вместо обновления данные в кэше инвалидируют.

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

Сквозная запись (Write-Through). Данные записываются или обновляются в кэше, а затем кэш синхронизирует их с основным хранилищем. Иногда под этой стратегией понимают Write Aside.

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

Отложенная запись (Write-Back или Write-Behind). Стратегия похожа Write Through, однако данные синхронизируются с источником асинхронно, через некоторый промежуток времени.

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

Запись в обход (Write-Around). Данные записываются непосредственно в основное хранилище, минуя кэш.
Преимущества: Уменьшает нагрузку на кэш при частых операциях записи.
Недостатки: Может увеличить время доступа к данным при их последующем чтении, если они не были кэшированы заранее.

Операции по расписанию

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

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

Жадная загрузка (Eager Loading) или прогрев кэша. Частный случай стратегии фонового обновления, когда данные загружаются в кэш в момент старта приложения. Данные для загрузки могут выбираться через анализ наиболее вероятных запросов или путем загрузки в кэш больших объемов.

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

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

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

На заметку: Подробнее про различные варианты управления данными в дополнительных материалах[3][5][6].

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

Необходимость сегментирования и партиционирования

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

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

На заметку: Шаблон сегментирования кэша хорошо описано в статьях от Microsoft[1][7].

Отказы кэша

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

Рекомендации:

  • Сохранение данных в постоянном хранилище. Следует убедиться, что критичные данные всегда надежно сохраняются в постоянном хранилище и кэш не используется в качестве единственного способа их хранения.
  • Переключение на первичный источник данных. Приложение должно быть способно определить недоступность кэша, переключиться на исходное хранилище и продолжить функционирование без сбоев, недоступности или потери важных сведений. Одним из вариантов решения в такой ситуации может быть шаблон «Автоматическое выключение» (Circuit-Breaker Pattern).

Безопасность

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

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

Вытеснение

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

  • Ничего не делать. Существует риск переполнения памяти.
  • Вытеснение случайных записей. Риск вытеснения востребованных записей.
  • Удаление записей с наименьшим TTL.
  • LRU (Least Recently Used). По времени последнего обращения.
  • LFU (Least Frequently Used). По частоте использования записи.

Управление параллелизмом в кэше

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

  • Оптимистичный подход. Приложение проверяет, изменялись ли данные в кэше с момента получения, непосредственно перед их обновлением. Если данные не изменялись, то можно произвести изменения. В противном случае приложение должно принять решение, следует ли обновить данные. (Бизнес-логика, которая управляет этим решением, будет конкретным приложением.) Этот подход подходит для ситуаций, когда обновления нечасто или где столкновения вряд ли возникают.
  • Пессимистичный подход. Извлекая данные, приложение блокирует их в кэше, чтобы предотвратить их изменение другим экземпляром. Этот процесс гарантирует, что столкновения не могут возникать, но они также могут блокировать другие экземпляры, которые должны обрабатывать те же данные. Пессимистичный параллелизм может повлиять на масштабируемость решения и рекомендуется к использованию только для кратковременных операций. Этот подход более подходит для ситуаций, где возникновение конфликтов наиболее вероятно, особенно в том случае, если приложение обновляет несколько элементов в кэше, и необходимо убедиться, что эти изменения применяются последовательно.

Сложность управления и экономика

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

Метрики кэширования

Метрики кэширования — это статистические данные, которые помогают анализировать эффективность системы кэширования. Основные метрики кэширования включают:

  • Процент попаданий в кэш (Cache Hit Rate) показывает эффективность использования кэша и представляет отношение числа успешных обращений к кэшу, когда данные присутствовали в кэше, к общему числу обращений. Существует обратная метрика — процент промахов кэша (Cache Miss Rate).
  • RPS чтения/записи — количество операций чтения/записи за единицу времени, чем больше операций чтения и меньше операций записи, тем эффективнее используется кэш.
  • Время отклика представляет собой время, в течение которого кэш отвечает на запрос клиента.
  • Использование процессора. Всплески использования процессора кэшем могут приводить к задержкам в приложении.
  • Потребление памяти — потребление всей доступной памяти может привести к подкачке с диска и вытеснению, что снижает производительность.
  • Эффективность использования памяти — отношение полезного использования памяти к общему размеру памяти кэша. Эта метрика помогает оценить, насколько эффективно используется выделенное пространство кэша.
  • Степень фрагментации памяти определяется как отношение памяти, выделенной операционной системой к памяти, используемой кэшем. Высокая степень фрагментации может снизить производительность и увеличить задержку.
  • Процент удаления записей по истечении TTL (Expired rate). Этот показатель помогает обнаружить проблемы с производительностью, вызванные большим количеством записей с одновременно истекшим TTL.
  • Частота вытеснения из кэша (Eviction rate). Частота, с которой данные удаляются из кэша для освобождения места для новых данных. Высокая частота изгнания может указывать на недостаточный размер кэша или на неэффективную политику управления кэшем.

Существуют другие метрики, помогающие оптимизировать производительность кэша и снижать нагрузку на систему, несколько статей приведены в разделе с материалами[8][9].

Заключение

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

  1. Анализ проблемы. Необходимо понять потребности и нефункциональные требования, которые необходимо удовлетворить с помощью кэширования.
  2. Разработка стратегии. Определить элементы стратегии кэширования исходя из специфики системы.
  3. Оптимизация и мониторинг. Выявление узких мест и оптимизация с помощью метрик.

Материалы

  1. Руководство по кэшированию от Microsoft — отличное подробное руководство по кэшированию в распределенных системах, приводятся общие проблемы и рекомендации по применению кэширования, обеспечению окончательной согласованности, управлению параллелизмом и защите, рассматривается Redis, как наиболее популярная система кэширования и варианты её использования, в конце приводятся полезные ссылки на материалы и шаблоны при реализации кэширования в приложениях.
  2. Обзор кэширования от Amazon — обзорная статья от Amazon по кэшированию, описаны основные сведения, преимущества и области использования применения. По ссылкам из меню можно найти ценную информацию по типам кэширования и лучшим практикам, где приводятся типовые шаблоны применения.
  3. [По полочкам] Кэширование. Хабр — хорошая статья про базовые концепции кэширования, такие как, метрики, выбор данных и стратегии для кэширования, сравнение типов кэшей, вытеснение и другие полезные заметки.
  4. Кэш — Википедия — история, функционирование и различные реализации.
  5. Caching Best Practice от Amazon — приведены полезные типовые варианты реализации и проблемы кэширование.
  6. Описание шаблона Cache-aside от Microsoft — подробное описание шаблона cache-aside, его преимуществ и недостатков.
  7. Описание шаблона Сегментирование (Sharding) от Microsoft — техника сегментирования может быть применена не только к постоянным хранилищам, но и к кэшу, что позволяет его масштабировать, повышать доступность и снижать время отклика.
  8. Отслеживание кэша Azure для Redis — приведено множество метрик кэша для кэша Redis в Microsoft Azure, несмотря на это большинство из них могут быть полезны в целом для других систем кэширования.
  9. Сто первое руководство по мониторингу метрик в Redis. Хабр — хорошая статься на Хабр с основными метриками Redis и способами их вычисления.

Comments

So empty here ... leave a comment!

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Sidebar