System Design 101
О сложных системах простыми словами.
В шпаргалке на высоком уровне рассматриваются такие вещи, как протоколы коммуникации, DevOps, CI/CD, архитектурные паттерны, базы данных, кэширование, микросервисы (и монолиты), платежные системы, Git, облачные сервисы etc. Особую ценность представляют диаграммы - рекомендую уделить им пристальное внимание.
Выражаю благодарность Анне Неустроевой за помощь в редактировании материала.
System Design (сборник на английском языке).
Протоколы
Архитектура (дизайн) определяет, как разные компоненты приложения взаимодействуют друг с другом. Она обеспечивает эффективность, надежность и легкость интеграции с другими системами путем предоставления стандартного подхода к проектированию и разработке API. Наиболее популярными архитектурами являются следующ ие:
- SOAP:
- зрелый, всесторонний, основанный на XML
- хорошо подходит для корпоративных приложений
- REST:
- популярный, легкий в реализации, основанный на методах HTTP
- хорошо подходит для веб-сервисов
- GraphQL:
- язык запросов (query language), позволяющий запрашивать данные избирательно (частично)
- меньше нагрузка на сеть, более быстрые ответы от сервера
- gRPC:
- современный, высокопроизводительный, основанный на буферах протоколов (protocol buffers)
- хорошо подходит для микросервисов
- WebSocket:
- двусторонний, позволяет обмениваться данными в реальном времени, соединение остается открытым
- хорошо подходит для обмена данными небольшого размера
- Webhook:
- основанный на событиях и коллбэках HTTP, асинхронный
- хорошо подходит для систем уведомлений
REST и GraphQL
Сравнение REST и GraphQL:
- GraphQL – это язык запросов для API, разработанный Meta. Он предоставляет полное описание данных в API и позволяет клиенту запрашивать только то, что ему нужно
- сервер GraphQL является посредником между клиентом и сервером. GraphQL может агрегировать несколько запросов REST в один запрос (и request, и query в переводе на русский означают "запрос"). Сервер GraphQL организует ресурсы в граф (отсюда и название)
- GraphQL поддерживает запросы, мутации (модификация ресурсов) и подписки (получение уведомлений о модификациях)
gRPC
RPC (Remote Procedure Call – удаленный вызов процедур) называется "удаленным", поскольку обеспечивает взаимодействие между удаленными сервисами, когда они находятся на разных серверах в микросервисной архитектуре. С точки зрения пользователя это выглядит, как вызов локальной функции.
На диаграмме представлен поток данных (flow) gRPC:
- Клиент отправляет REST-запрос. Тело запроса (request body) содержит данные в формате JSON (как правило). 2-4. Сервис заказов (order service) (клиент gRPC) получает запрос, преобразует его и отправляет запрос RPC сервису оплаты (payment service) (сервер gRPC). gRPC кодирует данные клиента в двоичный формат и отправляет их в низкоуровневый транспортный слой (transport layer).
- gRPC отправляет пакеты данных (data packages) по сети с помощью HTTP2. Благодаря двоичной кодировке и сетевым оптимизациям gRPC может быть до 5 раз быстрее JSON. 6-8. Сервис оплаты получает пакеты данных по сети, декодирует их и вызывает серверное приложение. 9-11. Результат, полученный от серверного приложения, кодируется и отправляется в транспортный слой. 12-14. Сервис заказов получает пакеты данных, декодирует их и отправляет результат клиентскому приложению.
Webhook
Сравнение Polling (опроса) и Webhook:
Предположим, что у нас есть электронный магазин. Клиенты отправляют заказы в сервис заказов через шлюз API (API Gateway), а сервис заказов обращается к сервису оплаты для выполнения денежных транзакций. Сервис оплаты, в свою очередь, обращается к внешнему провайдеру сервиса оплаты (Payment Service Provider, PSP) для завершения транзакции.
Существует 2 способа взаимодействия с внешним PSP.
- Короткий опрос.
После отправки платежного запроса PSP, сервис оплаты продолжает опрашивать PSP о статусе платежа (путем периодической отправки запросов) до тех пор, пока PSP не сообщит о завершении операции.
Короткий опрос имеет следующие недостатки:
- постоянные запросы статуса расходуют ресурсы сервиса оплаты
- внешний сервис взаимодействует напрямую с сервисом оплаты, что создает уязвимости безопасности
- Веб-хуки
Веб-хук регистрируется во внешнем сервисе. Мы как бы просим внешний сервис сообщить нам об изменениях по запросу по указанному URL. После выполнения операции, PSP отправляет запрос HTTP для обновления статуса платежа.
Ресурсы сервиса оплаты больше не расходуются на опрос.
Веб-хуки часто называют реверсивными (reverse) API или push-API, поскольку сервер отправляет запрос HTTP клиенту, а не наоборот.
При использовании веб-хуков следует уделять пристальное внимание следующим вещам:
- API должно быть правильно спроектировано для взаимодействия с внешним сервисом
- в шлюзе API должны быть установлены определенные правила безопасности
- во внешнем сервисе следует регистрировать правильный URL
Производительность API
5 распространенных способов улучшения производительности API:
Пагинация
Эту оптимизацию применяют в случае большого объема данных. Результат отправляется клиенту по частям (чанкам - chunks) для улучшения отзывчивости сервиса.
Асинхронное логирование
Синхронное логирование работает с диском при каждом вызове и может замедлить систему. Асинхронное логирование отправляет логи в буфер без блокировки (lock-free buffer) и сразу возвращается. Логи записываются на диск с определенной периодичностью. Это существенно снижает нагрузку на ввод/вывод.
Кэширование
Мы можем сохранять часто запрашиваемые данные в кэше и возвращать их клиентам без повторного обращения к базе данных. Доступ к данным, хранящемся в памяти Redis, например, гораздо быстрее, чем доступ к БД.
Сжатие полезной нагрузки
Запросы и ответы могут сжиматься с помощью GZIP и других алгоритмов сжатия. Чем меньше размер данных, тем быстрее они передаются по сети. Таким образом, сжатие ускоряет загрузку и скачивание данных.
Пул подключений
При доступе к ресурсам нам часто приходится загружать данные из БД. Открытие нового подключения к БД – дорогая операция, с точки зрения производительности, поэтому для доступа к БД следует использовать пул открытых (набор существующих) подключений (connection pool). Пул подключений отвечает за управление жизненным циклом соединений.
HTTP 1.0 -> HTTP 1.1 -> HTTP 2.0 -> HTTP 3.0 (QUIC)
- HTTP 1.0 был завершен и полностью задокументирован в 1996. Каждый запрос к серверу требует отдельного соединения TCP
- HTTP 1.1 был опубликован в 1997. TCP-соединение может оставаться открытым для повторного использования (постоянное подключение), но проблема блокировки HOL (head-of-line) остается. Блокировка HOL означает, что когда исчерпан лимит параллельных запросов, новые запросы ждут завершения предыдущих
- HTTP 2.0 был опубликован в 2015. Он решает проблему HOL путем мультиплексирования запросов на уровне приложения (application layer), но HOL остается на транспортном уровне (transport layer, например, TCP). Как видно на диаграмме, HTTP 2.0 представил концепцию "потоков" (streams) HTTP - абстракция, позволяющая разным запросам HTTP использовать одно соединение TCP. Потоки могут отправляться в разном порядке
- первый черновик HTTP 3.0 был опубликован в 2020. В качестве нижележащего транспортного протокола вместо TCP в нем используется QUIC, что решает проблему HOL в транспортном слое
QUIC основан на UDP. Он обеспечивает первоклассную поддержку потоков в транспортном слое. Потоки QUIC используют одно соединение QUIC, поэтому не требуется затрат на рукопожатия (handshakes) и холодные запуски для создания новых соединений. Потоки QUIC доставляются независимо, поэтому в большинстве случаев потеря пакетов в одном потоке не влияет на пакеты в другом потоке.
SOAP, REST, GraphQL и RPC
Существуют разные архитектурные стили API, каждый со своими паттернами и стандартами обмена данными:
Сначала код и сначала API
Разница между подходами к разработке "Сначала код" и "Сначала API" (Code First, API First):
- Микросервисы повышают сложность системы. Разные функции системы обслуживаются отдельными сервисами. Хотя такая архитектура облегчает разделение обязанностей, реализация взаимодействия между сервисами является дополнительным вызовом.
При написании кода следует помнить о сложности системы и аккуратно определять границы (зоны ответственности) сервисов.
- Отдельные команды разработчиков должны говорить на одном языке. Каждая ко манда отвечает только за свои компоненты и сервисы. Рекомендуется заранее проектировать дизайн API на уровне организации.
Для валидации дизайна API перед написанием кода можно использовать фиктивные запросы и ответы.
- В целом, микросервисная архитектура повышает качество ПО и продуктивность разработчиков. Грамотно спроектированное API позволяет быстрее запускать проект и делает процесс разработки более плавным.
Улучшается опыт разработки, поскольку разработчики могут сосредоточиться на реализации функционала вместо того, чтобы постоянно заниматься настройкой и интеграцией.
Снижается вероятность возникновения неприятных сюрпризов на последних этапах жизненного цикла проекта.
Наличие спроектированного API позволяет писать тесты, не дожидаясь разработки. Отсюда один шаг к разработке на основе тестов (Test Driven Design, TDD).
Коды статусов HTTP
Коды ответов(статусов) HTTP делятся на 5 категорий:
- Информационные (100-199).
- Коды успеха (200-299).
- Коды перенаправления (300-399).
- Коды ошибок на стороне клиента (400-499).
- Коды ошибок на стороне сервера (500-599).
Шлюз API
- Клиент отправляет запрос HTTP в шлюз API (API Gateway).
- Шлюз разбирает (парсит) и проверяет атрибуты запроса.
- Шлюз выполняет проверки по белому/черному списку ресурсов.
- Шлюз обращается к провайдеру идентификации (поставщику удостоверений – Identity Provider) для аутентификации и авторизации.
- К запросу применяются правила ограничения частоты запросов (rate limit). При превышении лимита запрос отклоняется. 6 и 7. После выполнения проверок шлюз определяет сервис, совпадающий с путем роута (route path).
- Шлюз преобразует запрос в подходящий протокол и передает его серверным микросервисам. 9-12. Шлюз отвечает за обработку ошибок, а также провалов, связанных с тем, что решение проблемы т ребует больше времени (разрыв цепочки - circuit break). Для логирования и мониторинга здесь также может использоваться стек ELK (Elastic-Logstash-Kibana). Здесь же можно реализовать кэширование данных.
Эффективное и безопасное API
Типичные проекты/схемы API на примере корзины товаров:
Обратите внимание, что дизайн API – это не только дизайн URL. Необходимо правильно выбирать названия ресурсов, идентификаторы и паттерны путей (path patterns). Также важно устанавливать правильные заголовки HTTP и эффективные правила ограничения частоты запросов (rate limit).
Инкапсуляция TCP/IP
Как данные передаются по сети? Почему в сетевой модели OSI (Open Systems Interconnection – взаимосвязь открытых систем) так много уровней?
На диаграмме показано, как данные инкапсулируются и распаковываются при передаче по сети.
- Когда устройства А передает данные устройству Б по протоколу HTTP, сначала на уровне приложения (application layer) в запрос добавляется заголовок HTTP.
- Затем к данным добавляются заголовки TCP или UDP. Они инкапсулируются в сегменты TCP на транспортном уровне (transport layer). Заголовок содержит порт источника (source port), порт назначения (destination port) и номер последовательности (sequential number).
- Затем на сетевом уровне (network layer) добавляется заголовок IP. Он содержит адреса IP источника/назначения.
- На уровне связи данных (data link layer) в датаграмму (datagram) IP добавляется заголовок MAC с MAC-адресами источника и получателя.
- Инкапсулированные кадры (фреймы – frames) попадают на физический уровень (physical layer) и передаются по сети в двоичном виде. 6-10. При получении битов по сети устройство Б выполняет процесс распаковки, обратный процессу инкапсуляции. Заголовки удаляются слой за слоем, после чего устройство Б может читать данные.
У каждого уровня своя задача. Каждый уровень извлекает инструкции из соответствующего заголовка, ч то избавляет от необходимости владеть полной информацией о данных.
Почему NGINX называют "обратным" прокси?
Разница между прямым (forward) и обратным (reverse) прокси:
Прямой прокси – это сервер, находящийся между пользователем и Интернетом.
Прямой прокси обычно используется для:
- Защиты клиентов.
- Преодоления ограничений браузера.
- Блокировки доступа к определенным ресурсам.
Обратный прокси – это сервер, принимающий запросы от клиента, передающий их веб-серверу и возвращающий результат клиенту после обработки запроса сервером.
Обратный прокси хорошо подходит для:
- Защиты сервера.
- Балансировки нагрузки.
- Кэширования статических ресурсов.
- Кодирования и декодирования подключений SSL.
Алгоритмы баланс ировки нагрузки
6 наиболее распространенных алгоритмов балансировки нагрузки:
Статические алгоритмы
- Циклический (round robin) – запросы клиента передаются разным экземплярам сервиса по порядку. Как правило, сервисы не владеют состоянием (stateless).
- Липкий (sticky) циклический – улучшение циклического алгоритма. Если первый запрос Алисы передается сервису А, последующие запросы Алисы также передаются этому сервису.
- Взвешенный (weighted) циклический – администратор присваивает веса каждому сервису. Чем больше вес, тем больше запросов может обработать сервис.
- Хэш – данный алгоритм применяется функцию хэширования к IP или URL входящего запроса. Хэш определяет сервис, которому передается запрос.
Динамические алгоритмы
- Наименьшее количество соединений (least connections) – новый запрос передается экземпляру с наименьшим количеством параллельных подключений.
- Наименьшее вр емя ответа (least response time) – новый запрос передается экземпляру с наименьшим временем ответа.
URL, URI и URN
Сравнение URL, URI и URN:
URI
URI расшифровывается как Uniform Resource Identifier (единый идентификатор ресурса). Он определяет логический или физический ресурс в вебе. URL и URN являются подтипами URI. URL определяет локацию (местонахождения) ресурса, а URN - название ресурса.
URI состоит из следующих частей:
URL
URL расшифровывается как Uniform Resource Locator (единый указатель ресурсов) и является ключевой концепцией HTTP. Он представляет собой уникальный адрес ресурса в вебе. URL может использоваться с другими протоколами, такими как FTP и JDBC.
URN
URN расшифровывается как Uniform Resource Name (единое имя ресурса). URN не могут использоваться для локализации ресурса. Простым примером является сочетание пространства имен (namespace) и специфичной для него строки.
URIs, URLs, and URNs: Clarifications and Recommendations 1.0.
CI/CD
CI/CD простыми словами
SDLC с CI/CD
SDLC (software development life cycle – процесс разработки ПО) состоит из нескольких основных этапов: разработка, тестирование, деплой и поддержка. CI/CD автоматизирует и интегрирует эти этапы для обеспечения более быстрых и надежных релизов.
Отправка ("пуш") кода в репозиторий запускает сборку и тесты. Запускаются тесты непрерывной цепочки (End-to-end, e2e) для валидации кода. Если тестирование проходит успешно, код автоматически разворачивается в стейдже/продакшне. В противном случае, код возвращается на доработку. Эта автоматизация обеспечивает получение быстрой обратной связи разработчиками и снижает риск попадания багов в продакшн.
Разница между CI и CD
Continuous Integration (CI) (непрерывная интеграция) автоматизирует процессы сборки, тестирования и объединения ("мержа"). Она запускает тесты при каждой фиксации ("коммите") кода для обнаружения проблем с интеграцией на ранних стадиях. Это способствует частой фиксации кода и быстрой обратной связи.
Continuous Delivery (CD) (непрерывная доставка) автоматизирует процессы релиза, такой как изменение инфраструктуры и деплой. Она обеспечивает надежность релизов в любое время с помощью автоматизированного конвейера. CD может также включать необходимость ручного тестирования и подтверждения ("апрува") перед деплоем в продакшн.
Конвейер CI/CD
Типичный конвейер (pipeline) CI/CD состоит из нескольких этапов:
- разработчик фиксирует изменения в коде
- сервер CI регистрирует изменения и запускает сборку
- код компилируется и тестируется (юнит-тесты, интеграционные тесты etc.)
- отчет о тестировании отправляется разработчику
- при успешном тестировании артефакты разворачиваются в промежуточной среде (стейдж)
- перед релизом могут запускаться дополнительные тесты
- система CD разворачивает утвержденные изменения в продакшне
Технический стек Netflix (конвейер CI/CD)
Планирование: инженеры Netflix используют JIRA для планирования и Confluence для документации.
Написание кода: основным серверным языком является Java, другие языки используются для разных целей по мере необходимости.
Сборка: для сборки в основном используется Gradle, для поддержки разных случаев разработаны специальные плагины Gradle.
Упаковка: пакет и зависимости помещаются в Amazon Machine Image (AMI) для релиза.
Тестирование: подчеркивает направленность культуры продакшна на создание инструментов хаоса.
Деплой: Netflix использует собственный Spinnaker для деплоя.
Мониторинг: метрики мониторинга аккумулируются в Atlas, а для обнаружения аномалий используется Kayenta.
Отчет об инциденте: инциденты сортируются по приоритету, для обработки инцидентов используется PagerDuty.
Архитектурные паттерны
MVC, MVP, MVVM, MVVM-C и VIPER
- MVC, самый старый паттерн, появился почти 50 лет назад
- каждый паттерн имеет "view" (V), отвечающий за отображение содержимого и обработку пользовательского ввода
- большинство паттернов включает "model" (M) для управления данными
- "controller", "presenter" и "view-model" являются посредниками между слоем отображения и моделью
18 основных архитектурных паттернов
Паттерны – это многократно используемые решения распространенных проблем дизайна, обеспечивающие более плавный и эффективный процесс разработки. Они являются проектами/схемами для разработки лучших структур ПО. Наиболее популярными паттернами являются следующие:
- Abstract Factory (абстрактная фабрика): создатель семьи – создает группы связанных элементов
- Builder (строитель): мастер лего – создает объекты поэтапно, разделяет создание и отображение
- Prototype (прототип): создатель клонов – создает копии полностью готовых объектов
- Singleton (одиночка): единственный и неповторимый – специальный класс, который может иметь только один экземпляр
- Adapter (адаптер): универсальный плагин – соединяет вещи с разными интерфейсами
- Bridge (мост): подключатель функций – связывает объекты с функциями
- Composite (композитор): строитель деревьев – формирует древовидные структуры из простых и сложных частей
- Decorator (декоратор): настройщик – добавляет возможности в объекты без изменения их ядра
- Facade (фасад): единый центр обслуживания – представляет целую систему с помощью одного простого интерфейса
- Flyweight (легковес): хранитель пространства – эффективно распределяет небольшие повторно используемые элементы
- Proxy (прокси): запасной актер – представляет другой объект, управляет доступом к нему или выполнение операций с ним
- Chain of Responsibility (цепочка ответственности): эстафета запроса – запрос обрабатывается цепочкой объектов
- Command (команда): обертка задачи – преобразует запрос в объект, готовый к выполнению операции
- Iterator (итератор): исследователь коллекций – предоставляет доступ к коллекции элементов по одному
- Mediator (посредник): коммуникационный хаб – упрощает взаимодействие между разными классами
- Memento (запоминатель): капсула времени – сохраняет и восстанавливает состояние объекта
- Observer (наблюдатель): издатель новостей – уведомляет классы об изменениях других объектов
- Visitor (посетитель): умелый гость – добавляет новые операции в класс без его модификации
База данных
Выбор правильной БД для проекта – задача не из простых. Изучение огромного количества возможностей БД, рассчитанных на решение конкретных задач, может быстро утомить.
Надеемся, что наша диаграмма, как минимум, облегчит начало вашего пути в этом направлении.
8 структур данных, улучшающих работу баз данных
Ответ на этот вопрос зависит от того, как БД используется. Индексы данных могут храниться в памяти или на диске. Форматы данных могут быть разными: числа, строки, географические координаты etc. Система может часто читаться или писаться (write-heavy, read-heavy). Все эти факторы влияют на выбор формата индексов БД.
Наиболее популярные структуры данных, использующиеся для индексации:
- Skiplist: индексы хранятся в памяти. Используется в Redis
- Hash index (хэшированный индекс): популярная реализация структуры данных "Карта" (или "Коллекция") (map)
- SSTable: иммутабельная (неизменяемая) реализация "Карты", хранящаяся на диске
- LSM дерево: Skiplist + SSTable. Хорошо подходит для частой записи
- B-дерево: индексы хранятся на диске. Согласованная производительность чтения/записи
- Inverted index (инвертированный индекс): хорошо подходит для индексации документов. Используется в Lucene
- Suffix tree (суффиксное дерево): хорошо подходит для поиска строковых паттернов (string patterns)
- R-дерево: разнонаправленный поиск, такой как поиск ближайшего соседа
Выполнение инструкции SQL в базе данных
Разные БД имеют разную архитектуру. Некоторые общие подходы:
- Инструкция SQL отправляется в БД через транспортный протокол (например, TCP).
- Инструкция передается парсеру команды (command parser), который выполняет ее синтаксический и семантический анализ и генерирует дерево запроса (query tree).
- Дерево запроса передается оптимизатору (optimizer). Оптимизатор создае т план выполнения (execution plan).
- План выполнения передается исполнителю (executor). Исполнитель извлекает данные для выполнения.
- Методы доступа (access methods) предоставляют логику получения данных, необходимых для выполнения, извлечения данных из движка хранилища (storage engine).
- Методы доступа определяют, является ли инструкция доступной только для чтения. Если да (SELECT), инструкция передается менеджеру буфера (buffer manager) для дальнейшей обработки. Менеджер буфера ищет данные в кэше или файлах.
- Если инструкцией является UPDATE или INSERT, она передается менеджеру транзакций (transaction manager) для дальнейшей обработки.
- В ходе транзакции данные блокируются (lock mode). За это отвечает менеджер блокировки (lock manager). Блокировка обеспечивает соответствие транзакции принципам ACID.
Теорема CAP
Теорема CAP является одним из самых известных терминов в компьютерной науке, но разные разработчики понимают ее по-разному.
Теорема CAP утверждает, что распределенная система не может одновременно соответствовать всем трем критериям.
Consistency (согласованность): все клиенты видят одни и те же данные, независимо от того, к какому узлу (node) они подключены.
Availability (доступность): любой клиент, запросивший данные, получает ответ, даже если какие-то узлы вышли из строя.
Partition Tolerance (устойчивость к разделению): разделение означает нарушение коммуникации между двумя узлами. Устойчивость означает, что система продолжает работать, несмотря на разделения.
Формулировка "2 из 3" может быть полезной, но также может вводить в заблуждение.
- Выбор базы данных – нелегкая задача. В данном случае нельзя опираться только на теорему CAP. Например, компании не выбирают Cassandra для приложений чата просто потому, что Cassandra является системой AP. Однако Cassandra имеет характеристики, которые делают ее неплохим выбором для хранения сообщений (см. историю Discord в конце).
- CAP ограничивает лишь небольшую часть возможностей: идеальная доступность и согласованность при наличии разделения, которое встречается довольно редко.
- Теорема утверждает, что возможна 100% доступность и согласованность. На самом деле, речь чаще всего идет о выборе между задержкой и согласованностью при отсутствии сетевого разделения. См. теорему PACELC.
Является ли теорема CAP полезной?
Я думаю, что да, поскольку она открывает дискуссию о компромиссах, но это лишь часть всей истории. При выборе БД нам необходимо погружаться в детали с точки зрения функционала приложения.
Типы памяти и хранилищ данных
Визуализация запроса SQL
Инструкции SQL выполняются БД следующим образом:
- разбор инструкции и ее валидация
- преобразование SQL во внутренний формат, такой как реляционная алгебра (relational algebra)
- оптимизация внутренне го представления и создания плана выполнения (execution plan) на основе индексации
- выполнение плана и возвращение результата
Выполнение инструкции SQL является сложным процессом, включающем множество соображений, таких как:
- использование индексов и кэшей
- порядок объединения таблиц
- управление конкурентностью
- управление транзакциями
Язык SQL
SQL (Structured Query Language – язык структурированных запросов) был стандартизирован в 1986. В течение следующих 40 лет он стал доминирующим языком для систем управления реляционными базами данных. Последний стандарт называется ANSI SQL 2016.
SQL включает в себя 5 основных компонентов:
- DDL: data definition language (язык определения данных) - CREATE, ALTER, DROP
- DQL: data query language (язык запроса данных) - SELECT
- DML: data manipulation language (язык манипуляции данными) - INSERT, UPDATE, DELETE
- DCL: data control language (язык управления данными) - GRANT, INVOKE
- TCL: transaction control language (язык управления транзакциями) - COMMIT, ROLLBACK
Разработчик бэк енда должен хорошо знать все эти компоненты. Аналитик данных может ограничиться DQL. Все зависит от того, чем вы занимаетесь.
Кэш
Кэширование данных
Кэширование данных в типичной архитектуре:
Кэширование данных выполняется на нескольких уровнях:
- Клиентские приложения: ответы HTTP могут кэшироваться браузером. Мы запрашиваем данные по HTTP в первый раз, данные возвращаются с политикой истечения срока действия (expiry policy) в заголовке HTTP. Мы запрашиваем данные повторно, клиентское приложение пытается извлечь данные из кэша браузера.
- CDN (Content Delivery Network – сеть доставки контента): CDN кэшируют статические веб-ресурсы. Клиенты могут извлекать данные из ближайшего узла CDN.
- Балансировщик нагрузки (Load Balancer): балансировщик нагрузки также может кэшировать данные.
- Брокер сообщений (Message Broker): брокер с начала записывает сообщения на диск, потребители извлекают их по необходимости. Данные кэшируются в кластерах Kafka, например, в течение времени, определяемого политикой удержания (retention policy).
- Сервисы: в сервисах существуют разные уровни кэша. Если данные не кэшированы в кэше CPU, сервис пытается извлечь данные из памяти. Иногда имеется второй слой кэша с хранением данных на диске.
- Распределенный кэш (Distributed Cache): Redis, например, хранит в памяти пары ключ-значение для различных сервисов. Операции чтения/записи в Redis выполняются значительно быстрее по сравнению с базой данных.
- Полнотекстовый поиск (Full-text Search): Elastic, например, хорошо подходит для поиска документов или отчетов (логов). Поисковый движок выполняет индексацию копии данных.
- БД: в БД существуют разные уровни кэша:
- WAL (Write-ahead Log – журнал упреждающей записи) – данные записываются в WAL перед построением B-дерева
- Bufferpool – область памяти, выделяемая для кэширования результатов запросов
- материализованное представление (Materialized View) – предварительное вычисление результатов запросов и их сохранение в таблицах БД для повышения производительности
- журнал транзакций (Transaction Log) – фиксация всех транзакций и обновлений БД
- журнал репликации (Replication Log) – фиксация состояния репликации кластера БД
Причины высокой производительности Redis
- Redis – это хранилище данных, основанное на RAM. Доступ к оперативной памяти как минимум в 1000 раз быстрее доступа к диску.
- Для повышения эффективности выполнения запросов в Redis используется мультиплексирование ввода/вывода и однопоточный цикл выполнения.
- В Redis используется несколько эффективных низкоуровневых структур данных.
Другим популярным решением для хранения данных в памяти является Memcached.