Раздел 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.