Тренажёр · System Design

Сократитель ссылок

Спроектируйте сервис коротких ссылок вроде bit.ly: пользователь отдаёт длинный URL и получает короткий, а переход по короткому ведёт на исходный адрес.

Уровень: Средний На собеседовании: 40–45 минут

Функциональные требования

  • Создать короткую ссылку по длинному URL
  • Переход по короткой ссылке — редирект на исходный URL
  • Необязательный срок жизни ссылки
  • Счётчик переходов по ссылке

Нефункциональные требования

  • 100 млн ссылок в базе, +100 млн в год
  • Чтений в ~100 раз больше, чем записей: 10 000 переходов/с против 100 созданий/с
  • Редирект укладывается в 50 мс на p99
  • Ссылки живут годами, потеря ссылки недопустима
  • Счётчик переходов может отставать на минуты — это нормально

Нарисуйте архитектуру

Не гонитесь за красотой: на собеседовании от схемы нужно, чтобы по ней было видно путь запроса и где лежат данные. Сначала нарисуйте сами — эталон и разбор ниже.

Блок из палитры — добавить. Тащите мышкой или пальцем. Двойной клик по названию — переименовать. «Связь» — щёлкнуть по одному блоку, потом по другому: получится стрелка.

Схема сохраняется в этом браузере — вкладку можно закрыть и вернуться позже.

1 С чего начать, если ступор
  • Сначала уточните требования и прикиньте объём: 100 млн × ~500 байт ≈ 50 ГБ — это влезает в одну БД, шардирование пока не нужно.
  • Решите, как генерировать ключ: счётчик в base62 или случайные 7 символов с проверкой коллизии. У обоих вариантов есть цена — назовите её.
  • Разделите горячий путь (редирект) и холодный (создание). У них разные требования и разная нагрузка.
  • Подумайте, что попадает в кэш и что происходит при промахе.
  • Счётчик переходов не должен стоять в пути редиректа.
2 Эталонная схема

Горячий путь — это client → балансировщик → API → кэш. В базу ходим только при промахе, а переход отправляем в очередь и считаем отдельно, чтобы аналитика не удлиняла редирект.

Это один из рабочих вариантов, а не единственно верный. Если у вас иначе, но вы можете объяснить почему — на собеседовании это ровно то, что нужно.

3 Разбор: что должно прозвучать

Требования и оценка объёма

  • Уточнили соотношение чтений и записей — именно оно определяет всю архитектуру
  • Прикинули объём данных (~50 ГБ) и вывод: шардирование на старте не нужно
  • Посчитали нагрузку: 10k RPS на редирект — это про кэш, а не про базу

Генерация короткого ключа

  • Счётчик в base62: ключи короткие и без коллизий, но предсказуемые и счётчик — узкое место (лечится диапазонами на инстанс)
  • Случайные 7 символов base62: непредсказуемо, но нужна проверка коллизии, то есть лишний поход в БД
  • Уникальный индекс по коду в БД как последний рубеж от гонки

Чтение и кэш

  • Кэш code → URL с TTL, промах — поход в БД и прогрев
  • Ссылки почти неизменяемы, поэтому инвалидация нужна только при удалении и смене срока жизни
  • Горячие ссылки: их немного, они и так лежат в кэше — но стоит вспомнить про thundering herd на прогреве

Редирект: 301 или 302

  • 301 браузер кэширует — повторные переходы до вас не доедут, статистика развалится
  • 302 (или 307) даёт корректный счётчик ценой лишнего запроса — для такого сервиса обычно выбирают его
  • Это любимый уточняющий вопрос: покажите, что понимаете trade-off, а не заучили ответ

Счётчик переходов

  • Синхронный UPDATE счётчика на каждом переходе убьёт базу на 10k RPS
  • Пишем событие в очередь, воркер агрегирует и обновляет статистику пачками
  • Отставание на минуты допустимо — это было в нефункциональных требованиях

Куда копать дальше, если спросят

  • Что ломается при отказе кэша целиком (и переживёт ли это база)
  • Как удалять просроченные ссылки, не блокируя базу
  • Когда всё-таки понадобится шардирование и по какому ключу
  • Защита от абьюза: лимиты на создание, проверка ссылок на вредоносность

Теория к этой задаче — в боте

Кэш, очереди, репликация и шардирование разобраны карточками с интервальным повторением.