Кеширование
Какие проблемы позволяет решать:
- Сокращение response-time сервисов,
- Снижение лишней нагрузки на сторонние сервисы,
- Переиспользование ранее полученных или вычисленных данных,
- Стабилизация работы при кратковременных отказах систем.
Основные термины
- Cache miss --- промах кэша, запрошенный ключ не был найден в кэше
- Cache hit --- попадание в кэш, запрошенный ключ найден в кэше
- Hit ratio --- процент попаданий запросов в кэш, характеризизует эффективность кэширования
- Горячий ключ --- ключ, на который приходиться большая часть запросов
- Прогрев кэша --- процесс наполнения кэша данными
- Инвалидация --- удаление кэшированных данных
Какие данные кэшировать?
- меняются редко(дни, недели, месяцы) --- в данном случае можно спокойно кешировать эти данные
- меняются нечасто(минуты, часы) --- чаще всего здесь задаемся вопросом - "стоит ли мне кэшировать"
- меняются часто(секунды) --- кэшировтаь чаще всего бессмысленно, но иногда может пригодиться
Кэширование ошибок
Кэшируем ошибки и тогда последующие запросы не будут обращаться к источнику информации, а также не будет cache miss attack
!!! По-хорошему, нужно уметь держать нагрузку без кэша
задача кэша - ускорить ответ, а не держать нагрузку
проблема старта с непрогретым кэшем
- предварительно загружаем данные в кэш часто используемых запросов
Всегда ли кэшированеи полезно?
оценка эффективности кэширования
AverageTime = DBAccessTime * CacheMissRate + CacheAccessTime
Пусть:
- DBAccessTime = 100ms
- CacheAccessTime = 20ms
Тогда при CacheMissRate > 0.8 - кэш вреден!
Виды кэширования
внутреннее кэширование
кэш хранится внутри сервиса, (например ввиде хэщ-таблиц) или рядом на диске
Плюсы:
- Высокая скорость
- Отсутствие сетевых запросов
- Нет расходов на Marshaling / Unmarshaling данных
Минусы:
- Горизонтальное мастшабирование
- Прогрев кэша после падения сервиса
внешнее кэширование
кэш храниться в какой-то другой in-memory базе данных, севрис обраащеться к ней и воспринимате как кэш
Плюсы:
- Хранение большого объёма данных
- Простое горизонтальное масштабирование
- После падение сервиса данные кэша не теряются
- Простой прогрев кэша и простая логика инвалидации
Минусы:
- Скорость работы
Способы взаимодействия с кэшем
Cache Aside(кэширование на стороне)
В этой стратегии, приложение координирует запросы в кэш и БД и само решает, куда и в какой момент обращаться
пайплайн чтения:
Пайплайн записи:
Cache through(Сквозное кэширование)
В рамках этой стратегии все запросы от приложения проходят через кэш
пайплайн чтения:
пайплайн записи:
Cache Ahead(Опережающее кэширование)
Запросы на чтение всегда идут только в кэш, никогда не попадая в БД напрямую
Резюме
- Важно учитывать актуализацию данных
- Стоит балансировать между характером нагрузки (какая нагрузка преобладает – чтение или запись)
- Также не забыть про отказоустойчивость
Алгоритмы вытеснения данных
допустим наш кэш способен вмещать только 4 пользователей
рассмотрим разные алгоритмы
Random
можно ли вытеснять рандомно?
можно, но вопрос в эффективности
FIFO
первым зашел первым вышел
LIFO
последним зашел первым вышел
LRU
вытесняеться элемент к которому дольше всего не было обращений
MRU
теоретический алгоритм, по сути вытесняется тот элемнет к кому последнему обращались по времени
LFU
ведется счетчик обращений к элементам, вытеснятеся тот к кому было меньше всего обращений
Second Chance
При обращении выставляем бит присутствия, а при вытеснении удаляем из начала очереди, если бит = 0
Если бит = 1, то меняем бит на ноль, затем переносим элемент в конец очереди и идем к следующему
Clock
Тот же Second Chance, только не нужно двигать элемент из начала в конец очереди, если бит равен единице
Используется циклическая очередь
2Q
Элементы, запрошенные из 1FIFO, никуда не двигаются. Вытесненные из 1FIFO перемещаются в 2FIFO
Элементы, запрошенные из 2FIFO, уходят в LRU. Вытесненные из 2FIFO удаляются
SLRU
- Сперва кладем в первую коробочку.
- При повторном запросе перекладываем во вторую
- При еще одном повторном – из второй в третью
TLRU (Time aware LRU)
Абсолютно такой же алгоритм, только к данным добавляется их время жизни в кэше (TTL), по которому они автоматически удаляются из кэша
LRU-k
Удаляет страницу, К-й последний доступ к которой находится дальше всего в прошлом. Например, LRU-1 — это просто LRU, тогда как LRU-2 удаляет страницы в соответствии со временем их предпоследнего доступа
Инвалидация данных в кэше
Инвалидация по TTL (Time To Live)
При сохранении данных в кэш для них устанавливается время жизни (TTL) и данные будут автоматически удалены через это время (основная проблема заключается в поборе TTL)
Jitter
Если записи становятся недействительными одновременно и в большом количестве, то источник данных может пострадать. В данной ситуации помогает Jitter (случайная величина, добавляемая к TTL)
Thundering herd problem
Резкий рост нагрузки на источник данных, который возникает, когда множество процессов/потоков одновременно запрашивают один ключ кэша, получают cache miss, а затем каждый из них выполняет один и тот же запрос
Инвалидация по событию
не устанавливаем время жизни данных, а удаляемых их по событию. Допустим у нас есть какой-то notifier (оповещатель). За ним есть брокер, который пойдет и поудаляет кэши по событию.
Версионирование кэша
К ключу кэширования добавляется версия: поэтому инвалидации всех ключей кэширования с префиксом достаточно инкрементировать версию ключа.
Тегирование кэша
Многомерный кэш
Кэши могут складываться в цепочку или в дерево
когда, например, происходит обращение к какому-нибудь сервису:
- Кэш браузера
- Кэш прокси сервера
- Внутренний кэш приложения
- Внешний кэш приложения
- Кэш движка базы данных
API
CRUD
Акроним, обозначающий четыре базовые функции, используемые при работе с данными: создание (C), чтение (R), модификация (U), удаление (D)
REST
- Глагол – метод запроса (GET, POST, DELETE, PUT)
- Существительное – URI запроса
- Дополнительная информация – хедеры запроса
- Содержимое – тело запроса и ответа в Json
- Результат – код ответа
SOAP
старый, мало где используется
тоже что что и rest только всё храниться внутри xml.
RPC
Класс технологий, позволяющих программам вызывать функции или процедуры в другом адресном пространстве
gRPC
описывается определенный формат ввиде protobuf, затем используется кодогенерация, в данном случае для языка программирвоания go
GraphQL
по сути отношусь к сервису как к базе данных, для кастомизации запросов
Резюме
- SOAP уже умер
- REST для клиентского взаимодействия
- gRPC для внутреннего взаимодействия
- GraphQL для кастомизации запросов
Получение событий от сервера
Как переодически получать обновления от сервера?
Webhook
Метод, позволяющий приложению автоматически передавать данные или уведомления другому приложению по определенным событиям или действиям
Streaming
SSE (Server Side Events)
Клиент подписывается на события сервера и как только происходит событие — клиент сразу же получает уведомление по HTTP и некоторые данные, связанные с этим событием
WebSocket
Протокол для общения между клиентом и сервером, предоставляющий возможность двухсторонней коммуникации поверх TCP
Резюме
- Polling --- используется, когда клиенту необходимо регулярно обновлять информацию
- Streaming --- используется, когда клиенту необходимо получать непрерывный или хаотичный поток данных
- Webhooks --- используются, когда серверу необходимо уведомить клиента о каком-то событии
Подходы проектирования API
Как отдавать большое количество сущностей через API?
Ответ: Пагинация
Большое количество сущностей отправляется клиенту по частям (чанками — chunks)
Как улучшить производительность API?
Вариант №1
Запросы и ответы можно сжимать
Чем меньше размер данных, тем быстрее они передаются по сети
Сжатие ускоряет загрузку и скачивание данных
Вариант №2
Можно сохранять запрашиваемые данные в кэше у клиента
И возвращать их клиентам без повторного обращения к сервису
Как предотвратить повторное выполнение операции?
ключ идемпотентности
На стороне клиента генерируем ключ и отправляем его сервису через API. Сервис у себя проверит, выполнял ли он уже эту операцию по ключу или нет
Как вносить изменения в API?
Версионирование API
Так как API со временем развивается, важно иметь стратегию управления версиями для управления будущими изменениями
Этот этап включает в себя реализацию механизмов управления версиями, включая номер версии
Observability
Мониторинг
Процесс сбора, анализа и визуализации данных о работе приложения с целью обеспечения его надежности, производительности и доступности.
Методологии мониторинга:
LTES (Google SRE)
- Latency – время на обработку одного запроса
- Traffic – количество запросов
- Errors – количество ошибок
- Saturation – насколько компонент использует свои ресурсы
RED
- Rate – количество запросов
- Errors – количество ошибок
- Duration – время обработки одного запроса
USE
- Utilization – время или процент использования ресурса
- Saturation – количество отложенной работы
- Errors – количество ошибок
Нельзя сказать, что какая-то из этих методологий «правильная»
Каждый отдельный компонент системы нужно мониторить, основываясь на здравом смысле
Алертинг
Алертинг отслеживает изменения определенных метрик с помощью алертов и отправляет уведомления через подходящий для вас канал уведомлений (основывается на концепциях SLI, SLA и SLO)
алерт должен срабатывать, когда возникла ошибка, но еще можно что-то исправить
а не когда «всё уже очень плохо» и не от каждой «помехи»
Логирование
Логированием называют запись логов - оно позволяет ответить на вопросы, что происходило, когда и при каких обстоятельствах
Без логов сложно понять, из-за чего появляется ошибка, если она возникает периодически и только при определенных условиях
Логируем:
- ошибки и исключения
- события и действия пользователей
- запросы к серверу и сторонним ресурсам
- информацию о состоянии приложения
!!!Используем уровни логирования
Персональные данные не логируем
Трейсинг
Инструмент для трассировки и анализа распределенных запросов, благодаря которому можно определить, где происходят сбои, что вызывает низкую производительность, а также как происходит процесс выполнения конкретных запросов
строиться граф распределенных запросов который проще анализировать
сервисы отправляют спаны с единым индетификатором запроса
спан это штука похожая на логи, за етм исключением, что у спана в отличие от лога есть время начала и конца, а также он хранит в себе связь с другими сервисами
Непрерывное профилирование
Метод анализа и измерения производительности программного обеспечения, который позволяет получать данные о работе программы в режиме реального времени
Допустим есть определенный сервис, который приходит к нашему приложению, и с помощью профилировщика снимает метрики памяти и cpu, допустим раз в 5 минут, тогда будет понятно, что так сильно измотало память или cpu перед неисправностью
Как надежно поставлять данные телеметрии?
есть подход связанный с агентами доставки данных телеметрии
агент - это sidecar, sidecar это такой патернн когда у вас например живете в k8s, и у вас рамках пода запущено несколько контейнеров, а вы можете в этот под подселить ещё контейнер который будет отвечать за какую-то инфраструктуру, в нашем случае за сбор телеметрии, ваше приложение будет просто писать в локалхост в какой-то порт и ничего не знать о надежности и всём таком. А ваш агент этот порт будет слушать, и сначала напишет в диск эти данные и потом из диска будет отправлять.