Кратко

Паттерны

Самые распространенные паттерны для интервью по System Design

Используя технологии и основные понятия, которые мы уже обсудили, и комбинируя их, вы можете построить самые разные системы. Но интервью ограничено по времени и успех в нем во многом зависит от вашего умения быстро распознавать и применять паттерны проектирования. Если вы хорошо знаете паттерны, вы не только опираетесь на лучшие практики, но и экономите время, не изобретая велосипед.

Способность выявлять и применять паттерны - это навык, который часто отличает Senior кандидатов от Middle кандидатов на собеседовании по System Design. Паттерны позволяют вам определить, что может быть интересно на интервью и на обсуждении чего стоит сфокусироваться.

Общая структура

Ниже приведены некоторые распространенные паттерны, которые можно использовать при проектировании систем. Эти паттерны не взаимоисключающие - чаще всего вы будете применять несколько одновременно. В наших разборах задач мы будем указывать, какие паттерны используются, чтобы вы учились выявлять и распознавать их.

Обновления в реальном времени

Во многих системах нужно уметь отправлять пользователю обновления в реальном времени. Для синхронных API это просто - вы возвращаете ответ, когда запрос завершен. Для чатов, сервисов уведомлений или live‑дашбордов нужно уметь пушить обновления по мере их появления.

При реализации обновлений реального времени нужно принять несколько решений. Во‑первых, нужно выбрать протокол. HTTP‑polling - самый простой вариант, но он не самый эффективный. Server‑Sent Events (SSE) и WebSockets специально предназначены для обновлений реального времени, но инфраструктуру для них сложно настраивать и поддерживать. Читайте подробности про выбор протокола в нашей статье Основы сетей. Мы рекомендуем начинать с HTTP‑polling, пока этого достаточно для ваших потребностей. Только когда вы упретесь в ограничения, стоит рассмотреть переход на SSE или WebSockets.

На серверной стороне у вас также есть несколько опций для реализации обновлений реального времени. Pub/Sub сервисы - распространенный способ разделить издателя и подписчика (используется в нашем разборе Telegram), а stateful сервера с согласованным хешированием подходят для ситуаций, где обработка тяжеловеснее (используется в нашем разборе Google Docs).

Сложности обновлений в реальном времени

Мы подробно обсуждаем все эти варианты в разборе паттерна Обновления в реальном времени.

Управление длительными задачами

Некоторые операции в распределенных системах слишком длительные для синхронной обработки - кодирование видео, генерация отчетов или любая задача, которая занимает больше нескольких секунд. Паттерн управления длительными задачами разделяет этот процесс на мгновенное подтверждение и фоновую обработку.

Когда пользователи отправляют тяжеловесные задачи, веб-сервер сразу валидирует запрос, добавляет задачу в очередь (например, Kafka) и возвращает идентификатор задачи в течение миллисекунд. Выделенные воркеры постоянно извлекают задачи из очереди и выполняют реальную работу. Это обеспечивает быстрый отклик пользователю, независимое масштабирование веб‑серверов и воркеров, а также изоляцию ошибок.

Многие кандидаты преждевременно добавляют очередь для обработки задач, но зачастую это плохое решение, и вам нужно уметь распознавать когда это действительно необходимо. Если у вас есть задачи которые можно выполнить быстро, их синхронное выполнение значительно упрощает вашу архитектуру, обеспечивая более прозрачное обратное давление (back-pressure) и улучшает пользовательский опыт.

Ключевые технологии здесь - очереди сообщений для координации задач и пул воркеров для обработки. Нужно учитывать отслеживание статуса задач, повторные попытки и сценарии ошибок, например, DLQ для сообщений которые не удалось обработать.

Длительные задачи

Подробное обсуждение асинхронных пулов воркеров, очередей задач и обработки отказов можно найти в разборе паттерна Управление длительными задачами.

Разрешение конфликтов

Когда несколько пользователей одновременно пытаются получить доступ к одному и тому же ресурсу (например, покупка последнего билета на концерт или ставка на аукционе), вам нужны механизмы, которые предотвратят состояние гонки и обеспечат согласованность данных. Этот паттерн решает задачи координации в распределенных системах.

Решения варьируются от подходов на уровне базы данных, таких как пессимистичная блокировка (pessimistic locking) и оптимистичный контроль конкурентности (optimistic concurrency control), до распределенных механизмов координации. Важно понимать, когда достаточно атомарности и транзакций, а когда нужны явные стратегии блокировок. В распределенных системах могут понадобиться распределенные блокировки (distributed locks), двухфазный коммит (two‑phase commit) или сериализация с помощью очереди.

Компромиссы, которые нужно учитывать, включают баланс между производительностью и гарантиями согласованности, а также между простыми решениями на уровне одной базы данных и более сложной распределенной координацией. Для большинства задач лучше начинать с решений на уровне одной базы данных и использовать распределенные блокировки только при необходимости.

Традиционные базы данных изначально построены с учетом проблемы состояния гонки. Когда вы разделяете данные между несколькими базами, вы берете на себя все те сложности, которые уже решены в СУБД. В некоторых случаях это полностью оправдано, но будьте осторожны, делая это преждевременно. Интервьюеры любят проверять, понимаете ли вы, от чего отказываетесь, разделяя данные.

Пример состояния гонки

Подробное погружение в блокировки, транзакции и техники распределенной координации можно найти в разборе паттерна Разрешение конфликтов.

Масштабирование чтения

Когда приложение растет до миллионов пользователей, первым узким местом часто становится чтение. Запись создает данные, а чтение их использует - и трафик чтения обычно растет значительно быстрее, чем трафик записи. Паттерн масштабирования чтения решает проблему большого числа запросов через оптимизацию базы данных, горизонтальное масштабирование и разумное кэширование.

В большинстве приложений соотношение чтений к записям начинается с 10:1, но часто достигает 100:1 и выше. Например, в Россграм при открытии приложения вы видите десятки фото, что требует сотен запросов в базу данных за метаданными, данными пользователей и реакциями. При этом вы, возможно, публикуете пост раз в день - то есть одна запись.

Решение должно следовать естественной траектории: сначала оптимизируйте чтение внутри базы данных через индексы и денормализацию, затем масштабируйте чтение горизонтально с помощью read‑реплик, а потом добавляйте внешние кэши вроде Redis и CDN.

Ключевые нюансы включают инвалидацию кэша, запаздывание данных в read‑репликах и проблему горячих ключей, когда миллионы пользователей одновременно запрашивают один и тот же популярный контент.

Масштабирование чтения в базе данных

Подробное обсуждение стратегий индексации, read‑реплик и управления кэшем можно найти в разборе паттерна Масштабирование чтения.

Масштабирование записи

Когда приложение растет до миллионов записей в секунду, отдельные базы данных и хранилища упираются в жесткие лимиты. Паттерн масштабирования записи решает узкие места при высоком трафике записи через шардирование, пакетирование (batching) и управление пиковыми нагрузками.

Ключевые стратегии - горизонтальное шардирование (распределение данных по узлам), вертикальное партиционирование (разделение разных типов данных) и обработка пиков записи через очереди. Важно выбирать хорошие ключи шардирования и партиционирования, которые равномерно распределяют нагрузку и при этом держат связанные данные вместе.

Для обработки пиков нагрузки можно использовать очереди записи, чтобы сгладить временные всплески, или сброс нагрузки (load shedding), чтобы при перегрузке приоритизировать важные записи. Техники пакетирования помогают уменьшить накладные расходы на каждую операцию за счет группировки нескольких записей.

Хорошие и плохие ключи партиционирования

Подробное обсуждение шардирования, партиционирования и обработки пиков записи можно найти в разборе паттерна Масштабирование записи.

Работа с большими файлами

Большие файлы вроде видео, изображений и документов требуют особого подхода. Вместо того чтобы прокачивать гигабайты через свои серверы, этот паттерн предполагает прямую загрузку от клиента в хранилище с presigned URL и скачивание с использованием CDN.

Ваш сервер генерирует временные, ограниченные по правам credentials (presigned URL), которые позволяют клиентам загружать файлы прямо в blob хранилище, например S3. Скачивание происходит с использованием CDN через presigned URL, которые обеспечивают контроль доступа. Этот подход исключает пропускную способность ваших серверов как узкого места, при этом обеспечивает возобновляемые загрузки, отслеживание прогресса и глобальное распространение.

Ключевые сложности - синхронизация состояния между метаданными в базе данных и blob хранилищем, обработка ошибок загрузки и управление жизненным циклом файлов. Уведомления о событиях из хранилища помогают поддерживать согласованность состояния приложения.

Большие бинарные данные

Подробное обсуждение presigned URL, возобновляемых загрузок и доставки через CDN можно найти в разборе паттерна Работа с большими файлами.

Многошаговые процессы

Сложные бизнес‑процессы часто включают взаимодействие между несколькими сервисами и долгие операции, которые должны переживать сбои, повторы и отказы внешних зависимостей. Этот паттерн обеспечивает надежную координацию для workflow вроде выполнения заказа, онбординга пользователя или обработки платежей.

Решения варьируются от простой оркестрации на одном сервере до полноценных workflow‑движков и систем надежного выполнения (durable execution). Event sourcing предлагает распределенный подход, где каждый шаг генерирует события, которые запускают следующие шаги. Современные системы вроде Temporal или AWS Step Functions берут на себя управление состоянием, восстановление после отказов и повторные попытки выполнения.

Ключевая идея этого паттерна это переход от разрозненного управления состоянием и ручной обработки ошибок к декларативным workflow‑описаниям, где система гарантирует выполнение ровно один раз (exactly‑once) и ведет полный аудит.

Многоэтапные процессы

Подробные примеры и стратегии реализации workflow‑движков и надежного выполнения можно найти в разборе паттерна Многошаговые процессы.

Сервисы на основе географической близости

Некоторые системы, такие как Uber или Сервис доставки, требуют поиска объектов по географической близости. Геопространственные индексы - ключевая технология, позволяющая эффективно искать такие объекты. Такие сервисы часто используют расширения обычных баз данных, например, PostgreSQL с PostGIS, geospatial тип данных в Redis или специализированные решения вроде Elasticsearch с geo‑запросами.

Архитектура решения обычно предполагает деление географической области на регионы и индексирование сущностей внутри них. Это позволяет быстро исключать огромные области, в которых нет нужных сущностей, и существенно сократить пространство поиска.

Геопространственные индексы действительно полезны, но они нужны только тогда, когда вы индексируете сотни тысяч или миллионы элементов. Если у вас карта из 1000 объектов, гораздо проще просканировать их все, чем поддерживать сложное решение с накладными расходами на специализированный индекс или сервис.

Обратите внимание, что большинству систем не нужно, чтобы пользователи искали по всему миру. Практически всегда пользователям нужно искать сущности только рядом с собой (или определенным местом).

Выбор паттернов

Эти паттерны часто работают совместно для решения сложных задач по System Design. Например, видеоплатформа может использовать Большие бинарные данные для загрузки видео, Длительные задачи для транскодирования, Обновления в реальном времени для уведомления о прогрессе и Mногошаговые процессы для координации всего workflow.

Ключ к успеху это умение распознавать, какие паттерны подходят именно вашей задаче, и понимать их компромиссы. Начинайте с простых подходов (HTTP polling, оркестрация на одном сервере) и добавляйте сложность только тогда, когда есть конкретные требования, которые этого требуют.

На собеседованиях по проектированию систем проактивное выявление и применение этих паттернов демонстрирует вашу архитектурную зрелость и помогает вам сосредоточиться на наиболее важных аспектах вашего дизайна.

Войдите чтобы отмечать прогресс