Twitter/X усложняется не в момент записи одного твита, а там, где одну публикацию нужно быстро превратить в свежую ленту для миллионов подписчиков при огромном перекосе в сторону чтения.
Глава помогает связать стратегию раздачи, кэш домашней ленты, поиск, тренды и работу с популярными аккаунтами в одну рабочую архитектуру.
В интервью и архитектурных обсуждениях кейс полезен тем, что заставляет объяснить, где допустима задержка обновления, как переживать очень популярные аккаунты и почему одна стратегия не подходит всем.
Стратегия раздачи
Главный выбор здесь не в базе данных, а в том, когда раскладывать твит по готовым лентам и когда собирать ответ на чтении.
Кэш домашней ленты
Самый горячий путь чтения обычно держится на предсобранной домашней ленте, иначе цена каждого запроса быстро становится слишком высокой.
Тренды в потоке
Поиск всплесков обсуждения требует отдельного потокового контура, который умеет считать быстро, но не тонет в памяти и шуме.
Популярные аккаунты
Очень большие авторы ломают наивную раздачу при записи, поэтому им почти всегда нужен отдельный режим обработки.
Введение
Проектирование Twitter/X упирается не в таблицу твитов, а в домашнюю ленту, которую нужно быстро обновлять для миллионов подписчиков при огромном перекосе в сторону чтения. Главная инженерная развилка здесь состоит в том, когда выгодна , а когда лучше .
Функциональные требования
- Публикация твита — короткий текстовый пост, медиа и базовые взаимодействия.
- Домашняя лента — персональная лента твитов от аккаунтов, на которые подписан пользователь.
- Лента пользователя — все твиты конкретного автора в обратном хронологическом порядке.
- Подписка и отписка — управление графом подписок между аккаунтами.
- Лайки и ретвиты — базовые сигналы вовлечения.
- Поиск — поиск по твитам, аккаунтам и ключевым словам.
- Трендовые темы — быстрое определение всплесков обсуждения в реальном времени.
- Уведомления — упоминания, ответы, лайки и другие события вокруг аккаунта.
Нефункциональные требования
Для пользователя здесь критичны низкая чтения и понятное окно обновления ленты. Абсолютная не обязательна: новый твит может появиться у части подписчиков с небольшой задержкой, если это помогает удерживать систему стабильной.
- Масштаб: около 500M зарегистрированных пользователей и 200M DAU.
- Объём записи: порядка 500M новых твитов в сутки.
- Профиль нагрузки: чтений на порядки больше, чем записей, примерно 1000:1.
- Домашняя лента: ответ желательно удерживать в пределах 200 мс.
- Допустимая задержка появления: 5-10 секунд для обновления у части подписчиков приемлемы.
- Доступность: ориентир — 99.99%, при этом сервис должен переживать частичные сбои без массового отказа.
- Проблема популярных аккаунтов: публикация не должна останавливаться, если у автора десятки или сотни миллионов подписчиков.
Оценка трафика: 200M DAU × 100 чтений ленты в день дают примерно 20B чтений в сутки, то есть около 230 тыс. на чтение. 500M твитов в сутки дают около 6 тыс. запросов в секунду на запись.
Главная задача: сборка ленты
Главная архитектурная проблема Twitter/X — как собирать домашнюю ленту быстро и дёшево, когда почти каждый твит нужно показать многим подписчикам. Базовых стратегий две: либо использовать и заранее раскладывать твит по готовым лентам, либо выбрать и собирать ленту по запросу из последних публикаций нужных аккаунтов.
Раздача при записи
После публикации твит сразу попадает в кэш домашних лент подписчиков.
- Быстрое чтение: лента уже предсобрана.
- Простая логика на пути чтения.
- Дорогая публикация для очень популярных аккаунтов.
- Высокая цена хранения из-за дублирования записей.
- Часть работы тратится на неактивных подписчиков.
Раздача при чтении
Домашняя лента собирается по запросу: система берёт список подписок, читает последние твиты и сливает их в один ответ.
- Публикация почти не зависит от числа подписчиков.
- Меньше дублирования в хранилище.
- Нет работы впустую для неактивных пользователей.
- Чтение становится тяжелее и требует нескольких источников данных.
- Слияние и фильтрация на лету усложняют путь ответа.
Гибридный подход
На практике Twitter/X использует гибридную схему: для обычных аккаунтов работает раздача при записи, а для очень популярных авторов — раздача при чтении. Такой режим снижает цену там, где публикация одного твита иначе взорвала бы систему по числу записей.
Гибридная схема ленты
Переключайте режим, чтобы подсветить путь обычных аккаунтов или популярных авторов.
Приём твита
API и загрузка медиа
Сервис твитов
сохранение твита и выбор стратегии
Сервис раздачи
запись в кэш лент подписчиков
Кэш домашних лент
Redis ZSET для каждого пользователя
Хранилище популярных авторов
отдельный слой для больших аккаунтов
Сборка при чтении
слияние с готовой лентой
Сервис ленты
ранжирование, фильтрация и выдача
Как распознают популярные аккаунты
Порог может быть условным, например 10 000 подписчиков. Если у автора аудитория заметно больше, твит не раскладывают сразу по персональным кэшам, а сохраняют в отдельном слое. При чтении домашней ленты система добавляет свежие твиты таких аккаунтов поверх уже подготовленного ответа.
Архитектура кэша домашней ленты
Кэш домашней ленты нужен, чтобы самый частый путь чтения был дешёвым. Обычно публикация подтверждается быстро, а сама раскладка по кэшам уходит в , чтобы не держать пользователя на тяжёлой фоновой работе.
Структура ленты в Redis
# У каждого пользователя есть собственная домашняя лента в Redis
# Key: timeline:{user_id}
# Value: Sorted Set (score = timestamp, member = tweet_id)
ZADD timeline:12345 1705234567 "tweet_abc123"
ZADD timeline:12345 1705234890 "tweet_def456"
# Получение последних 100 твитов
ZREVRANGE timeline:12345 0 99
# Приближённая оценка объёма:
# 200M пользователей × 800 твитов × 8 байт = 1.28 TB
# С репликацией ×3 = около 4 TB Redis-кластераОграничения кэша
- Обычно держат только последние 800 твитов на пользователя.
- Старые записи удаляются по рангу.
- Глубокая история дочитывается из основного хранилища.
Воркеры раздачи
- Работают асинхронно и не блокируют подтверждение публикации.
- Собирают обновления батчами, чтобы не делать лишние записи.
- Имеют ретраи и защиту от повторной обработки.
Трендовые темы
Определение трендов — это уже : системе важно ловить всплеск обсуждения, а не просто абсолютное число твитов. Для компактного счёта хорошо подходит , а скорость роста считать через .
Контур трендов
Нажмите на этап, чтобы подсветить соответствующую часть контура.
Поток твитов
топик Kafka
Извлечение сущностей
хэштеги и сущности
Фильтрация
спам и стоп-слова
Скоринг трендов
окна и затухание
Кэш трендов
Redis sorted sets
Count-Min Sketch
Вероятностная структура данных, которая позволяет считать частоты миллионов хэштегов без тяжёлой таблицы по каждому ключу. Цена за экономию памяти — небольшая погрешность оценки.
Скользящее окно
Обычно система смотрит на последние 5-15 минут и постепенно понижает вес старых сообщений. Так легче отличить настоящий всплеск интереса от давно накопленного фона.
Формула скоринга трендов
# Упрощённая формула скоринга трендов Twitter
def calculate_trend_score(topic, current_window):
# Текущая скорость: твиты в минуту за последние 5 минут
current_count = count_in_window(topic, minutes=5)
# Базовый уровень: среднее число твитов за 5 минут за последние 7 дней
baseline_count = get_baseline(topic, days=7)
# Во сколько раз тема растёт быстрее обычного
if baseline_count > 0:
velocity_ratio = current_count / baseline_count
else:
velocity_ratio = current_count * 10 # бонус для новой темы
# Вес свежести: экспоненциальное затухание
recency_weight = sum(
tweet.weight * exp(-lambda * (now - tweet.timestamp))
for tweet in current_window.tweets
)
# Буст от вовлечения
engagement_score = (likes + retweets * 2 + replies * 3) / total_tweets
# Финальный счёт
score = velocity_ratio * recency_weight * (1 + log(engagement_score))
# Фильтрация спама и ботов
if unique_users_ratio < 0.3: # слишком мало уникальных пользователей
score *= 0.1 # сильный штраф
return scoreРанжирование ленты
Современный Twitter/X опирается не только на хронологию, но и на . Модель пытается предсказать вероятность вовлечения и поднять в ленте те твиты, которые пользователь с большей вероятностью прочитает, лайкнет или откроет.
Признаки для ранжирования
Признаки твита
- Возраст твита.
- Наличие медиа.
- Наличие ссылок.
- Длина текста.
- Текущий темп вовлечения.
- Средняя вовлечённость автора.
Признаки пользователя
- История взаимодействий с автором.
- Тематические интересы.
- Привычки по времени активности.
- Близость в графе подписок.
- Тип устройства.
- Контекст текущей сессии.
Двухшаговое ранжирование
Шаг 1: быстрый отбор кандидатов. Система собирает примерно 1000 твитов из кэша домашней ленты, публикаций популярных аккаунтов и дополнительных блоков вроде «Вы могли это пропустить».
Шаг 2: финальное ранжирование. Модель оценивает каждого кандидата, после чего пользователю показывают верхнюю часть списка. Время вывода модели стараются удерживать в пределах десятков миллисекунд.
Архитектура поиска
Поиск по твитам живёт в отдельном контуре: сначала новые документы попадают в оперативный индекс, а затем уплотняются в дисковые сегменты. На пользовательском пути система разбирает запрос, ищет по шардам параллельно и после этого сливает результаты.
Поисковый контур
Переключайте между путём индексации и путём пользовательского запроса.
Путь индексации
Приём документа
твит и токенизация
Оперативный индекс
шарды Lucene в памяти
Холодный индекс
дисковые сегменты
Путь запроса
Разбор запроса
термы и фильтры
Поиск по шардам
параллельный поиск
Слияние и ранжирование
свежесть и вовлечение
Модель данных
Основное хранилище держит нормализованные сущности, а путь чтения опирается на денормализованные структуры в Redis и вспомогательные индексы для быстрых ответов.
# Базовые таблицы
tweets {
tweet_id: UUID (Snowflake ID)
user_id: UUID
content: VARCHAR(280)
media_urls: JSON
created_at: TIMESTAMP
reply_to_tweet_id: UUID (nullable)
retweet_of_id: UUID (nullable)
quote_tweet_id: UUID (nullable)
like_count: INT
retweet_count: INT
reply_count: INT
}
users {
user_id: UUID
username: VARCHAR(15)
display_name: VARCHAR(50)
bio: VARCHAR(160)
follower_count: INT
following_count: INT
is_verified: BOOLEAN
is_celebrity: BOOLEAN # follower_count > 10K
created_at: TIMESTAMP
}
follows {
follower_id: UUID
followee_id: UUID
created_at: TIMESTAMP
PRIMARY KEY (follower_id, followee_id)
}
# Денормализованные структуры для чтения
user_timelines (Redis Sorted Set)
Key: timeline:{user_id}
Score: tweet_timestamp
Member: tweet_id
# Твиты популярных аккаунтов
celebrity_tweets (Redis Sorted Set)
Key: celebrity:{user_id}
Score: tweet_timestamp
Member: tweet_idГенерация Snowflake ID
Twitter разработал Snowflake ID, чтобы получать уникальные и примерно сортируемые по времени идентификаторы без центрального координатора.
# Структура Snowflake ID (64 бита)
┌────────────────────────────────────────────────────────────────┐
│ 1 bit │ 41 bits timestamp │ 10 bits │ 12 bits │
│unused │ (milliseconds) │machine │ sequence │
│ │ since epoch │ ID │ number │
└────────────────────────────────────────────────────────────────┘
# Свойства:
# - Примерно сортируется по времени
# - Не требует центральной координации
# - 4096 ID в миллисекунду на машину
# - Хватает на десятки лет до переполнения
# Пример ID: 1605978261000000000
# Timestamp: 2020-11-21 12:31:01 UTC
# Machine: 42
# Sequence: 0
def generate_snowflake_id(machine_id, last_timestamp, sequence):
timestamp = current_time_ms() - TWITTER_EPOCH
if timestamp == last_timestamp:
sequence = (sequence + 1) & 0xFFF # 12 bits
if sequence == 0:
timestamp = wait_next_millis(last_timestamp)
else:
sequence = 0
id = (timestamp << 22) | (machine_id << 12) | sequence
return id, timestamp, sequenceВысокоуровневая архитектура
На верхнем уровне архитектура раскладывается на отдельные контуры публикации, чтения, поиска и трендов. Главное здесь — не смешивать их в одну универсальную цепочку, а дать каждому пути свою стоимость, свои кэши и свои режимы деградации.
Высокоуровневая архитектура
Выберите поток, чтобы подсветить ключевые компоненты.
Пограничный слой и маршрутизация
Клиенты
веб и мобильные приложения
CDN
статика и медиа
Балансировщик
маршрутизация трафика
API Gateway
авторизация и лимиты
Основные сервисы
Сервис твитов
приём и запись твитов
Сервис ленты
сборка домашней ленты
Сервис поиска
путь пользовательских запросов
Сервис трендов
потоковая обработка
Сервис пользователей
профили и граф подписок
Асинхронный слой и данные
Очередь сообщений
Kafka и асинхронная раздача
База твитов
основное хранилище
Кэш домашних лент
Redis ZSET
Поисковый индекс
Lucene и Elasticsearch
Кэш трендов
Redis sorted sets
Графовая база
граф подписок
Подсказки для интервью
Ключевые компромиссы
- Раздача при записи против раздачи при чтении и почему Twitter выбирает гибрид.
- Цена кэша против цены вычисления на лету.
- Скорость выдачи против свежести обновления ленты.
- Точность трендов против цены потоковой обработки.
Частые уточняющие вопросы
- Что делать с аккаунтом, у которого 100M подписчиков?
- Как масштабировать воркеры раздачи и не допускать очередей на часы?
- Как отделять настоящие тренды от спама и ботов?
- Как встраивать персонализацию вроде «Для вас» поверх базовой ленты?
Дополнительные ресурсы
Связанные главы
- System Design Interview: An Insider's Guide (краткий обзор) - даёт классический каркас для разговора о домашней ленте, стратегии раздачи твитов и компромиссах между скоростью и стоимостью.
- Chat System - показывает смежные паттерны долгоживущих соединений, мгновенной доставки и поведения системы под высокой онлайновой нагрузкой.
- Designing Data-Intensive Applications, 2nd Edition (short summary) - усиливает теорию о потоковой обработке, репликации, согласованности и работе систем с большими потоками событий.
- Событийно-ориентированная архитектура: Event Sourcing, CQRS, Saga - объясняет событийный каркас для асинхронной раздачи, фоновых очередей и декомпозиции сервисов ленты.
- Social Media Infrastructure View - расширяет картину до доменной архитектуры соцсети: публикация, лента, идентичность, медиа и сигналы вовлечения.
- Acing the System Design Interview (краткий обзор) - помогает собрать интервью-ответ по Twitter/X: требования, оценки нагрузки, архитектуру и ключевые компромиссы.
- Hacking the System Design Interview (краткий обзор) - добавляет практику по уточняющим вопросам и защите архитектурного решения на собеседовании.
- Frontend system design кейс: Design Instagram Feed - показывает клиентскую сторону продукта с лентой: UX потока, рендеринг и поведение интерфейса под нагрузкой.
- Примеры задач по системному дизайну - помогает сравнить Twitter/X с другими популярными кейсами и увидеть, какие архитектурные паттерны повторяются.
