Вам не нужно изучать какую‑либо теорию, кроме этой статьи, чтобы начать собеседоваться. После прочтения смело приступайте к решению типовых System Design задач.
Изучая System Design, вы часто видите только теоретические материалы. В этой статье я постарался показать в том числе практическую реализацию многих вещей, чтобы вы не просто готовились к собеседованиям, но и знали, как эти вещи используются в реальном мире.
Содержание
Зачем изучать проектирование систем?
Что такое сервер?
Задержка и пропускная способность
Масштабирование и его типы
+ Вертикальное
+ ГоризонтальноеАвтоматическое масштабирование
Оценка на коленке
Теорема CAP
Масштабирование базы данных
+ Индексирование
+ Партиционирование
+ Архитектура «master-slave»
+ Multi-master
+ Шардирование
+ Недостатки ШардированияSQL и NoSQL СУБД. Когда какую базу данных использовать?
+ SQL СУБД
+ NoSQL СУБД
+ Особенности масштабирования
+ Когда использовать ту или иную базу данных?Микросервисы
+ Что такое монолит и микросервис?
+ Почему мы разбиваем наше приложение на микросервисы?
+ Когда следует использовать микросервисы?
+ Как клиенты отправляют запросы?Load Balancer
+ Зачем нам нужен балансировщик нагрузки?
+ Алгоритмы балансировщика нагрузкиКэширование
+ Введение в кэширование
+ Преимущества кэширования
+ Типы кэшей
+ Подробное описание RedisХранилище BLOB-объектов
+ Что такое BLOB и зачем нам нужно хранилище BLOB?
+ AWS S3Сеть доставки контента (CDN)
+ Знакомство с CDN
+ Как работает CDN?
+ Ключевые понятия в CDNMessage Broker
+ Асинхронное программирование
+ Зачем мы добавили посредника для передачи сообщений?
+ Queue
+ Stream
+ Кейсы использованияApache Kafka Deep dive
+ Когда использовать Kafka
+ Внутреннее устройство KafkaPub/Sub
Event-Driven Архитектура
+ Введение
+ Зачем использовать EDA?
+ Система нотификаций с id
+ Система с передачей всего состоянияDistributed Systems
Leader Election
Big Data Tools
Consistency Deep Dive
+ Когда использовать Strong Consistency, Eventual Consistency
+ Как добиться Strong, Eventual ConsistencyConsistent Hashing
Data Redundancy and Data Recovery
+ Зачем мы делаем резервные копии баз данных?
+ Различные способы резервного копирования данных
+ Непрерывное резервное копированиеProxy
+ Что такое прокси сервер?
+ Прямой и обратный прокси сервер
+ Создание собственного обратного прокси-сервераКак решить любую проблему, связанную с проектированием системы?
Зачем изучать системный дизайн?
Возможно, ранее вы создавали личные проекты с бэкендом, созданном на любимом фреймворке и базой данных.
Пользователь запрашивает данные в вашем приложение. Затем вы выполняете некоторые вычисления на сервере и операции CRUD в базе данных, чтобы вернуть ответ. Это хорошо для прототипов и личных проектов, но в реальном мире с миллионами реальных пользователей, такая простая архитектура может не сработать. Нам нужно подумать о масштабировании, отказоустойчивости, безопасности, мониторинге и многом другом, чтобы система была надёжной и эффективно работала во всех случаях. Для этого мы изучаем различные концепции проектирования систем.
Что такое Сервер?
Возможно, многие из вас уже знают, что такое сервер, но этот пост для новичков, поэтому я расскажу подробней.
Сервер — это физическая машина (например, ноутбук), на которой выполняется код вашего приложения. Вы создаёте веб‑приложение на ReactJS/NodeJS/Django/... и запускаете его по адресу http://localhost:8080
. Локальный хост — это доменное имя, которое разрешается в IP‑адрес 127.0.0.1
. Это IP‑адрес вашего ноутбука.
Для внешних веб‑сайтов вы вводите https://abc.com
. После нажатия «Enter» реализуется следующий алгоритм:
Идёт обращение к DNS (службе доменных имён), чтобы найти IP‑адрес сервера, соответствующего этому домену. У каждого сервера есть IP‑адрес. Это физический адрес, уникальный для каждого устройства.
Ваш браузер получает IP‑адрес сервера.
С помощью IP‑адреса он отправляет запрос на сервер.
Сервер получает запрос. На сервере работает несколько приложений. (Например, на вашем ноутбуке одновременно работает несколько приложений, таких как Google Chrome, Netflix и т. д.). Сервер находит нужное приложение с помощью порта. Затем возвращает ответ.
Запрос https://abc.com
— это то же самое, что запрос 35.154.33.64:443
. Здесь 443 — это порт (порт https по умолчанию).
Запомнить IP-адреса сложно, поэтому люди обычно покупают для них доменные имена и создаётся маппинг доменного имени на IP-адрес сервера.
Как развернуть приложение в интернете?
Ваше приложение работает на порту 8080. Теперь вы хотите сделать его доступным в интернете, чтобы другие люди смогли заходить на ваш сайт. Для этого вам нужно купить публичный IP-адрес и привязать этот публичный IP-адрес к своему ноутбуку, чтобы люди могли заходить на ваш сайт по адресу http://<your_laptop_ip_address>:8080
.
Делать всё это самостоятельно и управлять сервером - та ещё морока, поэтому обычно арендуют серверы у поставщиков облачных услуг. Они предоставят вам виртуальную машину, на которой вы сможете запустить своё приложение. Эта машина также будет иметь публичный IP-адрес, так что вы сможете получить к ней доступ из любой точки мира.
Задержка и пропускная способность
Задержка
Задержка (Latency) - это время, которое требуется для передачи одного запроса от клиента к серверу и обратно (или для выполнения одной единицы работы). Обычно она измеряется в миллисекундах (мс).
Round Trip Time (RTT) - общее время, которое требуется для отправки запроса на сервер и получения ответа. Иногда RTT используется как синоним задержки.
Пропускная способность
Пропускная способность (Throughput) - это количество запросов или единиц работы, которые система может обрабатывать в секунду. Обычно она измеряется в запросах в секунду (RPS) или транзакциях в секунду (TPS).
У каждого сервера есть лимит. То есть, он может обрабатывать X запросов за раз. Дополнительная нагрузка может привести к его перегрузке или отключению.
Высокая пропускная способность: система может обрабатывать множество запросов одновременно.
Низкая пропускная способность: система с трудом обрабатывает множество запросов одновременно.
В идеальном случае мы хотим создать систему с высокой пропускной способностью и низкой задержкой.
Пример:
Задержка - время, которое требуется автомобилю для проезда из одной точки в другую (например, 10 минут).
Пропускная способность - количество автомобилей, которые могут проехать по шоссе за один час (например, 1000 автомобилей).
Короче говоря,
Задержка измеряет время обработки одного запроса.
Пропускная способность измеряет, сколько запросов может обрабатываться одновременно.
Масштабирование и его типы
Вы часто видите, что когда кто‑то запускает свой сайт в интернете и внезапно увеличивается трафик, он выходит из строя. Чтобы предотвратить это, систему нужно уметь масштабировать.
Масштабирование означает, что нам нужно увеличить характеристики машины (например, объём оперативной памяти, процессор, хранилище и т. д.) или добавить больше машин для обработки нагрузки.
Это можно сравнить с мобильным телефоном: если вы покупаете дешёвый телефон с меньшим объёмом оперативной памяти и хранилища, то он будет зависать при одновременном запуске ресурсоёмких игр или множества приложений. То же самое происходит с инстансом сервера: когда одновременно поступает большой объём трафика, он тоже начинает зависать, и в этот момент нам нужно масштабировать нашу систему.
Типы масштабирования
Масштабирование бывает 2-ух типов:
Вертикальное масштабирование
Горизонтальное масштабирование
Вертикальное масштабирование
Если мы увеличим характеристики (оперативную память, хранилище, процессор) того же компьютера, чтобы он справлялся с большей нагрузкой, то это называется вертикальным масштабированием.
Этот тип масштабирования в основном используется в базах данных SQL, а также в приложениях с отслеживанием состояния, поскольку при горизонтальном масштабировании сложно поддерживать согласованность состояний.
Горизонтальное масштабирование
Вертикальное масштабирование невозможно после определённого момента. Мы не можем бесконечно увеличивать характеристики машины. Мы столкнёмся с узким местом, и после этого не сможем увеличивать характеристики.
Решение этой проблемы заключается в добавлении новых устройств и распределении поступающей нагрузки. Это называется горизонтальным масштабированием.
Пример:
У нас есть 8 клиентов и 2 компьютера, и мы распределяем нагрузку между ними. Мы хотим распределить нагрузку равномерно, чтобы первые 4 клиента могли обращаться к компьютеру 1, а следующие 4 клиента — к компьютеру 2.
Мы не можем дать клиентам 2 разных IP-адреса и позволить самим решать к какому компьютеру обращаться. Для этого мы устанавливаем балансировщик нагрузки. Все клиенты обращаются к балансировщику нагрузки, который отвечает за перенаправление трафика на наименее загруженный сервер.
В этой конфигурации клиенты не отправляют запросы напрямую на сервер. Вместо этого они отправляют запросы на балансировщик нагрузки. Балансировщик нагрузки принимает входящий трафик и перенаправляет его на наименее загруженный сервер.
Большую часть времени в реальном мире мы используем горизонтальное масштабирование.
На изображении ниже видно, что 3 клиента отправляют запросы, а балансировщик нагрузки равномерно распределяет нагрузку между 3 инстансами EC2 (пример оплаченной виртуальной машины в AWS).
Автоматическое масштабирование
Предположим, вы запустили свой бизнес и вывели его в онлайн. Арендовали сервер для развертывания своего приложения. Виртуальный сервер имеет ограничения (процессор, оперативная память и т.д.) на одновременное обслуживание определенного количества пользователей. В это время вы горизонтально масштабируетесь - увеличиваете количество экземпляров приложения, серверов и подключаете балансировщик нагрузки.
Предположим, что один инстанс может обслуживать 1000 пользователей без сбоев. В некоторые дни на сайте 10 000 пользователей, которых могут обслуживать 10 инстансов. В другие дни 100 000 пользователей, и вам нужно 100 инстансов.
Одним из решений может быть постоянное использование максимального количества (100) инстансов. Таким образом, мы можем без проблем обслуживать всех пользователей в любое время. Но в периоды низкой нагрузки мы тратим деньги на дополнительные инстансы. Нам нужно всего 10 инстансов, но мы постоянно запускаем 100 инстансов и платим за это.
Лучшее решение для этого - каждый раз запускать только необходимое количество инстансов. И добавить какой-нибудь механизм, который:
а) Мониторит нагрузку. К примеру, ЦП инстанса
б) Когда она достигает определённого порога (скажем, 90%), он запустит ещё один инстанс и распределит трафик без нашего участия.
Такое динамическое изменение количества серверов в зависимости от трафика называется автоматическим масштабированием.
Примечание: все эти цифры являются гипотетическими. Даны для наглядности. Если вы хотите найти фактический порог, вы можете провести нагрузочное тестирование своего экземпляра.
Оценка на коленке
Выше вы увидели, что нужно больше серверов для обработки нагрузки. Нужно уметь давать приблизительную оценку - необходимое количество серверов, хранилищ и т.д.
На System Design интервью, считается хорошей практикой потратить на это 5 минут (не более). Для облегчения счёта обычно используются приближенные вычисления.
Ниже приведена полезная таблица для таких вычислений. Желательно её запомнить:
Рассчитывать можно множество параметров, я предпочитаю выполнять вычисления только для следующих вещей:
Оценка нагрузки (Load)
Оценка хранилища (Storage)
Оценка ресурсов (Resource)
Давайте возьмем пример Twitter и проведем такой расчет.
Оценка нагрузки
Здесь нужно указать DAU(Daily Active Users - количество ежедневно активных пользователей). Затем рассчитайте количество просмотров и количество публикаций.
Предположим, что у Twitter 100 миллионов ежедневно активных пользователей, и каждый пользователь публикует 10 твитов в день.
Количество твитов за один день:
100 миллионов * 10 твитов = 1 миллиард твитов в день
Это означает, что количество записей = 1 миллиарду твитов в день. Предположим, что 1 пользователь читает 1000 твитов в день.
Количество прочтений за один день:
100 миллионов * 1000 твитов = 100 миллиардов просмотров в день
Оценка объема хранилища
Твиты бывают двух типов:
Обычные твиты
Твиты с фотографиями.
Предположим, что только 10% твитов содержат фотографии. Пусть один твит содержит 200 символов. Размер одного символа составляет 2 байта, а одной фотографии - 2 МБ. Размер одного твита без фотографии = 200 символов * 2 байта = 400 байт ~ 500 байт.
Общее количество твитов за один день = 1 миллиард (как рассчитано выше). Общее количество твитов с фотографиями = 10% от 1 миллиарда = 100 миллионов
Общий объем памяти, необходимый для каждого дня:
=> (Размер одного твита) * (Общее количество твитов) + (Размер фотографии) * (общее количество твитов с фотографией)
=> (500 байт * 1 миллиард) + (2 МБ * 100 миллионов)
Для упрощения расчета возьмите приблизительно:
=> (1000 байт * 1 миллиард) + (2 МБ * 500 миллионов)
=> 1 ТБ + 1 ПБ.
Что составляет приблизительно = 1 ПБ. (игнорируйте 1 ТБ, поскольку это очень мало для 1 ПБ, поэтому добавление его не имеет значения)
Итого - каждый день нам требуется 1 ПБ памяти.
Оценка ресурсов
Здесь рассчитайте общее количество необходимых процессоров и серверов. Предположим, что мы получаем 10 тысяч запросов в секунду, и на обработку каждого запроса процессору требуется 10 мс.
Общее время обработки процессором:
(10 000 запросов в секунду) * (10 мс) = 100 000 мс в секунду.
Если предположить, что каждое ядро процессора может обрабатывать 1000 мс в секунду, то общее количество необходимых ядер:
=> 100 000 / 1000 = 100 ядер. (оригинальный авторский способ подсчёта нагрузки)
Пусть на одном сервере будет 4 ядра процессора.
Таким образом, общее количество необходимых серверов = 100/4 = 25 серверов.
Мы будем использовать 25 серверов с балансировщиком нагрузки для обработки наших запросов.
CAP Теорема
Эта теорема описывает очень важный компромисс при проектировании любой системы.
Аббревиатура CAP состоит из 3 слов:
C: Consistency
A: Availability
P: Partition Tolerance
Все, что мы обсудим в теореме CAP, относится к распределенным системам. Распределенная система означает, что данные хранятся на нескольких серверах.
Почему мы делаем систему распределенной?
Имея несколько серверов, мы можем распределить рабочую нагрузку и обрабатывать большее количество запросов одновременно, повышая общую производительность.
Храните базы данных в разных местах и передавайте данные из ближайшего к пользователю местоположения. Это сокращает время доступа. Отдельный сервер, являющийся частью общей распределенной системы, называют Нодой(Node).
В такой системе мы реплицируем одни и те же данные на разных серверах, чтобы получить вышеуказанные преимущества. Вы можете видеть это на рисунке ниже. Одни и те же данные хранятся на нескольких серверах баз данных (узлах), расположенных в разных местах.
Если данные добавляются в один из узлов, они автоматически реплицируются во все остальные узлы. О том, как происходит эта репликация, мы поговорим позже.
Давайте обсудим все 3 слова CAP:
Согласованность: каждый запрос на чтение возвращает один и тот же результат независимо от того, с какого узла мы считываем данные. Это означает, что все узлы одновременно имеют одни и те же данные. На приведенном выше рисунке вы можете видеть, что наш кластер баз данных согласован, поскольку все узлы имеют одни и те же данные.
Доступность: система доступна и всегда может отвечать на запросы, даже если некоторые узлы выйдут из строя. Это означает, что даже если некоторые узлы выйдут из строя, система должна продолжать обслуживать запросы с помощью других работоспособных узлов.
Устойчивость к разделению: система продолжает работать даже в случае сбоя связи или разделения сети между различными узлами.
Пример: Пусть имеется 3 узла A, B, C.
Согласованность: все A, B и C имеют одинаковые данные. Если в узле B происходит обновление, то происходит репликация данных, и B распространит это обновление на A и C.
Доступность: Представим, что узел B столкнулся с аппаратным сбоем. Узлы A и C все еще работают. Несмотря на сбой узла B, система в целом остается доступной, поскольку узлы A и C все еще могут отвечать на запросы клиентов.
Допуск к разделению: происходит сетевое разделение, которое отделяет B от A и C. Узел B все еще может функционировать и обслуживать запросы, но он не может взаимодействовать с A и C.
Что такое теорема CAP
Теорема CAP гласит, что в распределённой системе можно одновременно гарантировать только два из этих трёх свойств. Все три свойства одновременно обеспечить невозможно.
CA — возможно
AP — возможно
CP — возможно
CAP — невозможно
Это вполне логично, если вдуматься.
В распределённой системе неизбежно возникают сбои в работе сети, поэтому система должна быть устойчива к таким сбоям. Это означает, что в распределённой системе «P» всегда будет присутствовать. Мы будем искать компромисс между CP и AP.
Почему мы можем достичь только CP или AP, а не CAP?
Снова рассмотрим тот же пример с узлами A, B и C.
Предположим, что происходит разделение сети, и B теряет связь с A и C.
Тогда B не сможет передать свои изменения A и C.
Если мы поставим доступность на первое место, то продолжим обслуживать запросы. B не сможет передать свои изменения A и C. Таким образом, пользователи, которым отвечает B, могут получить другие результаты, чем те, которым отвечают A и C. Следовательно, мы добились доступности, пожертвовав согласованностью.
Если мы поставим во главу угла согласованность, то не будем принимать запросы до тех пор, пока не будет устранена проблема с разделением сети B, потому что мы не хотим, чтобы операция записи в B не доходила до узлов A и C. Мы хотим, чтобы в A, B и C были одни и те же данные. Поэтому мы добились согласованности, пожертвовав доступностью.
Почему бы не выбрать CA?
CA означает, что сетевого разделения не происходит, вы можете добиться как согласованности, так и доступности, поскольку связь не является проблемой.
На практике сетевое разделение обязательно произойдет в распределенной системе. Итак, мы выбираем между CP или AP.
Что выбрать, CP или AP?
Для безопасных приложений, таких как банковское дело, платежи, акции и т.д., соблюдайте согласованность. Вы не можете позволить себе показывать противоречивые данные.
Для социальных сетей и т.д. соблюдайте доступность. Если количество лайков к посту не совпадает у разных пользователей, мы в порядке.
На этом первая часть перевода подошла к концу. Позже постараюсь сделать новый подход. Изучим разделы Масштабирование баз данных, SQL и NoSQL СУБД, Микросервисы, Load Balancer.
Каждую из тем можно расширять. Можно привести интересные кейсы из практики. Как писал автор статьи - статья для начинающих. Именно так и стоит её воспринимать - материал, который поможет влиться в эту тематику всем интересующимся. Если материал оказался полезным и хотите продолжения - ставьте лайк, делитесь мнением в комментариях.
Автор перевода — Невзоров Владимир. Приветствую :) Я работаю старшим бэкэнд разработчиком на HighLoad геораспределенном проекте, который защищает крупных клиентов, сервисами которых вы, скорее-всего, пользуетесь на ежедневной основе :) Проект способен держать при этом порядок миллиона RPS.
Мне очень нравится тема System Design. Веду собственный телеграмм канал. Мы вместе с уже крупным сообществом проводим стримы, устраиваем моки для улучшения навыков прохождения System Design Интервью. Недавно провели очередную Архитектурную кату, которая получила отличные отзывы. Делюсь интересными постами из мира бэкэнда.
Если интересуетесь темами Архитектуры, System Design, Highload бэкэнд буду рад приветствовать на канале. Там вы можете обогатиться актуальными знаниями как по прохождению System Design Интервью в БигТех, которые я сам недавно успешно проходил и лайфхаками по которым делюсь. Так и услышать мнения практиков из нашего сообщества. Подписаться.
Если есть желание принять участие в очередной Архитектурной Кате, также милости просим в наш уютный профессиональный чат сообщества. Зайти в чат, рассказать о себе, о намерение принять участие в качестве участника или наблюдателя по ссылке — зайти.
Удачи в дальнейшем изучение темы System Design!
olku
Не подскажете хорошую книжку по System Design на английском? Популярные "System Design Interview" "Grokking the System Design" читал, они плохо систематизированы. То концепции без примеров, то примеры вида "сделать твиттер за 15 минут", то описание конкретных продуктов. Выглядит как компиляция разных статей без структурированного подхода. Например, клиент-сервер это не единственная архитектура. CAP теорема вне проблематики распределенных систем бессмысленна. Качественных характеристик, по которым можно сравнивать те или иные альтернативы, больше 30. Итд... Может есть фундаментальное, но более читабельное Кнута?
griha_shershen
Мне в чате сильных программистов и уже архитекторов посоветовали данное произведение, оригинал на английском, так что должно подходить.
Ричардc М., Форд Н. - Фундаментальный подход к программной архитектуре
Мне очень нравится, обрывочные знания после неё структурировались