Паттерны кеширования и способы инвалидации кеша
Этот урок будет посвящён не только повторению паттерна Cache-Aside, который вы изучили в курсе раньше, но и четырём новым паттернам. А ещё вы узнаете способы инвалидации кеша.
Паттерны кеширования для чтения
Изученный вами паттерн Cache-Aside считается самым простым и распространённым среди других паттернов кеширования.
Напомним механизм его работы.
- Пользователь отправляет запрос на чтение данных. Приложение проверяет кеш. Если данные найдены, происходит попадание в кеш.
- Если данные не найдены, происходит кеш-промах и приложение отправляет запрос к базе данных.
- Пользователь получает ответ. Данные заносятся в кеш.
Плюсы Cache-Aside:
- Устойчивость к сбоям кеша Если сервер кеша выходит из строя, система всё равно может работать, обращаясь напрямую к базе данных. Хотя это всё равно нежелательно, ведь увеличение нагрузки на базу данных может привести к выходу её из строя. В таких случаях кеш лучше реплицировать.
- Модель данных в кеше может отличаться от модели данных в БД Это позволяет выполнить запрос к БД, объединяющий несколько таблиц, и сохранить его в кеше под одним ключом. Так можно избежать дальнейшего выполнения сложной логики.
💡 Паттерн Cache-Aside лучше всего подходит для приложений с большим объёмом запросов на чтение: ведь именно чтение оптимизируется в рамках этой стратегии.
Минусы Cache-Aside:
- Низкая скорость обновления данных Обрабатываются только операции чтения, а не записи. Следовательно, любая операция записи всегда будет миновать кеш и выполняться непосредственно в базу данных, что считается медленной операцией.
- Несогласованность данных в кеше с базой данных Запись делается в базу данных, но не в кеш — в нём всё ещё старые данные. Чтобы справиться с этим, разработчики обычно используют параметр TTL для содержимого кеша и продолжают отдавать потенциально устаревшие данные до истечения TTL. Если нужно гарантировать свежесть данных, можно вручную инвалидировать кеш, используя одну из стратегий инвалидации, которые рассмотрим позже в этом уроке.
Read-Through
Различие с паттерном Cache-Aside в том, что актуальные данные из базы данных сразу кладутся в кеш и только потом возвращаются приложению.
💡 В паттерне Read-Through к базе данных обращается именно кеш, а не приложение.
Разберём пример кода данного паттерна:
JSX
Плюсы Read-Through:
- Меньшая сложность приложения и низкая вероятность ошибокКеш не обновляется на стороне приложения — теперь это ответственность базы данных. В стратегии Read-Through кеш является вспомогательным сервисом БД и приложению не нужно обновлять кеш при чтении.
Минусы Read-Through:
- Кеш-промах при первом запросе Когда данные запрашиваются в первый раз, это всегда приводит к кеш-промаху и влечёт за собой дополнительное время на загрузку данных в кеш. Это можно исправить с помощью «прогрева кеша» — ручной отправки запросов на старте приложения, чтобы данные из БД успешно подгрузились в кеш.
- Ограничения на выбор модели данных в кеше Модель данных в кеше должна быть идентична модели базы данных.
- Чувствительность к ошибкам в отличие от Cache-Aside Из-за того, что к базе обращается не приложение, а кеш, в случае ошибки данные будет брать неоткуда. И вернётся запрос с ошибкой. В паттерне Cache-Aside приложение в случае ошибки само отправит запрос к БД.
Практическая реализация такого подхода лежит на стороне специалистов, отвечающих за настройку БД или кеширующих серверов, — писать серверный код не требуется.
Оба паттерна оптимизируют только операции чтения и подходят для приложений, где практически отсутствуют операции записи. Конечно, это справедливо лишь для малой части реальных приложений.
Refresh-ahead
Это принудительное обновление часто используемых кешированных данных до истечения срока их действия. Оно происходит асинхронно, чтобы приложение не ощущало эффекта медленного чтения при извлечении объекта из хранилища данных в случае истечения срока его действия.
Плюсы Refresh-ahead:
- Низкая стоимость чтения данных из БД;
- Согласованность записей кеша, к которым часто обращаются пользователи;
- Высокая чувствительность к задержкам.
Минусы Refresh-ahead:
- Кеш должен работать без ошибок, поскольку в случае ошибки это будет не сразу определено и приведёт к неконсистентности данных и чтению из базы. Нужна качественная система мониторинга, чтобы не оказаться с устаревшими данными.
Паттерн идеально подходит, например, для спортивных трансляций: можно оперативно передавать данные об игре.
Пример кода:
JSX
Паттерны кеширования Cache-Aside и Read-Through отлично работают в приложениях, рассчитанных на большое количество запросов чтения, — Read-Heavy-приложения. Но как быть, когда запросы на запись превалируют? Рассмотрим два новых паттерна кеширования: Write-Through и Write-Behind.
Паттерн Write-Through
Паттерн применяет похожую на Read-Through логику для операций записи. При Write-Through любая операция записи всегда проходит и через кеш, и через базу данных последовательно.
Механизм работы паттерна Write-Through:
- Приложение обновляет кеш.
- Кеш немедленно обновляет базу данных.
- База данных обновляется в соответствии с задачей.
- Ответ об успешной операции возвращается клиенту.
Пример паттерна Write-Through:
JSX
Плюсы Write-Through:
- Данные между кешем и базой данных всегда будут синхронизированы. Это исключает возможность неконсистентности кеша и его инвалидацию.
Минусы Write-Through:
- Необходимость ждать, пока кеш обновит базу данных. Так как эта операция синхронная, клиент не получит ответа, пока база не обновится. В некоторых случаях критично убедиться в немедленном обновлении базы: например, если клиент обновил важные для всех остальных клиентов данные и они должны быть мгновенно отображены у каждого другого пользователя системы.
💡 В реальных приложениях, где присутствуют активные операции и чтения, и записи, используют смешанный подход из нескольких паттернов кеширования. Чаще всего это комбинация Read-Through и Write-Through.
Часто обновления кеша бывает достаточно, а базу допустимо обновить позже в определённые сроки. Это позволяет сделать другой паттерн для записи кеша.
Паттерн Write-Behind
Ключевое различие двух паттернов записи: в Write-Through данные, записанные в кеш, обновляют базу данных синхронно, в Write-Behind — асинхронно.
Механизм работы паттерна Write-Behind:
- Приложение обновляет кеш.
- Кеш возвращает клиенту ответ.
- Кеш планирует асинхронную задачу на обновление базы данных.
- База данных обновляется в соответствии с задачей.
С точки зрения приложения запись Write-Behind выполняется быстрее, поскольку перед возвратом ответа приложению необходимо обновить только кеш. Пользователю не нужно ждать ответа базы данных — её обновление произойдёт в фоновом режиме.
Пример кода паттерна:
JSX
Асинхронные задачи и коммуникации при построении микросервисов считаются хорошим тоном. Избавление от синхронной цепочки вызовов даёт возможность компонентам системы работать независимо друг от друга вместо блокирующего ожидания ответа.
Мы подготовили для вас таблицу-шпаргалку с плюсами и минусами всех пройденных паттернов. Вы можете скачать её и пользоваться при выборе способа кеширования.
Вы уже понимаете, что кеширование помогает ускорить получение данных пользователями. Но при этом нужно сохранить консистентность данных, поэтому вместе с кешированием всегда выбирают и настраивают очистку кеша. Расскажем о том, какие есть способы это делать и как выбрать подходящий под задачу.
Способы инвалидации кеша
Основным преимуществом кеширования считается высокая скорость доступа к данным. Однако за эту скорость приходится платить потенциально устаревшими данными, если кеш не успел обновиться из настоящего источника данных.
Следовательно, важнейший вопрос при внедрении кеширования — в какой момент очищать данные в кеше?
Ещё такую очистку называют инвалидацией. Способов инвалидации кеша много. Каждый из них имеет свои особенности и подходит для определённых случаев. Рассмотрим наиболее популярные способы.
Временная инвалидация
Самый простой способ: для его реализации устанавливается фиксированное время жизни данных в кеше.
Пример. Веб-страница содержит информацию о погоде. Исследования аналитиков показали, что пользователи заинтересованы в прогнозе погоды на ближайший час. В этом случае можно использовать временную инвалидацию и установить срок действия кеша на один час. Когда пользователь запросит эту страницу, данные будут получены из кеша, но после истечения часа страница будет считаться устаревшей и данные будут обновлены.
Инвалидация, основанная на запросах
Механизм работы данного способа в том, что кеш инвалидируется каждый раз, когда приходит запрос на обновление данных.
Пример. Онлайн-магазин, где пользователи могут добавить товар в корзину и произвести оплату.
Пользователь оформил и оплатил заказ. После успешной оплаты система может выполнить инвалидацию кеша страницы заказа, чтобы пользователю предоставили актуальную информацию о статусе его заказа. В противном случае заказ висел бы в состоянии ожидания оплаты.
Способ обеспечивает надёжность и актуальность данных, основываясь на активности и запросах пользователей.
Инвалидация на основе изменений
Кеш инвалидируется каждый раз, когда происходят изменения в данных.
Пример. Блог, где пользователи могут оставлять комментарии к статьям. При добавлении нового комментария система инвалидирует кеш страницы со статьёй, чтобы отобразить новый комментарий. Так пользователи смогут видеть актуальные данные и взаимодействовать с контентом, даже если другие пользователи вносят изменения в информацию.
Программная инвалидация
Способ предоставляет возможность явно указывать, когда кеш должен быть инвалидирован на основе определённых условий или событий в приложении.
Пример. Система бронирования авиабилетов, где доступность мест на рейс может изменяться динамически. При каждом запросе на бронирование система программно инвалидирует кеш, если количество доступных мест изменилось. Это гарантирует, что пользователю будет показана актуальная информация о доступности мест и он не будет пытаться забронировать уже занятые места.
Программная инвалидация обеспечивает гибкость и контроль над инвалидацией кеша в соответствии с логикой и потребностями приложения.
Инвалидация по ключу
Позволяет инвалидировать кеш для конкретных данных или ключей. Это полезно в ситуациях, когда только некоторые данные нуждаются в обновлении.
Пример. Приложение для учёта товаров на складе, где каждый товар имеет уникальный идентификатор. При обновлении количества товаров на складе очищается кеш только для изменённого товара по его уникальному идентификатору. Это помогает минимизировать объём инвалидации и обновлять только необходимые данные, улучшая производительность и уменьшая нагрузку на систему.
При выборе стратегии инвалидации кеша важно учитывать особенности и требования конкретного приложения.
Советуем внимательно изучить бизнес-процессы вашего приложения и на их основе выбирать наиболее подходящую стратегию инвалидации кеша. Иногда подойдёт всего одна стратегия, а иногда — их комбинации. От этого зависит эффективность работы приложения, обеспечение актуальности данных и улучшение производительности.
Задание 3
Соедините способы инвалидации кеша с их описанием.
Итоги
- Паттерн Cache-Aside лучше всего подходит для приложений с большим объёмом запросов на чтение, ведь в рамках этой стратегии оптимизируется именно чтение.
- Паттерн Read-Through устроен так, что к базе данных обращается именно кеш, а не приложение.
- Паттерн Refresh-ahead имеет низкую стоимость чтения данных, но кеш должен работать без ошибок, поскольку в случае ошибки это приведёт к неконсистентности.
- Паттерн Write-Through применяет похожую на Read-Through логику для операций записи. Любая операция записи проходит и через кеш, и через базу данных последовательно.
- Паттерн Write-Behind выполняется быстрее, поскольку перед возвратом ответа приложению необходимо обновить только кеш. Пользователю не нужно ждать ответа базы данных: её обновление произойдёт в фоновом режиме.
- В реальных приложениях используют смешанный подход из нескольких паттернов кеширования. Чаще всего это комбинация Read-Through и Write-Through.
- Очистку данных называют инвалидацией. Способов инвалидации кеша несколько: временная и программная инвалидация, инвалидация на основе запросов, на основе изменений и по ключу. Каждый способ имеет свои особенности и подходит для определённых задач.
В следующем уроке вас ждёт практика: вы научитесь настраивать паттерны кеширования.