> Как реализовать функцию сокращения URL (Python)
Уровень: senior · Роль: backend · Язык: Python · Категория: Технические вопросы
Компании: Международный аэропорт Шереметьево
Стек: Python
> Пример ответа
Короткий ответ
Сокращение URL - это сервис, который принимает длинную ссылку и возвращает короткий идентификатор, сохраняя соответствие в хранилище. Основные компоненты: генерация уникального идентификатора, persistence (обычно key-value хранилище), редирект с HTTP 301/302 и обработка коллизий. Для генерации используют base62-кодирование счётчика или случайные строки с проверкой уникальности. Ключевые trade-off: длина ссылки, скорость генерации, масштабируемость и безопасность (защита от перебора).
Подробное объяснение
Архитектура сервиса сокращения URL включает три основных этапа: создание короткой ссылки, хранение и редирект. При создании сервис генерирует уникальный идентификатор длиной 6-8 символов из алфавита [a-zA-Z0-9] (62 символа). Для 7 символов это 62^7 ≈ 3.5 триллиона комбинаций - достаточно для большинства сервисов.
Основные подходы к генерации идентификаторов:
-
Счётчик + base62: используем глобальный инкрементальный счётчик (например, в Redis или БД), кодируем число в base62. Плюс - детерминированность и отсутствие коллизий. Минус - можно предсказать следующий ID, что создаёт риск перебора.
-
Случайная строка: генерируем случайные символы и проверяем уникальность в БД. Плюс - сложнее угадать. Минус - возможны коллизии, нужна повторная генерация.
-
Хеширование: берём хеш от оригинального URL (например, MD5 или SHA-256) и берём первые N символов. Минус - высокая вероятность коллизий при короткой длине, нужна проверка.
Хранение: оптимально использовать key-value хранилище (Redis, DynamoDB, Cassandra) с ключом = короткий ID, значением = оригинальный URL. Для реляционных БД - таблица с уникальным индексом на короткий ID.
Редирект: при запросе на /short_id сервер ищет запись и возвращает HTTP 301 (постоянный редирект) или 302 (временный). 301 кешируется браузерами, что снижает нагрузку, но усложняет аналитику. 302 - для подсчёта переходов.
Масштабирование: для высоких нагрузок используют кэширование популярных ссылок (Redis), шардирование по короткому ID, асинхронную запись в аналитику.
На практике
На собеседовании важно показать понимание полного цикла: от API до инфраструктуры. Начните с описания REST API: POST /shorten с телом {"url": "..."} и ответом {"short_url": "..."}, затем GET /{short_id} с редиректом.
Обсудите выбор хранилища: для MVP подойдёт PostgreSQL с уникальным индексом, для production - Redis как кэш + основное хранилище. Упомяните TTL для временных ссылок и кастомные алиасы (пользователь сам выбирает короткое имя).
Важно проговорить обработку коллизий: при случайной генерации - повторная попытка, при счётчике - невозможны. Также стоит упомянуть валидацию URL (проверка схемы http/https) и защиту от SSRF, если сервис делает запросы к URL.
Пример кода
PYTHONimport secretsimport stringfrom dataclasses import dataclassfrom typing import OptionalALPHABET = string.ascii_letters + string.digits # 62 символа@dataclassclass URLStore:"""Простое in-memory хранилище для демонстрации."""_data: dict[str, str] = Nonedef __post_init__(self):self._data = {}def save(self, short_id: str, original_url: str) -> None:self._data[short_id] = original_urldef get(self, short_id: str) -> Optional[str]:return self._data.get(short_id)def exists(self, short_id: str) -> bool:return short_id in self._dataclass URLShortener:def __init__(self, store: URLStore, length: int = 7):self.store = storeself.length = lengthdef shorten(self, original_url: str) -> str:"""Генерирует короткий ID и сохраняет соответствие."""while True:short_id = self._generate_id()if not self.store.exists(short_id):self.store.save(short_id, original_url)return short_iddef resolve(self, short_id: str) -> Optional[str]:"""Возвращает оригинальный URL по короткому ID."""return self.store.get(short_id)def _generate_id(self) -> str:"""Генерирует случайный ID из алфавита base62."""return ''.join(secrets.choice(ALPHABET) for _ in range(self.length))# Пример использованияstore = URLStore()shortener = URLShortener(store)short_id = shortener.shorten("https://example.com/very/long/path?with=params")print(f"Short ID: {short_id}")original = shortener.resolve(short_id)print(f"Resolved: {original}")
Как отвечать на собеседовании
Структурируйте ответ по слоям: API → генерация ID → хранилище → редирект → масштабирование.
Начните с краткого описания задачи: "Сервис принимает длинный URL и возвращает короткий, при переходе по которому происходит редирект". Затем переходите к деталям.
Обязательно проговорите trade-off между счётчиком и случайной генерацией. Спросите интервьюера про требования к нагрузке - это покажет системное мышление.
Упомяните edge cases: дублирующиеся URL (можно возвращать существующий ID), невалидные URL, слишком длинные ссылки, кастомные алиасы.
В конце добавьте про observability: логирование, метрики количества переходов, rate limiting.
Что проверяет интервьюер
- Понимание базовых алгоритмов генерации уникальных идентификаторов.
- Умение выбирать хранилище под задачу.
- Знание HTTP-статусов для редиректов и их различий.
- Способность мыслить о масштабировании и отказоустойчивости.
- Внимание к безопасности (перебор ID, SSRF, валидация входных данных).
- Практические навыки проектирования REST API.
Типичные ошибки
- Использование MD5 или SHA-1 без учёта коллизий - при 6-7 символах вероятность высокая.
- Возврат 200 OK вместо 301/302 при редиректе.
- Игнорирование кеширования популярных ссылок.
- Отсутствие обработки дубликатов - каждый запрос создаёт новую запись.
- Неучёт длины ID: слишком короткий (5 символов) - мало комбинаций, слишком длинный - неудобно.
- Хранение в реляционной БД без индекса на короткий ID - деградация производительности.
- Отсутствие валидации URL - возможность вставки
javascript:или других опасных схем.
> Похожие задачи по Python
Как справляться с коллизиями при генерации коротких ссылок
Как хранить маппинг между длинным и коротким URL
Как проверять корректность работы сервиса и обработку сообщений по разным приоритетам вовремя
Как реализовать сложные триггеры для мониторинга, например отправку SMS при загрузке CPU и низком потреблении памяти
> Похожие задачи по backend
Как справляться с коллизиями при генерации коротких ссылок
Как хранить маппинг между длинным и коротким URL
Как проверять корректность работы сервиса и обработку сообщений по разным приоритетам вовремя
Как реализовать сложные триггеры для мониторинга, например отправку SMS при загрузке CPU и низком потреблении памяти
> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?
Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью