← Назад к списку
Системный дизайнPythonSenior

Спроектируйте на Python сервис сокращения ссылок: API, хранилище, кэш, масштабирование.

Короткий ответ

  • Начать с требований: RPS, соотношение чтение/запись, latency
  • FastAPI + uvicorn-воркеры, stateless-приложение
  • Код ссылки: base62 от счётчика или случайный с проверкой коллизий
  • PostgreSQL как источник истины, Redis-кэш на горячие редиректы
  • Редирект 301/308 против 302/307 — вопрос кэширования и статистики
  • Клики считать асинхронно: очередь, а не запись в БД на редиректе
  • Горизонтальное масштабирование за балансировщиком

Stateless-сервис на FastAPI с PostgreSQL как источником истины, Redis-кэшем на чтение и асинхронным подсчётом кликов масштабируется горизонтально под читающую нагрузку.

Как сказать вслух

пример ответа

Сначала я уточню нагрузку: такой сервис почти всегда читающий — редиректов на порядки больше, чем созданий. Дальше беру FastAPI с несколькими воркерами, Postgres как основное хранилище и Redis как кэш горячих ссылок, чтобы редирект не ходил в базу. Код генерирую как base62 от счётчика или случайную строку с обработкой коллизий. Клики на редиректе пишу не в базу напрямую, а в очередь, и агрегирую фоном. Приложение stateless, поэтому масштабируется просто добавлением инстансов за балансировщиком.

Подробный ответ

Основной ответ

Требования: предположим 100:1 чтение/запись, редирект должен укладываться в десятки миллисекунд. API: POST /links (принимает URL, опционально кастомный алиас и TTL, отдаёт короткий код) и GET /{code} с редиректом. Генерация кода: base62 от монотонного ID (коротко, но предсказуемо) или 7-8 случайных символов с UNIQUE-ограничением и ретраем на коллизию. Хранилище: PostgreSQL (code PK, url, owner, created_at, expires_at); чтение закрывается Redis-кэшем cache-aside с TTL, негативное кэширование защищает от перебора несуществующих кодов. Редирект: 307/302, если нужна статистика на каждый переход, иначе браузер закэширует 301 и кликов не будет видно. Счётчики кликов — событие в очередь (Redis Stream, Kafka) и фоновый консьюмер с батч-агрегацией. Масштабирование: stateless FastAPI за балансировщиком, реплики Postgres на чтение, мониторинг p99 и hit-rate кэша.

Ключевые моменты

  • Читающий профиль нагрузки. Архитектуру диктует редирект: кэш перед БД, минимальная работа на горячем пути, никакой синхронной записи.
  • Генерация кода. Счётчик+base62 даёт короткие коды, но раскрывает объёмы; случайный код с ретраем на UNIQUE — компромисс по умолчанию.
  • Семантика редиректа. 301 кэшируется браузером навсегда — быстро, но теряется аналитика; 302/307 держит трафик на сервисе.
  • Асинхронная аналитика. Инкремент в БД на каждый редирект станет узким местом; события в очередь и батчевая агрегация решают это.

Практический контекст

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

Частые ошибки

  • Начинают рисовать компоненты, не уточнив нагрузку и соотношение чтение/запись
  • Выбирают 301 и теряют статистику переходов из-за кэширования в браузере
  • Пишут счётчик кликов синхронно в Postgres на каждом редиректе

ИП Кочкин Алексей Сергеевич · ИНН 390509026279 · ОГРНИП 325390000030973 · jiniys2005@yandex.ru