Как происходит взаимодействие микросервиса сокращения ссылок с Redis и базой данных
Что проверяют
Понимаете ли вы, кто здесь источник правды, а кто ускоритель. Интервьюер ждёт, что вы разнесёте запись и чтение и объясните, что будет, когда Redis упадёт. Следом спрашивают, как генерировать короткий код без коллизий.
Как отвечать
Создание ссылки. Код берут из последовательности базы или атомарного INCR в Redis и кодируют в base62, либо генерируют случайную строку и полагаются на уникальный индекс, повторяя попытку при конфликте. Запись идёт в PostgreSQL: это основное хранилище.
Переход по ссылке это горячий путь, и здесь работает cache-aside:
- чтение из кэша:
GET short:<code>в Redis, при попадании сразу редирект; - промах: запрос в базу, затем
SET short:<code> <url> EX <ttl>и редирект; - несуществующий код: отрицательный ответ тоже кэшируют ненадолго, иначе перебор случайных кодов пойдёт прямо в базу;
- код ответа: 301 браузер кэширует, и повторные переходы до вас не дойдут. Если нужна статистика, отдают 302;
- счётчик переходов:
INCRв Redis, а в базу его сбрасывают пачками фоновой задачей.
Ссылки почти не меняются, так что TTL нужен ради памяти, а не свежести. Отсюда важная настройка: по умолчанию у Redis maxmemory-policy noeviction, и при заполнении памяти запись начнёт падать с ошибкой. Для кэша ставят allkeys-lru или allkeys-lfu.
В Python это обычно FastAPI, redis.asyncio из пакета redis и asyncpg или SQLAlchemy. Вызов Redis оборачивают коротким таймаутом: если кэш недоступен, сервис идёт в базу, а не падает.
Где спотыкаются
Пишут ссылку только в Redis «для скорости». После рестарта без персистентности или вытеснения по памяти ссылки пропадают навсегда.
Генерируют случайный код без проверки уникальности. Коллизия редкая, но на миллионах ссылок она случится, и одна ссылка молча перезапишет другую.
Забывают про горячий ключ: популярная ссылка истекает из кэша, и сотни одновременных запросов уходят в базу. Помогает блокировка на перезаполнение или TTL со случайным разбросом.