Архитектура базы данных мессенджера

Не очень понимаю, как хранить множественную информацию в бд, которая привязана к сильно менее множественной. Например, чат в мессенджере: у нас есть таблица чатов - id, юзеры, которые туда входят и тд. Но как хранить сообщения и их информацию? Логично создать таблицу сообщений и ее связать с таблицей чатов по id. Но чатов в тысячи раз меньше чем сообщений. Когда надо будет достать все сообщения из какого либо чата придется искать по всей таблице сообщений, что очень затратно по ресурсам и времени.

Ещё один вариант - это создавать таблицу сообщений для каждого чата, но мне кажется то что такой подход почти не используется, тк в тех ORM, которые я использую, такого функционала нет.

Также можно общую таблицу сообщений индексировать. Это уже более реалистичный вариант. Но не уверен, что лучший.

Подскажите, как лучше всего реализовать это максимально производительно в sql бд?


Ответы (2 шт):

Автор решения: Швеев Алексей

Первым делом скажу, что учитывая ваши масштабы вам сойдёт БД практически любого формата, разве уж искусственно замедленная может тормозить)

Здесь же я просто делюсь своими уперевшимися в оптимизацию мыслями, так что если вам покажется что то неэффективным/непрактичным будет интересно пообщаться на эту тему в комментариях!

Как уже упоминали в комментарии к оригинальному вопросу:

Главное, не хранить больше, чем нужно.

Учитывая что у нас огромное количество сообщений хранение метаданных к каждой из них будет довольно неэффективно. По хорошему для максимального сжатия стоит их хранить с минимальным количеством метаданных, друг за дружкой, без какой либо лишней информации об их хранении.

Связанный список

Первым делом я подумал о простом связанном списке, записанном в файл "как есть". Условно:

0 [1.len] [1.data] [1.prevlen] [2.len] [2.data] ... [(n-1).len] [n.len] [n.data] [n.len] [eof]

где len - размер текущего блока данных и data - сами данные.

Таким образом мы сможем легко прыгать от одного сообщения до другого без каких либо лишних проблем как по связанному списку не читая и не храня в памяти весь объект (определённый набор данных).

Непрерывный поток данных с ссылками

Далее мне пришла в голову более простая версия - можно просто хранить все данные непрерывно, но в начале выделить массив фиксированного размера с ссылками на эти самые данные. Условно:

[n][1.offset][2.offset]...[n.offset][1.data][2.data]...[n.data]

Где offset - сдвиг позиции данных относительно начала объекта, а data - сами данные.

Таким образом мы сможем получить доступ к любому элементу данных за константное время имея на руках весь объект.


В случае если мы хотим отредактировать или удалить сообщение мы можем установить определённый флаг в data и после запихнуть туда же отредактированные данные или ссылку на новые данные если они туда не влезают (которые уже могут храниться в стандартном sql формате)

Далее можно просто поделить эти объекты (непрерывные блоки данных) на несколько участков по времени или (что более вероятно) по количеству. Условно начинать записывать в новый блок после 100 сообщений.

Каждый из этих объектов можно хранить в условной табличке или в виде файлов и надеятся, что ФС не умрёт)


В общем если подумать можно много что интересного надумать

→ Ссылка
Автор решения: S.H.

Эта задача интересна, и, я уверен, она попадается на "архитектурных секциях" собеседований.

Есть два соображения:

  1. понятно, что "сообщения" должны храниться в единой таблице (таблица Messages).

Вместе с каждм сообщением должны храниться еще три поля:

DateTime - время (видимо, в формате UTC)

AuthorId - идентификатор того, кто написал сообщение

ChatId - это Id того чата, в который было отправлено сообщение.

Из этой таблицы можно получить содержимое любого чата. Достаточно сделать выборку по ChatId.

  1. Но, может оказаться, что делать такую выборку по кокретному чату по всей таблице Messages - слишком долго. Тогда можно сделать дополнительную таблицу, в которой будут храниться только сообщения текущего чата.

В терминологии DB это называется "денормализацией", а в современных DB есть еще понятие materialized view, предназначенных для этой же цели - быстрая выборка данных. При этом, каждое новое сообщение должно добавляться в два места:

  • в "общую таблицу" Messages (для всех сообщений) и

  • в таблицу сообщений конкретного чата

→ Ссылка