Доброго времени суток.

Представляю вашему вниманию перевод статьи «Everything you need to know about Caching — System Design» автора Animesh Gaitonde.

Введение


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



Так выглядит моя страница в Instagram при медленном соединении. Как видите, текст отображается, в то время как изображения еще не загрузились.

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

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

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


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



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

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


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



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

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

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

Следующая диаграмма демонстрирует процесс чтения данных из кэша:



Ключевые концепции кэша


Время жизни (Time to Live, TTL)


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

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

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

Политика удаления


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

Существует несколько политик очистки кэша, таких как «старые (наименее недавно используемые)» (Least Recently Used, LRU), «редко запрашиваемые (наименее часто используемые)» (Least Frequently Used, LFU), «последние (наиболее недавано используемые)» (Most Recently Used, MRU). Эти политики удаляют данные из кэша по определенному принципу.

Старые


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

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

Редко запрашиваемые


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

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



Из этого кэша впоследствии удаляются редко набираемые слова. В приведенном примере, если вы будете использовать слова «feature», «features», «feather» и т.д., через какое-то время телефон перестанет предлагать вам «feat», поскольку оно будет удалено из кэша.

Последние


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



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

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

Типы кэша


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


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



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

Запись из кэша


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



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

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

Запись в обход кэша


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



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

Примеры использования кэша в распределенных системах


Список открытых проектов для работы с кэшем:

  • Redis
  • Memcached
  • VoltDB
  • Aerospike DBS
  • Apache Ignite

Благодарю за внимание.