Ссылка
нажмите — покажем
нажмите — покажем
System Design: backend
Как и обещал — задачка с бэковой секции Яндекса. На system design там реально дают «спроектируй ленту новостей, как в Твиттере/ВК, где новостные посты выдаются на основе подписок на авторов»: человек подписан на кучу людей, открывает приложение и видит их посты, свежие сверху. Погнали.
Базовая идея — идти от базы: на открытии ленты делаем SELECT * FROM posts WHERE author IN (на кого подписан) ORDER BY time LIMIT 50. В деве летает. Но в продовой бд так не прокатит: у юзера тысяча подписок, у каждого тысячи постов, и так одновременно долбят миллионы людей. Лента открывается на каждый заход — чтение это самый горячий путь, и вот на нём база и ляжет. Дальше обсудим, какое примерное решение от тебя ждут на собесе.
Уточняем детали
Сначала уточни границы: сколько подписок у среднего юзера, сколько постов в день, какая задержка ок. Но главный вопрос один: есть ли у нас звёзды — аккаунты на миллионы подписчиков? Звучит как продуктовая мелочь, а на деле именно он разворачивает всю архитектуру — сейчас увидишь как.
Попробуем зайти от обратного
Ключевая идея, ради которой задача и придумана: не собирай ленту в момент, когда её открыли. Собери заранее. Когда автор публикует пост, мы разносим (fan-out) его по заранее посчитанным лентам всех подписчиков — кладём в «инбокс» каждого. Тогда открыть ленту — дешёвое чтение готового списка, без тяжёлого запроса по всем подпискам. Смысл манёвра: дорогую работу мы перенесли с частого чтения на редкую запись. Пост пишут один раз, а ленту с ним открывают тысячи раз — вот пусть будет тяжело один раз на записи, а не тысячу раз на чтении.
Возвращаемся к звёздам
А теперь тот самый вопрос. Fan-out на записи прекрасен — пока блогер с 40 миллионами подписчиков не нажмёт «опубликовать». Один пост превращается в 40 миллионов записей, разом, всплеском. Одна звезда убивает всю схему. Поэтому — гибрид. Обычные аккаунты разносим по подписчикам на записи. А посты звёзд не разносим: их мало, зато подписчиков тьма. Их подтягиваем в момент чтения и подмешиваем в готовую ленту. Разложил систему на два пути ровно из-за вопроса про звёзд — это ответ уже не ниже мидла.
В целом вся задача это пример из «Designing Data-Intensive Applications» Клеппманна, первая глава там ровно про ленту Твиттера. Готовишься к систем дизайну — тогда стоит ознакомиться с этой книжкой, не пожалеешь.
Где запнуться можно
Всё вытекает из мысли «работаем на записи». Забыл — провалился. Разнос — асинхронный, через очередь. Автор нажал «опубликовать» — его запрос не ждёт, пока пост доедет до десятков тысяч лент: кинули задачу в брокер, ответили сразу, подписчики увидят через секунду. Тут ок eventual consistency — лента не банковский баланс. В инбоксы кладём не посты, а их id. Хранить полный текст в ленте у каждого из тысяч подписчиков — копия одного и того же миллион раз. Держим ссылки, сам пост подтягиваем при чтении. Не разноси мёртвым душам: если юзер не заходил полгода, гонять его ленту на каждый пост подписок — жечь ресурсы впустую. Соберём на чтении, если вернётся. И идемпотентность: разнос будет ретраиться при сбоях, один и тот же пост не должен лечь в ленту дважды.
На что нацелена задача по итогу
Проверяют этой задачей одно: видишь ли ты, где в системе на самом деле тяжело. Наивный SELECT по подпискам — «лента как страница в базе», его напишет и джун. А инженер замечает, что ленту открывают в тысячи раз чаще, чем пишут пост, и переносит всю тяжесть туда, где она случается редко — на запись. Дальше это само тянет за собой и очередь, и гибрид под звёзд, и хранение по ссылкам: весь дизайн — следствие одного решения, считать ленту заранее, а не на лету. Дожал эту мысль — секция твоя.
Подписаться: @codeof_art
Вступить в чатик: @code_of_art
1.8K ·