Как хранить чат в базе данных
Перейти к содержимому

Как хранить чат в базе данных

  • автор:

Система личных сообщений

Подскажите, пожалуйста, как устроена система личных сообщений между пользователями в крупных проектах (Вконтакте, Одноклассники, Topface и т.п) с учетом масштабирования? Интересует именно хранение данных о пользователях/чатах/сообщениях и доступ к этим данным.

Допустим у нас есть 100 000 000 пользователей и необходимо сделать горизонтальное масштабирование (шардинг) этих данных на 200 MySQL серверов. На данный момент, я вижу это следующим образом: разделяем всех пользователей и данные на 200 серверов по user_id, получается примерно 500 000 юзеров на каждый сервер. Можно еще разделить данные на споты по 1000 юзеров и получится, что на каждом сервере БД будет 500 спотов по 1000 юзеров (всего 500 000 юзеров на сервер).

Доступ к серверам БД/спотам можно вычислять по user_id, например spot_id = user_id % 1000. Каждый спот будет хранить данные в виде таблиц, например:

Spot1: — spot1_users (информация о пользователях) — spot1_chats (информация о чатах между пользователями) — spot1_messages (сообщения из чатов) .

Spot2: — spot2_users — spot2_chats — spot2_messages . Проблема возникает тогда, когда необходимо хранить/получать общие данные между юзерами. Например, 2 пользователя начинают переписку между собой. В этом случае необходимо создать чат в таблице spotN_chats и поместить туда информацию chat_id (id чата), receiver_id(id получателя), sender_id (id отправителя). Сообщения будут хранится в таблице messages (chat_id, message, time).

Теперь начинается самое интересное — пользователи начинают переписку между собой. Здесь необходимо сделать такие базовые операции: 1) Создание нового чата между 2 пользователями 2) Получение информации о чате или списке чатов конкретного пользователя 3) Создание нового сообщения 4) Получение списка сообщений по chat_id

Также есть 2 варианта развития событий: 1) пользователи находятся на одном споте (например, spot1); 2) пользователи находятся на разных спотах (например, spot1 и spot2);

Задача 1. Пользователь1 решил начать переписку с пользователем2. В этом случае необходимо создать новый чат в БД. Если пользователи на одном споте, то можно просто создать новый чат в таблице spot1_chats, получать chat_id, а дальше создавать новые сообщения в таблице spot1_messages с полученным chat_id. Но если пользователи находятся на разных спотах (spot1, spot2), то такой подход не будет работать, поскольку чтобы каждый пользователь увидел список своих чатов, то их нужно дублировать на 2 споты одновременно. Но в таком случае chat_id будут разными для 2 таблиц(spot1_chats, spot2_chats) если использовать поле autoincrement для chat или же нужно строить какой-нибудь общий для 100 млн. пользователей генератор id для новых чатов. Кроме того, если 2 пользователи на одном споте, то при дублировании чатов все равно будет создан только 1 чат в таблице spot1_users, а вот если мы решим перенести 1 пользователя на другой спот, то как дублировать информацию о чатах?

Задача 2. Пользователь1 отправляет сообщение пользователю2, chat_id у нас уже есть после создания нового чата. Здесь возникает та же самая проблема, что и в первой задаче. Если 2 пользователи на одном споте, то мы просто добавляем новое сообщение в таблицу spot1_messages, но если в будущем захотим перенести пользователя на другой спот, то как дублировать сообщения? Если же пользователи на разных спотах, то для отправки сообщения необходимо создать новое сообщение в таблице spot1_messages и в таблице spot2_messages. Кроме того, если мы хотим обновлять какой-нибудь счетчик новых сообщений или время последнего сообщения в чатах, то нужно будет также обновлять информацию о чатах в таблицах spot1_chats и spot2_chats. Получается для простой отправки одного сообщения необходимо будет сделать несколько запросов в БД, а именно: создать новое сообщения в таблицах spot1_messages и spot2_messages, а также обновить информацию о чате в таблицах spot1_chats и spot2_chats.

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

БД для хранения сообщений чата, какую выбрать?

Добрый день, делаю проект на Yii2, в нем уже реализован чат на комет сервере чат уже полностью работает, пользователи могут общаться только в приватных беседах т.е. только 2 пользователя друг с другом. При отправке сообщения, оно улетает аяксом в php скрипт, валидируется по определенным правилам и если все ок, записывает в таблицу бд и возвращает успех на клиентскую часть после чего уже сообщение попадает в окошко чата.

Структура бд:
tbl_dialog:
dialog_id — id Диалога
dialog_one_user_id — id первого юзера
dialog_two_user_id — id второго юзера
dialog_time — время создания диалога

tbl_message
chat_messages_id — id сообщения
chat_messages_text — текст сообщения
chat_messages_fk_dialog_id — id Диалога к которому относится сообщение
chat_messages_fk_user_id — id отправителя
chat_messages_fk_to_user_id — id получателя
chat_messages_ip — ip отправителя
chat_messages_isRead — прочитано ли сообщение получателем
chat_messages_isVisible_one_user — видимость сообщение для первого юзера
chat_messages_isVisible_two_user — видимость сообщение для первого юзера (для возможности отчисти сообщений, если просто удалять тогда сообщение пропадет из истории у обоих юзеров)
chat_messages_time — время отправки сообщения

Вся система работает с бд MySQL — InnoDB, сообщения пишутся в бд при каждой отправке (INSERT), пока сервис еще не запущен, сообщений мало (только мои тестовые) все работает шустро, но вот когда запущу и количество сообщений перевалит за несколько миллионов, что будет тогда с моей бд? Начнутся жесткие тормоза при select и insert?

Вот пока не поздно думаю стоит ли переписывать хранение сообщений на другую NoSQL БД, с ними не работал никогда, почитал доки и на первый взгляд ничего сложного.

В голове вертятся 2 варианта реализации:
1) перевести табличку tbl_message в коллекцию MongoDB и так же писать туда сообщения при каждой отправке (т.е. пользователь отправил 1 сообщение и мы его сразу в бд записали, по одной записи делать буду и так каждое сообщение). Как Mongo будет вести себя при вставке сообщения, когда в коллекции будет за 20 млн записей? Правильная ли эта схема?

1) Записывать каждое сообщение в память в виде массива сообщений (memcached или redis или еще что то — посоветуйте) и по крону допустим каждую минуту или 5 мин брать весь массив сообщений, писать через транзакцию в MySQL, далее чистим массив сообщений для новых записей. Так получается что не будет несколько инсертов в секунду, т.к. сообщения будут храниться в памяти. Но тут опять засада, сможет ли MySQL нормально работать с огромной таблицей 20млн записей?

Сам текст сообщений очень короткий, что то типа «Привет! Как дела?»

  • Вопрос задан более трёх лет назад
  • 19115 просмотров

3 комментария

Средний 3 комментария

Хранение сообщений для чатов

Author24 — интернет-сервис помощи студентам

добрый день
у меня возник такой вопрос » как хранить и быстро отдавать сообщения из чата»
изначально была идея хранить все в реляционной базе данных, но тут возникает такая проблема, что с некоторым временем таблица сообщений разрастется, и скорость отдачи замедлиться
была идея репликации по юзерам(стоит ли такое делать)
еще был вариант «перебрасывать пачками» из реляционной в не реляционную(например монгу), но тут возникает вопрос получения сообщений с пагинацией
что посоветуете опираясь на свой опыт ?
заранее спасибо

94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
Ответы с готовыми решениями:

Хранение сообщений в базе данных
Здравствуйте! Подскажите пожалуйста чайнику, как лучше сделать: Будет сайт (что-то типа соц.сети).

Как лучше сделать хранение сообщений в БД?
Чисто теоретический вопрос, как лучше сделать хранение сообщений в БД, например, для форума, такого.

есть ли бесплатные хостинги для ICQ чатов
привет всем, есть ли в наше время бесплатный хостинг для ICQ ЧАТОВ

Таблица под хранение информации о непрочитанных сообщений на форуме
Собственно нужно сделать мини-форум. И чтобы обязательно у каждого пользователя была статистика.

Как правильно хранить переписку в БД в данном случае?

Пишу проект на java. В качестве СУБД выбрал mySql. Возникла потребность в хранении переписки между пользователями. В программе присутствует только диалог 1:1 никаких комнат и ничего подобного. Я думаю правильно хранить полностью всю переписку в одной таблице, но как правильно это реализовать? Плюс мне нужно как-то формировать историю сообщений. Из идей у меня такое:
|логин_отправителя|логин_получателя|текст_сообщения|дополнительный_параметр_сообщения|номер_предыдущего_сообщения| .
Но вот мне не нравится такая конструкция по причине, что , чтобы добавить новое сообщение, мне нужно знать номер предыдущего. И вдруг пользователи в раз отправят сообщение, и что получится? что два сообщения под одним номером? Как вообще тогда формировать смс? Правильно ли я составил таблицу?

  • Вопрос задан более трёх лет назад
  • 897 просмотров

Комментировать
Решения вопроса 3

tsklab

Здесь отвечаю на вопросы.

Без дополнительной таблицы. В таблице сообщений хватит Код, кто, кому, время , что бы выбрать переписку.

не буду знать какое смс пришло раньше

То, у которого код меньше, если у вас точности время не хватает.
Ответ написан более трёх лет назад
Нравится 1 1 комментарий
TopMetaFizick_010 @TopMetaFizick_010 Автор вопроса

У каждого пользователя свой connectoion к бд, что произойдет если они в раз напишут друг-другу? В бд получится одно и тоже время? Если так, то как я буду формировать истроию переписки, порядок смс может перепутаться, т.к. я не буду знать какое смс пришло раньше? или я не прав?

JRK_DV

Рецепты https://codepen.io/jrkdv/full/LKLXdq

user = id | .
message = id | user_id | timestamp | msg | .

История чата:
SELECT msg FROM message WHERE user_id=uid1 OR user_id=uid2 ORDER BY timestamp;
где uid1\uid2 собеседники
при одинаковом времени, всё равно одна запись будет раньше, другая позже

Ответ написан более трёх лет назад
Нравится 1 1 комментарий
TopMetaFizick_010 @TopMetaFizick_010 Автор вопроса

Либо я не так понял, либо тут мы получаем только исходящие смс от uid1?
И у пользователя же может быть хоть какое количество диалогов. Как узнать сколько у него диалогов и с кем они? Пробегаться по всей таблице, и смотреть сколько уникальных uid2 у пользователя uid1?

Таблица такая: Messages(id, user_from_id, user_to_id, text, msg_time, . ).
Никаких предыдущих номеров Вам запоминать не нужно и никаких записей с одинаковым номером не будет. В таблице Messages поле id делаете автоинкрементным и забываете обо всем, что вас беспокоит.
Как получать историю:

select * from Messages where user_from_id = $id_пользователя or user_to_id = $id_пользователя order by id

Ответ написан более трёх лет назад
TopMetaFizick_010 @TopMetaFizick_010 Автор вопроса

Опять же, разве я не получаю таким способом только исходящие смс? Если входящие в бд записаны немного наоборот, т.е. user_from_id и user_to_id поменяны местами? Не бейте палкой, я просто новичок в этом деле, и с БД знаком очень поверхностно, да, знаю, что нужно изучить принцип работы, но пока на изучении стоят совсем другие технологии, а БД нужно просто привинтить к проекту для более менее стабильной работы.

TopMetaFizick_010, нет, таким образом вы получаете всю историю сообщений для какого-то пользователя. Допустим есть пользователь Вася и его И допустим он написал Диме у которого сообщение. Тогда в таблицу попадает запись, где user_from_id=52(кто отправил — Вася), а user_to_id=105(кому отправили — Диме). А если Дима напишет Васе то запись будет выглядеть наоборот — user_from_id=105(кто отправил — Дима), а user_to_id=52(кому отправили — Васе). Так вот запрос, который я привел выше — если Вы в него подставите id Васи то есть 52, то он вернет все сообщения, где встречается среди отправителей(«user_from_id = $id_пользователя») и все сообщения, где встречается среди получателей(«user_to_id = $id_пользователя»), то есть вернет всю историю сообщений Васи

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *