Архитектура базы данных мессенджера
Не очень понимаю, как хранить множественную информацию в бд, которая привязана к сильно менее множественной. Например, чат в мессенджере: у нас есть таблица чатов - 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 сообщений.
Каждый из этих объектов можно хранить в условной табличке или в виде файлов и надеятся, что ФС не умрёт)
В общем если подумать можно много что интересного надумать
Эта задача интересна, и, я уверен, она попадается на "архитектурных секциях" собеседований.
Есть два соображения:
- понятно, что "сообщения" должны храниться в единой таблице (таблица
Messages
).
Вместе с каждм сообщением должны храниться еще три поля:
DateTime - время (видимо, в формате UTC)
AuthorId - идентификатор того, кто написал сообщение
ChatId - это Id того чата, в который было отправлено сообщение.
Из этой таблицы можно получить содержимое любого чата. Достаточно сделать выборку по ChatId.
- Но, может оказаться, что делать такую выборку по кокретному чату по всей таблице
Messages
- слишком долго. Тогда можно сделать дополнительную таблицу, в которой будут храниться только сообщения текущего чата.
В терминологии DB это называется "денормализацией", а в современных DB есть еще понятие materialized view, предназначенных для этой же цели - быстрая выборка данных. При этом, каждое новое сообщение должно добавляться в два места:
в "общую таблицу" Messages (для всех сообщений) и
в таблицу сообщений конкретного чата