> Как реализовать функцию сокращения 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 триллиона комбинаций - достаточно для большинства сервисов.

Основные подходы к генерации идентификаторов:

  1. Счётчик + base62: используем глобальный инкрементальный счётчик (например, в Redis или БД), кодируем число в base62. Плюс - детерминированность и отсутствие коллизий. Минус - можно предсказать следующий ID, что создаёт риск перебора.

  2. Случайная строка: генерируем случайные символы и проверяем уникальность в БД. Плюс - сложнее угадать. Минус - возможны коллизии, нужна повторная генерация.

  3. Хеширование: берём хеш от оригинального 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.

Пример кода

PYTHON
import secrets
import string
from dataclasses import dataclass
from typing import Optional
ALPHABET = string.ascii_letters + string.digits # 62 символа
@dataclass
class URLStore:
"""Простое in-memory хранилище для демонстрации."""
_data: dict[str, str] = None
def __post_init__(self):
self._data = {}
def save(self, short_id: str, original_url: str) -> None:
self._data[short_id] = original_url
def 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._data
class URLShortener:
def __init__(self, store: URLStore, length: int = 7):
self.store = store
self.length = length
def 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_id
def 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: или других опасных схем.

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью