Facebook, Instagram, Amazon и Flipkart… эти приложения являются любимыми для многих людей и, скорее всего, это самые посещаемые веб-сайты в вашем списке.

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

Почему это происходит? Ответ:Кэширование.

Если вы проверите свою страницу в Instagram при медленном подключении к Интернету, вы заметите, что изображения продолжают загружаться, но текст отображается. Для любого бизнеса эти вещи имеют большое значение. Лучшее качество обслуживания клиентов/пользователей — это самое важное, и вы можете потерять много клиентов из-за плохого взаимодействия с вашим сайтом. Пользователь немедленно переключается на другой веб-сайт, если обнаруживает, что текущему веб-сайту требуется больше времени для загрузки или отображения результатов. Вы можете взять пример с просмотра любимого сериала в любом приложении для потокового видео. Как бы вы себя чувствовали, если бы видео все время буферизировалось? Скорее всего, вы не будете пользоваться этой услугой и прекратите подписку.

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

Кэширование — введение

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

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

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

Куда можно добавить кеш?

Кэширование используется почти на каждом уровне вычислений. Например, в аппаратном обеспечении у вас есть различные уровни кэш-памяти. У вас есть кеш-память уровня 1, которая является кэш-памятью ЦП, затем у вас есть кеш-память уровня 2, и, наконец, у вас будет обычная ОЗУ (оперативная память). Вы также должны кэшировать в операционных системах, таких как кэширование различных расширений ядра или файлов приложений. У вас также есть кэширование в веб-браузере, чтобы уменьшить время загрузки веб-сайта. Таким образом, кэширование можно использовать практически на всех уровнях: аппаратном обеспечении, ОС, веб-браузерах и веб-приложениях, но чаще всего оно находится ближе всего к внешнему интерфейсу.

Как работает кэш?

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

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

Типы кэша

Обычно существует четыре типа кеша…

1. Кэш сервера приложений

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

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

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

2. Распределенный кэш

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

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

3. Глобальный кэш

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

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

4. CDN (сеть распространения контента)

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

Инвалидация кеша

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

1. Запись через кеш

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

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

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

2. Запись вокруг кеша

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

3. Кэш обратной записи

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

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

Политика выселения

Мы обсудили так много концепций кэширования… теперь у вас может возникнуть один вопрос. Когда нам нужно сделать/загрузить запись в кеш и какие данные нам нужноудалить из кеша?

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

1. LRU (наименее недавно использованный)

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

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

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

2. LFU (наименее часто используемый)

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

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

3. MRU (самые последние использованные)

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

Tinder хранит в кеше все возможные совпадения пользователя. Он не рекомендует пользователю тот же профиль, когда он проводит пальцем по профилю влево/вправо в приложении. Это приведет к ухудшению пользовательского опыта, если один и тот же профиль будет рекомендован снова и снова. Таким образом, Tinder удаляет из кеша профиль, который наблюдался в последнее время, то есть профили, пролистываемые влево или вправо.

4. Случайная замена

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