Кэш — промежуточный буфер с быстрым доступом к нему, содержащий информацию, которая может быть запрошена с наибольшей вероятностью.
В любом приложение есть медленные операции (запросы к БД или внешним API), результаты которых можно сохранить на какое-то время. Это позволит выполнять меньше таких операций, а большинству пользователей показывать заранее сохраненные данные.
Что кэшировать?
- Страницы целиком или ее частей
- Запросы к БД
- Медленные операции внутри системы (расчеты, счетчики и т. д.)
- Запросы к внешним системам
Повторные запросы
Некоторые запросы могут очень часто запрашиваться, даже в рамках одной страницы и запросы к внешнему кэшу (на отдельном сервере) могут приводить к задержкам или росту трафика.
Для решения этой проблемы можно разделить кэш на 2 части:
- Внутренний (в самом приложении)
- Внешний (на отдельном сервере)
Внимание! Такой подход может привести к утечкам памяти. Так что стоит с умом использовать внутренний кэш приложения.
Ключевые понятия
Время жизни (ttl) — это время после которого данные из кэша будут удалены. Никогда не устанавливайте время в 0 (бесконечная жизнь). Это может привести ко многим проблемам.
LRU (Least recently used) — алгоритм, который определяет какие ключи удалять в случае если не хватает места в кэше и нужно вытеснить какие-то данные.
Кэширование очень медленных запросов
Например у нас есть запрос, который отрабатывает 10 секунд и мы кладем его в кэш со временем жизни в 1 час. Когда проходит это время, данные в кэше удаляются. После этого у нас возникает ситуация, когда несколько пользователей одновременно выполняют этот тяжелый запрос.
Для решения данной проблемы можно использовать технику дублирования ключей с разным временем жизни.
Она еще называется «методика дублирования».
Проблемы при работе с кэшем
- Деление данных между кэширующими серверами
- Параллельные запросы на обновление данных
- «Холодный» старт и «прогревание» кэша
Деление данных между кэширующими серверами
Причины для разделения кэша между несколькими серверами:
- Данных может быть очень много, и они физически не поместятся в память одного сервера
- Данные могут запрашиваться очень часто, и один сервер не в состоянии обработать все эти запросы
- Сделать кэширование более надёжным.
Способы разбивки данных между серверами
- Вычисление номера сервера псевдослучайным образом в зависимости от ключа кэширования.
Проблема такого подхода заключается в том, что в случае падения одного из сервером, мы теряем больше 80% кэша. Так как местоположение ключей поменяется.
- Использование алгоритма согласованного хеширования. Основная идея этого механизма очень простая: здесь добавляется дополнительное отображение ключей на слоты, количество которых заметно превышает количество серверов.
- Логическое разделение данных. Например данные могут распределятся по некоему UserId. Так мы будем понимать что все данные, которые ему принадлежат — лежат на одном сервере.
Параллельные запросы на обновление данных
Может быть такая ситуация что данных нету в кэше и тогда приложение должно загрузить данные. Но проблема возникает тогда, когда несколько пользователей одновременно запрашивают данные, которых нету в кэше. В таком случае, каждый из них будет делать запрос напрямую в БД. Это может привести к тому что возрастет нагрузка на БД.
Какую проблему еще называют: cache stampede, hit miss storm или dog-pile effect.
Есть несколько способов решения данной проблемы:
Блокировка перед обновлением кэша — в таком случае, первый запрос получает уникальную блокировка на загрузку данных в кэш, а все остальные ожидают блокировку. Но такой способ порождает дополнительные проблемы: как обработать таймауты, нужно правильно подобрать время блокировки.
Фоновое обновление кэша — в таком случае приложение только читает данные из кэша, а отдельная программа/скрипт осуществляет обновление данных в фоне. В таком случае мы полностью исключаем возможность параллельного изменения данных.
Вероятностное обновление данных — мы обновляем данные не только при отсутствии их в кэше, а и с какой-то вероятностью их наличия. Это позволит обновлять их до того, как закэшированные данные «протухнут» и потребуются сразу всем процессам.
«Холодный» старт и «прогревание» кэша
Проблема массового обновления данных в кэше может быть вызвана не только их отсутствием но и одновременной загрузкой. Такая ситуация может возникнуть, когда выпускается новый функционал, которым будет пользоваться одновременно большое количество людей.
Сразу после выкатки этого функционала произойдет массовое наполнение кэша и в момент, когда его время жизни закончится.
Частично решить эту проблему можно решить следующими способами:
- Плавное включение нового функционала. Например можно включать функционал не лдля всех пользователей, а для небольшого количества и потом наращивать его.
- Разное время жизни элементов кэша. В данном случае мы не избавимся от первоначальной нагрузки, но в дальнейшем мы «размажем» ее во времени.
Достоинства
Существенно разгружается система и ее составляющие, что позволит значительно ее ускорить.
Недостатки
- Сложность реализации
- Множество проблем, которые необходимо решить
Ссылки
- Кэширование данных
- Обзор кэширования от amazon
- Кэширование и производительность веб-приложений
- 11 видов кэширования для современного сайта
- Тяжелое кэширование
- Проблемы при работе с кэшем и способы их решения
- Проблема одновременного перестроения кэшей