Чтобы создать сокращатель URL, вам нужны четыре вещи: место для хранения маппинга короткого кода на целевой URL, способ генерации уникального кода для каждой новой ссылки, обработчик редиректа, который ищет код и возвращает HTTP-редирект, и кеш перед поисковым запросом, потому что чтений значительно больше, чем записей. Это всё ядро, и его можно поднять за afternoon.
Ловушка - думать, что версия, собранная за afternoon, это уже продукт. Редирект, работающий на вашем ноутбуке, и сервис сокращения URL, выдерживающий незнакомцев, направляющих его на вредоносное ПО, нагружающих трафиком и ожидающих четырёх девяток доступности, - это разные инженерные задачи. Первое - алгоритм. Второе - операционное обязательство.
Это руководство честно строит ядро, а затем большую часть времени тратит на то, что пропускают туториалы по системному дизайну: что ещё нужно построить после того, как редирект заработал. Если сначала хотите концептуальное введение, статья как работают сокращатели URL объясняет механику без кода.
Кратко: что на самом деле делает сокращатель URL
Сокращатель URL - это поиск по ключу-значению с HTTP-редиректом. Ключ - это короткий код, значение - длинный URL, и вся задача сводится к превращению example.com/aB3x9 в 302, указывающий на исходный адрес.
Модель данных - одна таблица:
CREATE TABLE links (
id BIGSERIAL PRIMARY KEY,
short_code TEXT NOT NULL UNIQUE,
long_url TEXT NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE UNIQUE INDEX idx_links_short_code ON links (short_code);
Через неё проходят два пути. Путь записи принимает длинный URL, генерирует короткий код и вставляет строку. Путь чтения принимает короткий код, ищет строку и возвращает редирект. Чтения преобладают в соотношении примерно 1000 к 1, поэтому почти всё инженерное внимание должно быть направлено на то, чтобы поиск был быстрым и дешёвым. Уникальный индекс на short_code - это то, что делает поиск поиском по индексу, а не полным сканированием. Вот и всё ядро.
Генерация короткого кода: Base62, случайный или хеш
Короткий код - место, где находится интересное решение. Есть три реалистичные стратегии, и они компромиссны по длине, предсказуемости и сложности обработки коллизий.
Base62 от уникального ID - классика. Берёте автоинкрементный ID строки и кодируете его в base62, 62 символа a-z, A-Z и 0-9. Коды короткие, коллизии невозможны, так как каждый ID уникален, и они становятся на один символ длиннее примерно каждые 62x по объёму. Недостаток - они последовательны и предсказуемы, поэтому любой может обойти ваше пространство имён.
const alphabet = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
// encode turns a positive integer ID into a base62 short code.
func encode(id uint64) string {
if id == 0 {
return string(alphabet[0])
}
var b []byte
for id > 0 {
b = append(b, alphabet[id%62])
id /= 62
}
// reverse, since we built the digits least-significant first
for i, j := 0, len(b)-1; i < j; i, j = i+1, j-1 {
b[i], b[j] = b[j], b[i]
}
return string(b)
}
Случайные строки решают проблему предсказуемости. Генерируете короткий случайный код, например с помощью библиотеки nanoid, и проверяете его по уникальному индексу перед сохранением. При семи символах base62 у вас триллионы возможностей, так что коллизии редки, но вы всё равно должны обрабатывать редкую вставку, которая нарушает ограничение уникальности, повторно пробуя с новым кодом.
Хеширование URL - третий вариант и обычно худший. Хеш длинного URL детерминирован, что кажется удобным, но вам всё равно нужно его усекать, вы всё равно получаете коллизии, и одинаковые URL отображаются на одинаковые коды, что раскрывает информацию. Большинство продакшн-сервисов выбирают base62 для внутренних ID или случайные коды для публичных. Кастомные или брендированные слаги, коды, которые пользователь вводит вручную, проверяются по тому же уникальному индексу перед принятием.
Путь редиректа: 301 против 302 и почему это определяет вашу аналитику
Код статуса редиректа - не косметический выбор. Он определяет, увидите ли вы второй клик.
301 Moved Permanently сообщает браузерам и прокси, что перемещение постоянное, поэтому они кешируют его. После первого посещения браузер может направлять будущие клики прямо к месту назначения, не обращаясь к вашему серверу. Отлично для скорости, но губительно для аналитики, потому что клики, которые вы больше всего хотите считать, это те, которые никогда до вас не доходят. HTTP-семантика прописана в RFC 9110, который определяет как постоянные, так и временные редиректы.
302 Found или 307 Temporary Redirect запрашивается каждый раз. Браузер обращается к вашему серверу при каждом клике, что означает возможность считать каждое посещение и изменять место назначения позже без борьбы с устаревшими кешами. Для сокращателя ссылок, вся ценность которого - в редактируемых ссылках и данных о кликах, это правильный вариант по умолчанию. Цена - один сетевой round trip на клик, который попадание в кеш делает незначительным.
Практическое правило: используйте 302, если нет конкретной причины хотеть, чтобы ссылка была заморожена и кеширована навсегда. Пост 301 против 302 редиректов подробно разбирает этот компромисс, а типы редиректов охватывает остальных представителей семейства 3xx, включая случаи, когда важны 307 и 308.
Хранение и кеширование: проектирование для соотношения чтения/записи 1000:1
Поскольку чтения значительно перевешивают записи, узким местом является не база данных, а стратегия кеширования. Паттерн - сквозной кеш для чтения: при клике сначала проверяете кеш в памяти, и обращаетесь к базе данных только при промахе, записывая результат обратно в кеш для следующего раза.
func resolve(ctx context.Context, code string) (string, error) {
if url, ok := cache.Get(code); ok {
return url, nil // hot path: served from memory
}
url, err := db.LookupLongURL(ctx, code)
if err != nil {
return "", err
}
cache.Set(code, url) // populate for the next click
return url, nil
}
В продакшне это обычно становится двухуровневым: небольшой внутрипроцессный кеш для самых горячих ссылок, поддерживаемый общим хранилищем в памяти, например Redis, чтобы каждый экземпляр сервера получал пользу от поиска, выполненного любым из них. База данных, источник истины, затрагивается только при реальном холодном промахе. Настройте этот слой правильно, и один скромный сервер обработает огромный объём кликов. Пост стратегия кеширования для URL-редиректов подробно разбирает решения по вытеснению и размерам, а краеугольная статья о достижении p95 менее 15 мс показывает, как выглядит настроенный путь редиректа под нагрузкой.
Если вы предпочитаете не запускать всё это самостоятельно, API Elido предоставляет уровень редиректа, кеш и внутрирегиональную EU-доставку с p95 менее 15 мс при попадании в кеш - за один вызов. Начните бесплатно и избавьте себя от операционных вопросов.
Подсчёт кликов без замедления редиректа
Ошибка, которая убивает латентность редиректа, - запись клика в базу данных внутри обработчика редиректа. Тогда каждый посетитель ждёт завершения аналитической записи, прежде чем получит редирект.
Разделите их. Обработчик немедленно выполняет редирект, а затем отправляет событие клика в надёжный журнал или очередь сообщений по принципу "выстрелил и забыл". Отдельный потребитель читает этот поток и записывает события в аналитическое хранилище по собственному расписанию. Посетитель никогда не ждёт, а аналитический запрос, сканирующий миллионы строк кликов, никогда не конкурирует с путём редиректа за ресурсы. Колоночная аналитическая база данных обрабатывает такие агрегатные запросы значительно лучше, чем строковые хранилища, вот почему события кликов обычно попадают куда-то отдельно от таблицы ссылок. Пост приём кликов по принципу "выстрелил и забыл" подробно описывает сторону очереди, а статья почему колоночное хранилище лучше Postgres для аналитики кликов объясняет выбор хранилища. Аналитика Elido следует этой схеме, поэтому клики можно запрашивать за секунды, не добавляя миллисекунд к редиректу.
Что ещё нужно построить: сложные 80 процентов
Вот часть, которую пропускают руководства по системному дизайну. Рабочий редирект - это, возможно, пятая часть реального сервиса сокращения URL. Остальное - всё то, что превращает демо во что-то, что можно выставить в публичный интернет.
- Сканирование на злоупотребления и безопасность. Публичный сокращатель становится мишенью для фишинга в течение нескольких часов после запуска. Нужно проверять пункты назначения по базе угроз, например Google Safe Browsing, и повторно сканировать, потому что чистый URL при создании может позже стать вредоносным. Контрольный список безопасности сокращателя URL - полный перечень.
- Ограничение запросов и идемпотентность. Открытый эндпоинт создания ссылок немедленно подвергается скриптовым атакам. Нужны лимиты на ключ и идемпотентность, чтобы повторный запрос не создавал дублирующиеся ссылки. Механика - в статье ограничение API запросов и идемпотентность.
- Кастомные домены с TLS. Брендированные ссылки означают выпуск сертификатов для доменов, которыми вы не владеете, по требованию, без ручных шагов.
- GDPR-безопасные данные кликов. С момента, когда вы начинаете логировать клики, вы обрабатываете персональные данные. Усечение IP-адресов и документирование сроков хранения не опционально в EU, как показывает статья GDPR для сокращателей URL.
- Высокая доступность. Ваш редирект теперь находится на критическом пути каждой ссылки, которой кто-либо поделился. Простой ломает контент других людей, поэтому требования к доступности выше, чем для большинства приложений.
Ничто из этого не является экзотикой. Это просто большой объём непрерывной работы, которая никогда не заканчивается, - и это честная причина, по которой большинство команд останавливается на MVP и обращается к чему-то готовому.
Создать, купить или развернуть самостоятельно
Создать собственный сокращатель - лучший способ понять редиректы, кодирование и кеширование, а для закрытого внутреннего инструмента MVP может быть всем, что когда-либо понадобится. Создайте его. За выходные узнаете больше, чем даст любая подготовка к собеседованию.
Для всего публичного или бизнес-ориентированного честно оцените расходы на поддержку. Редирект бесплатен; обработка злоупотреблений, TLS для кастомных доменов, аналитический пайплайн и дежурство - нет. Если хотите контроль без написания с нуля, можно развернуть существующий сервис: Elido предлагает путь для самостоятельного развёртывания, а пост опции с открытым исходным кодом сравнивает их. Если предпочитаете делегировать полностью, решение для разработчиков и быстрый старт с API и SDK дадут вам продакшн-уровень редиректа без бэклога выше.
Читайте также в блоге
- Как работают сокращатели URL - концептуальное введение, код не требуется.
- Достижение p95 менее 15 мс для редиректов - инженерная основа настроенного пути редиректа.
- Стратегия кеширования для URL-редиректов - двухуровневый кеш в деталях.
- Приём кликов по принципу "выстрелил и забыл" - отделение аналитики от редиректа.
- Сокращатели URL с открытым исходным кодом - опции с открытым кодом, если не хотите создавать с нуля.
Частые вопросы
Как создать сокращатель URL?
Сохраните маппинг короткого кода на целевой URL, сгенерируйте уникальный код для каждой новой ссылки и при запросе верните HTTP-редирект на целевой адрес. Поставьте кеш перед базой данных, потому что чтений значительно больше, чем записей. Вот и всё ядро. MVP - проект на выходные; продакшн-версия - нет.
Какой алгоритм используют сокращатели URL для генерации коротких кодов?
Два распространённых подхода: base62-кодирование уникального числового ID, что даёт короткие коды без коллизий, и генерация случайной строки (например, через nanoid) с проверкой на коллизии перед сохранением. Хеширование URL - третий вариант, но он требует обработки коллизий и не даёт никаких практических преимуществ, поэтому большинство сервисов выбирают base62 или случайные коды.
Какой редирект использовать для сокращателя URL: 301 или 302?
Используйте 302 или 307, если хотите считать каждый клик и сохранить возможность редактирования целевого адреса, так как браузеры при каждом переходе повторно запрашивают короткую ссылку. Используйте 301 только если хотите кеширующий редирект, который никогда не изменится, поскольку 301 может кешироваться так агрессивно, что будущие клики обойдут ваш сервер и аналитику полностью.
Какая база данных лучше всего подходит для сокращателя URL?
Реляционная база данных - обычный источник истины, с коротким кодом в виде уникального индексированного ключа для быстрых поисков. Поставьте перед путём чтения кеш в памяти, а для событий кликов используйте отдельное хранилище, чтобы тяжёлые аналитические запросы никогда не затрагивали путь редиректа.
Сколько времени занимает создание сокращателя URL?
Рабочий редирект с генерацией кода - это выходные. Долгосрочная часть занимает месяцы: сканирование на злоупотребления, ограничение запросов, кастомные домены с TLS, аналитика кликов в масштабе, GDPR-безопасная обработка данных и высокая доступность. Большая часть инженерных усилий приходится на период после готовности MVP.
Сложно ли создать сокращатель URL?
Сам редирект - просто. Защита для продакшна - сложно, потому что вы получаете публичный эндпоинт, который незнакомые люди будут направлять на вредоносное ПО, нагружать трафиком и ожидать, что он будет работать постоянно. Сложность операционная, а не алгоритмическая.
Попробуйте Elido
Вставьте URL - получите короткую ссылку
Без регистрации. Ссылка живёт 30 дней. Зарегистрируйтесь, чтобы оставить её навсегда.
Бесплатно, без регистрации · 2 в день