Кейс: сократитель ссылок (URL shortener)
Классическая задача собеседования: спроектировать сервис вроде bit.ly, превращающий длинный URL в короткий.
Идём строго по шаблону: требования → оценки → высокоуровневая схема → детали → узкие места. Этот же каркас подойдёт почти к любой задаче.
1. Требования
Функциональные:
- по длинному URL создать короткий (например,
short.ly/abc123); - переход по короткому → редирект на длинный;
- (опц.) пользовательский алиас и срок жизни ссылки;
- (опц.) аналитика кликов.
Нефункциональные: редирект быстрый (p99 < 100 мс); высокая доступность (битая ссылка хуже медленной); чтений ≫ записей; ссылки не должны теряться и коллизий быть не должно.
2. Оценки масштаба
Пусть 100 млн новых ссылок в месяц. Соотношение чтений к записям возьмём 100:1.
Записи: 100 млн / мес ≈ 40 записей/с (100M / 2.5M сек)
Чтения: 40 * 100 = 4000 редиректов/с
Пик чтения: ~ x2 = ~8000 QPS
Хранилище: ~500 байт на ссылку * 100M * 12 мес * 5 лет
≈ 0.5KB * 6 млрд ≈ 3 TB
Выводы сразу: 3 TB на годы — поместится на нескольких узлах; 8000 QPS на чтение — нужен кэш; чтений в 100 раз больше — оптимизируем именно редирект.
3. Сколько символов в коде
Используем алфавит из 62 символов (a–z, A–Z, 0–9). Сколько ссылок закодируем кодом длины n? 62ⁿ.
| Длина кода | Сколько уникальных ссылок |
| 6 символов | 62⁶ ≈ 56 млрд |
| 7 символов | 62⁷ ≈ 3,5 трлн |
За 5 лет нам нужно ~6 млрд кодов — 7 символов дают огромный запас. Берём 7.
4. Высокоуровневая схема
[клиент] → [LB] → [сервис ссылок] → [кэш Redis] → [БД код→URL]
создание: сгенерировать код, записать в БД
редирект: код → URL (сначала кэш, потом БД) → 301
5. Как генерировать код
| Подход | Плюс | Минус |
| Хеш URL (MD5) → первые 7 символов | детерминирован | коллизии, надо проверять/разрешать |
| Счётчик + base62 | без коллизий, коротко | коды предсказуемы, счётчик — узкое место |
| Случайный код + проверка уникальности | непредсказуем | лишняя проверка в БД |
Хорошее решение — счётчик с base62: глобальный счётчик (или диапазоны, раздаваемые узлам, чтобы он не стал SPOF) даёт уникальное число, его кодируем в 7 символов. Диапазоны (например, по 1000 id на узел) убирают узкое место единого счётчика.
6. Модель данных и редирект
{
"short_code": "a9Bx2Cd",
"long_url": "https://example.com/very/long/path",
"created_at": 1718380800,
"expires_at": null
}
Доступ всегда по ключу short_code — идеально для key-value хранилища и кэша. Редирект отдаём как HTTP 301/302. Нюанс: 301 (постоянный) браузеры кэшируют — быстро, но клики хуже считаются; 302 (временный) всегда идёт через сервис — нужно для аналитики. Это компромисс «скорость против учёта».
7. Узкие места и решения
| Узкое место | Решение |
| 8000 QPS на чтение | кэш горячих ссылок (Redis), hit rate высок — ссылки популярны неравномерно |
| 3 TB данных | шардирование по short_code (hash/range) |
| Единый счётчик — SPOF | раздавать узлам диапазоны id |
| Доступность редиректа | реплики БД, кэш переживает падение БД |
Итог
- Шаблон работает: требования → оценки → схема → детали → узкие места.
- 7 символов base62 (62⁷ ≈ 3,5 трлн) с запасом покрывают объём; счётчик с диапазонами убирает коллизии и SPOF.
- Чтений ≫ записей → главное оптимизировать редирект: кэш + шардирование + реплики.