Курс «Архитектура мессенджера» · Часть 3 · Хранение и данные

Раздел 8A. Модель данных (схемы таблиц)

Сущности и таблицы; ключ шардирования; SQL и NoSQL; связь модели данных с ручками.

API-ручки (Раздел 2A) и хранение (Раздел 8) опираются на модель данных. Набросать сущности и ключ шардирования — частый вопрос интервьюера: «какие таблицы заведёшь и по какому ключу шардируешь?».

Главные сущности

users(user_id PK, имя, профиль, …)                       аккаунт
devices(device_id PK, user_id FK, platform, push_token, last_seen)
                                                          устройств у юзера много
chats(chat_id PK, type[dialog|group|channel], title, created_at)
chat_members(chat_id, user_id, role, muted, joined_at)   членство · PK (chat_id, user_id)
messages(message_id, chat_id, sender_id, seq, type,
         body/ciphertext, created_at, edited_at, deleted) durable-лог
attachments(file_id PK, message_id, url, type, size, preview)
read_cursors(chat_id, user_id, device_id, last_read_seq)  прочитанность на устройство
prekeys(user_id, key_id, pubkey)                          для E2EE (Раздел 15)

Ключевые решения (их и спрашивают)

  • messages шардируем по chat_id. Все сообщения чата лежат на одном шарде, и главный запрос — «последние N сообщений чата» — бьёт в один шард. Минус: горячий шард на чатах-гигантах и каналах — лечится особой обработкой больших каналов (Раздел 11).
  • Порядок — через seq, монотонный в рамках чата. Сортировка по нему, а не по времени (часы серверов расходятся). Разрыв в seq = обнаруженный пропуск (Раздел 6).
  • Членство ≠ история. chat_members и messages — разные таблицы с разной нагрузкой: fan-out читает список участников, история — это бесконечный append. Это и есть «Метаданные чата» на схеме (Раздел 11).
  • Прочитанность — курсор на устройство. last_read_seq свой для каждой пары (чат, устройство): телефон и десктоп догоняют независимо (Раздел 10). Сервер — источник правды.
  • Медиа — ссылкой, не блобом. Файл лежит в объектном хранилище + CDN, в attachments/messages — только URL, тип, размер, превью (Раздел 9). Блоб в таблице сообщений убил бы и её, и тракт доставки.
  • SQL и NoSQL вместе. История сообщений — NoSQL-стиль (поток записей, бесконечный рост, горизонтальное шардирование). Аккаунты, контакты, членство — SQL, где важны строгие связи и согласованность (Раздел 8).

Как это связано с ручками

Каждая ручка из Раздела 2A читает/пишет эти таблицы: GET /messages/{chat_id} идёт постранично из messages по (chat_id, seq); POST /chats/{id}/members — строка в chat_members; POST /media — запись в attachments плюс файл в хранилище. Модель данных — это «обратная сторона» API.