У большинства компаний в сфере информационных технологий отдельный тур собеседования посвящён проектированию систем. Кандидатов просят спроектировать масштабируемую систему типа Facebook NewsFeed, Instagram stories, WhatsApp-чата, CI/CD системы и т.д.
В Интернете есть куча материалов для подготовки к собеседованию по проектированию систем. Переварить такое обилие информации может стать непосильной задачей для любого разработчика. Есть, однако, ряд типичных тем, вокруг которых концентрируются вопросы, обсуждаемые на собеседовании в ИТ-компаниях. Чёткое представление об основных принципах и понятиях проектирования систем поможет вам с честью выдержать это непростое испытание.
Попробуем изложить систематизированный подход для выработки стратегии успешного прохождения таких собеседований. Рассмотрим вопросы, которые там поднимаются, и научимся правильно на них отвечать.
Почему проводятся собеседования по проектированию систем?
Сегодня большинство компаний сферы информационных технологий вкладываются в создание масштабируемых высокопроизводительных систем. Поэтому инженер-разработчик должен иметь необходимые навыки проектирования.
Вопросы на собеседовании по проектированию систем задаются с таким расчётом, чтобы дать возможность соискателю проявить себя с разных сторон, позволяя оценить не только его навыки проектировщика, но также общий ход мыслей и умение рассуждать, знание алгоритмов и коммуникативные навыки.
Высокоуровневая стратегия
При прохождении собеседования по проектированию систем предлагаю вам придерживаться подхода вот с такой пятиступенчатой структурой:
- требования и уточняющие вопросы;
- ёмкость системы;
- цели проектирования;
- проектирование API и алгоритмы;
- база данных, механизм кэширования и разделение данных.
Рассмотрим каждый из пунктов подробнее.
Требования и уточняющие вопросы
Когда нам предлагается спроектировать серверную систему для Uber, на ум сразу приходит целая куча разных функциональных средств, свойств и возможностей. Однако надо понимать, что системы типа Uber или Facebook прошли долгий путь развития, прежде чем сформировались в своём нынешнем виде, чему способствовал самоотверженный и многолетний труд проектировщиков. Поэтому спроектировать широкомасштабную систему со всеми её свойствами и особенностями за 45–60 минут не представляется возможным. Как же быть?
Наше решение — сузить рамки задачи. Требования распределяем на функциональные и нефункциональные. Все основные свойства и особенности можно расписать на интерактивной доске или набросать на листочке в процессе собеседования.
Например, в случае с проектированием серверной части для Uber можно составить такой упрощённый список требований:
- Пассажир должен иметь возможность заказать такси и при необходимости отменить поездку.
- Система должна сводить вместе водителей и пассажиров.
- Таксист вправе отменить поездку.
- Водитель и пассажир по окончании поездки могут обменяться оценками.
- Пассажир может выбрать способ оплаты.
Кроме того, необходимо задавать уточняющие вопросы. В нашем примере нужно выяснить, кто будет пользоваться системой, как эти пользователи будут с ней взаимодействовать и каковы особенности использования системы. Такие вопросы помогут выявить узкие места, устранить всё лишнее и масштабировать систему.
Ёмкость системы
Уточнив требования, можно перейти к следующему вопросу: сколько пользователей будет у системы? Ответ на этот важный вопрос поможет устранить многие узкие места проекта.
Зная хотя бы приблизительное количество активных пользователей, можно оценить, сколько система будет получать запросов и сколько памяти потребуется для хранения данных. Дальше можно выяснить, предназначена ли система больше для чтения или для записи. От этого будет зависеть выбор хранилища данных (будет это реляционная база данных или нереляционная), и здесь же можно решить, нужен ли в вашем приложении слой кэширования.
Предположим теперь, что вам надо спроектировать систему по типу твиттера. В этом случае потребуется собрать данные о:
- минимум 200 активных пользователях с более чем 600 твитами в день;
- всех пользователях, имеющих в среднем по 100 подписчиков;
- твитах, процитированных хотя бы 10 раз;
- людях, выкладывающих по одному медиафайлу на каждые 10 твитов.
На основе этой информации можно получить представление о степени горизонтального масштабирования или необходимом количестве серверов в системе. К тому же, имея приблизительную оценку объёма данных для хранения, будет легче планировать серверы баз данных.
Цели проектирования
Одними из основных требований, предъявляемых к любой системе, являются низкая временная задержка и высокая пропускная способность. Временная задержка — это количество времени, уходящее на обработку запроса клиента. Пропускная способность — это количество запросов, обрабатываемых за определённый промежуток времени.
У высокопроизводительной системы должна быть минимальная временная задержка и максимальная пропускная способность. Предположим, у сайта интернет-магазина, который вы проектируете, много времени уходит на загрузку. Пользователи вряд ли будут этим довольны. Недопустимо, чтобы в самый ответственный момент — в день распродажи — производительность вашей системы упала из-за возросшей нагрузки.
Вторая цель проектирования — выбрать между согласованностью данных и их доступностью. Согласно теореме Брюера, у вас может быть либо система CP, либо система AP для обеспечения устойчивости к сбоям сети. Выбор между согласованностью данных и их доступностью зависит от типа проектируемой системы.
Допустим, у вас система для банков, которая непосредственно имеет дело с деньгами пользователей. Здесь первостепенную важность приобретает согласованность данных. Если же вы проектируете Facebook feed, то можно обойтись без обновления состояния страницы или статуса друга.
Проектирование API и алгоритмов
Для каждого требования в рамках задачи необходимо изобразить базовые API, которые будут предоставляться серверной системой. Здесь можно упомянуть о применяемых запросах, откликах, идентификаторах состояния, методе HTTP и т.д. В зависимости от поставленной задачи можно обсудить целесообразность использования REST, GraphQL или GRPC для API.
Вы можете даже схематично представить высокоуровневые компоненты, которые будут использованы в системе. Начать лучше с клиентских компонентов, то есть API-шлюза и балансировщиков нагрузки. Можно поговорить о том, какие стратегии неплохо было бы использовать в балансировщиках нагрузки для равномерного распределения её среди серверов приложения.
Если вас попросят спроектировать систему по типу Youtube, Netflix или Instagram, можно будет сказать пару слов о CDN (сети доставки содержимого), обеспечивающей доставку статических изображений или видеофайлов.
Для любого API нужно решить, реализовывать ли алгоритм на серверной стороне или это будет обычный CRUD API. Например, при проектировании твиттера для ленты новостей необходимо будет придумать её алгоритм. В случае если один пользователь подписан на другого, это будет обычный CRUD API.
База данных, механизм кэширования и разделение данных
Зная ёмкость системы, вы можете прикинуть, какой объём данных система будет обрабатывать. Вы также должны иметь чёткое представление о сущностях, используемых в моделировании данных, и об отношениях между ними. Нужно определиться с тем, какие у вас данные — структурированные или неструктурированные — и решить, нужна ли им гибкость схемы. От этого будет зависеть выбор вашей базы данных (будет это реляционная база данных или нереляционная).
Большинство систем предназначено больше для чтения. Ускорить все запросы на чтение в базе данных вы можете, создав индекс в колонке или наборе колонок, в зависимости от организации доступа. Можете сделать архитектуру типа «ведущий-ведомый», где записи выполняются на стороне ведущего, а чтение — ведомого. Так вы отделите чтение от записей и улучшите производительность системы.
Запросы к базе данных на чтение приводят к операциям ввода-вывода, выполнение которых сопряжено с большими вычислительными затратами. Чтобы минимизировать ввод-вывод с диска, между приложением и базой данных можно добавить слой кэширования. Это улучшит производительность системы в будущем.
При добавлении кэша нужно будет выбрать стратегию вытеснения из кэша, а также шаблоны кэширования, такие как сквозная запись, отложенная запись, кэш на стороне и т.д.
При проектировании системы, рассчитанной на миллионы пользователей, данные не поместятся на одном сервере баз данных. А потому важно распределить данные по нескольким серверам. Это так называемое секционирование данных. На собеседовании вас могут спросить о стратегиях секционирования, используемых для распределения данных, и попросить рассказать о плюсах и минусах каждой из них.
Развитие навыков проектирования систем требует много времени, опыта и сил. Почерпнуть для себя много полезного из этой сферы можно в специализированных блогах ИТ-компаний, где очень интересно описываются методы решения сложных задач.
Вы тоже можете продемонстрировать своё умение справляться с задачами проектирования. Добро пожаловать на форумы LeetCode и Carrer cup, где можно найти единомышленников, также желающих внести свою лепту в решение задач или предложить какой-то свой альтернативный подход!
Теперь, имея представление об основах распределённых систем и применяя изложенный в статье структурированный подход, вам будет легче пройти собеседование по проектированию систем в любой компании из сферы информационных технологий. Остаётся немного поднакопить опыта.
Полезные ссылки
- Проектирование систем для начинающих
- Educative.io — как пройти собеседование по проектированию систем
- Interview bit — курс по проектированию систем
- API-шлюз и балансировщики нагрузки
Читайте также:
- Что может помешать разработчику самостоятельно создать успешное приложение
- 5 мудрых цитат известнейших программистов
- Как добиться большей зарплаты на собеседовании
Перевод статьи Animesh Gaitonde: System Design Interviews 101