Извините, ничего не найдено.

Не расстраивайся! Лучше выпей чайку!
Регистрация
Справка
Календарь

Вернуться   www.boolean.name > Программирование в широком смысле слова > Алгоритмика

Алгоритмика Об алгоритмах вообще; методы, обсуждения способов решения

Ответ
 
Опции темы
Старый 17.10.2015, 08:32   #1
St_AnGer
Элита
 
Аватар для St_AnGer
 
Регистрация: 21.01.2010
Адрес: Россия, Рязанская область, г.Михайлов
Сообщений: 2,024
Написано 1,158 полезных сообщений
(для 2,780 пользователей)
Чат: PHP + MySQLi или что то другое?

Добрый день, Булка!

В целях развития решился я тут снова приняться за чатик аля Аська или (если брать из известного среди ньюфагов) Viber. Ну как чатик - полноценную систему обмена сообщениями с сохранением всего и вся чем обмениваются пользователи. Этот же чат будет и для яОСи (где то на Булке я уже выкладывал скрин меню по яОСь), и, в последующем, наверно, под Ведроид. Но не суть. Все наработки которые у меня были под яОСь были мной отвергнуты ввиду своей убогости и неоправданной сложности. В общем, начинаю с нуля.

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

На данный момент пишу веб-клиент (так как его куда проще реализовать, чем приложение под мобилки) и общий сервер на всё. Что есть в планах на реализацию:
- регистрация
- авторизация
- активация пользователя пятью голосами других пользователей или одним голосом администратора (изначально планировалось как корпоративный чат, посему лишних болтунов в нём не надо)
- просмотр списка неактивированных пользователей
- поиск пользователей
- добавление пользователей в список
- удаление пользователей из списка
- добавление групп пользователей (привет, Аська!)
- изменение групп пользователей (пока что только изменение названия группы)
- перенос пользователей из контактного списка в созданные группы
- удаление групп (вместе со всеми пользователями что в ней состоят)
- создание бесед
- запрос списка бесед в которых состоит пользователь
- отсылка сообщений в беседы
- запрос списка сообщений в беседе

Сейчас имею реализованные с лёгкостью и полностью оттестированные первые 11 пунктов, уткнулся на создании бесед.
Беседы будут создаваться двумя путями:
1) беседа из отдельных выбранных пользователем участников;
2) беседа с выбранной группой пользователей.
С этим проблем нет, приведение к общему списку пользователей на серверной стороне я реализовал. Но, беседы ведь не должны дублироваться, вот тут у меня и начались сложности. Нужно перед созданием новой беседы произвести поиск уже существующей, по сформированному массиву пользователей и, исходя из результатов, или вернуть id старой беседы, или создать новую.


Вот конкретно что не знаю и не очень понимаю.

Пункт 1.
Каким образом организовать структуру таблиц базы данных для этого?
Есть 2 мысли:
а) таблица "chats" в которой есть поле owner (собственно тот кто создал) и поле users (в ней хранится сортированный json-массив id пользователей состоящих в диалоге)
б) таблица "chats" (есть аналогичное поле owner) и таблица "chat_users" (содержит записи о пользователях в конкретных беседах)

Чем нравится вариант "а":
- нет лишней таблицы
- данные о беседе и список её пользователей хранятся в одной записи, простой поиск по id беседы
- довольно простой поиск беседы зная только список пользователей из ней (сформировали json-массив и спокойно ищем по полю "users")
Чем не нравится вариант "а":
- "сложность" извлечения списка пользователей. Сложность в том, что сначала надо декодировать массив пользователей и только потом делать запрос к базе данных что бы узнать их имена и данные для отображения
- вытекающая из предыдущего проблема поиска списка бесед в которых состоит текущий пользователь

Чем нравится вариант "б":
- простота поиска бесед в которых состоит текущий пользователь
- простота извлечения (одним запросом) данных пользователей зная id беседы (SELECT по id беседы и пара JOIN-ов для данных пользователей)
Чем не нравится вариант "б":
- очень сложный и долгий поиск беседы зная только список пользователей из неё

Извлечение данных пользователей беседы так же важно, как и поиск самой беседы по списку пользователей (как писал выше - такой поиск необходим при создании беседы).


Пункт 2.
Как организовать саму переписку в беседе? Допустим, как добавить сообщение в базу данных понятно - INSERT и всё. Но как реализовать правильную выборку сообщений для пользователей состоящих в беседе? Если в беседе 1 пользователь - всё просто, один запрос. А если 10 пользователей - то это уже 10 запросов за одними и теми же данными. Вопрос скорости работы при 30-40 пользователях встанет ребром, а при 100 и более - сервер умрёт. Расширение мощности сервера не вариант - чаты существуют очень давно и изначально они были реализованы на машинах с мощностью меньшей, чем нынешние калькуляторы. Однако работало всё на ура.

В наличии VPS, его конфигурация такова: 2 ГГц проц, 1 ГБ оперативы, 10 гигов пространства на HDD, Debian в качестве ОСи, никаких "иксов", только консоль. Серверную сторону пишу на PHP + MySQLi (возможно заменю на Firebird SQL, очень уж я к нему прикипел на предыдущей работе).

Буду очень рад советам по этому направлению, потому что знаний по нему у меня чуть больше чем ноль. Читал статьи на хабре, много думал, рисовал разных схем, искал структуру и принцип работы Аськи, ВКонтакта, Фейсбука.

Сегодня узнал про некий Redis (нереляционная БД в ОЗУ), но как его готовить и с чем его употреблять - пока что представления не имею, хотя её данные мне очень понравились, особенно скорость работы (естественно она в десятки раз быстрее, чем любая хранимая на HDD база данных). Интересно было бы узнать мнение работавших/работающих с ним. И ещё было бы интересно узнать как из Redis в MySQLi данные перекочевряжить (лично мне удобнее читать из базы данных данные, чем из файлов "снимков" данных из ОЗУ), хотя скрипт переноса на том же PHP я скорее всего тоже накатаю довольно быстро.
__________________
Main PC:
Intel Core i5 4260U 1.44 GHz + LPDDR3 1x4096 1600 MHz + Intel HD Graphics 5000.

Asus Ёжик T101-MT:
Intel Atom N-570 1.66 Ghz + DDR2 2x1024 800 Mhz + Intel GMA 3150 128 Mb DDR2


Скачать Doom 2D: Remake v0.3.8a
(Offline)
 
Ответить с цитированием
Старый 17.10.2015, 13:56   #2
h1dd3n
Бывалый
 
Аватар для h1dd3n
 
Регистрация: 19.06.2008
Сообщений: 677
Написано 263 полезных сообщений
(для 448 пользователей)
Ответ: Чат: PHP + MySQLi или что то другое?

Во-первых с точки зрения удобства пользования:

Зачем нужно проверять беседу при создании на "дубликацию" ?
Если в скайпе ты создал конференцию A в которой есть Ты, Вася и В.В. Путин, то при создании конференции Б в которой есть все те же товарищи не должно быть ошибки - это же 2 разные конференции, и там сообщения разные. Или если у тебя есть 2 друга и вы вместе сидите в конференции "Беседка C++" (вас там только 3) и также вы сидите в конференции "Беседка PHP" (вас там тоже только 3) никаких проблем в этом нету. 2 разные конференции - 2 разные чата (хоть и набор пользователей идентичен).

И к тому же групп. чаты (беседы, комнаты, конференции) как правило не создаются с указанием пользователей сразу - ты сначала создаешь чат "Обсуждение Fallout 4", а потом туда пользователей добавляешь (или удаляешь).

Во-вторых насчет структуры в БД:

Если у тебя реляционная база, то классическим вариантом будет users, chats, chat_user_links. В users - пользователи, в chats - чаты, в chat_user_links связи пользователей и чатов (в минимальном варианте 2 колонки chatId, userId).
В этом варианте у тебя:
Быстрый поиск всех пользователей в чате.
Быстрый поиск всех чатов у пользователя.
Что еще нужно?

В-третьих насчет "переписки":

Сама переписка к базе никакого отношения не имеет.
Переписка это получение и доставка сообщений (как 1х1 так и 1хдохрена).
И тут может быть (на факт что обязательно будет, но может появится) проблема в PHP. Весь вопрос в том как ты собираешься в php держать соединения с пользователями (само tcp соединение конечно держит http сервер, но суть в том как устроена обработка событий с этих соединений). Есть разные подходы, и если на малом кол-ве клиентов они все дают одинаковый результат то при увеличении некоторые варианты дают совсем херовый результат (тут тебе придется изучить технологию на которой ты пишешь)

Когда ты написал про базу, я так понимаю ты имел в виду "где и как хранить историю? и каким образом отдавать ее юзерам?".
В целом ответ 1 - хранить историю на клиенте (это можно сделать и веб приложении, а в мобильном/десктопном и подавно). Если прям нужно чтобы и на сервере хранилась, то записывать и на сервер и на клиент. Тогда с сервера тянуть историю будут только те у кого ее по каким-то причинам нет (только что присоединившийся пользователь, пользователь который у себя историю потер и т.д.). В этом случае нагрузка будет уже совсем не большая.
__________________
(Offline)
 
Ответить с цитированием
Сообщение было полезно следующим пользователям:
St_AnGer (17.10.2015)
Старый 18.10.2015, 01:42   #3
moka
.
 
Регистрация: 04.08.2006
Сообщений: 10,429
Написано 3,454 полезных сообщений
(для 6,861 пользователей)
Ответ: Чат: PHP + MySQLi или что то другое?

Много текста..

Для чата нужен real-time - это главное.
Следственно делать чат на PHP + MySQL - это как делать ММО на PHP, т.к. клиенту нужно будет постоянно спрашивать о сообщениях - AJAX. Постоянные HTTP запросы - не нужный overhead.
Это можно обойти Long Poll'ом или WebSocket'ами, но читай дальше.

PHP + MySQL. Как понимаю MySQL будет использоваться посредником для хранения данных и обменом между запросами к PHP. В любом случае PHP скрипту прийдется постоянно спамить MySQL на наличие новых сообщений. При каждом запросе от клиента или при фиксированном периоде если используется Long Poll или WebSockets подход.
Короче - постоянные запросы в огромные таблицы MySQL - просто не нужный overhead снова.

Как делается чат:
Начинается все только с real-time основы. Тебе нужно позаботиться о двух вещах:
1. real-time рассылка с минимальным overhead'ом.
2. Горизонтальное масштабирование, т.к. при больших нагрузках одного сервера тебе никогда не хватит.

1 - решается очень просто - не используй никаких посредников, а используй pub/sub паттерн, и при получении сразу рассылай клиентам которые подписаны на канал (комнаты), или шли напрямую на соединения (их может быть несколько), клиенту которому адресовано сообщение.

2 - сложнее. Тут у тебя может быть много процессов, с большим числом клиентов каждый. Тебе нужно сделать систему routing'а сообщений, т.к. иногда у тебя может оба общающихся клиента быть в одном процессе, а могут быть на разных. В таком случае нужно делать Routing систему, где сообщения будут толкаться серверам которым нужно пересылать сообщения. Использовать одну точку для messaging pub/sub, например redis - не смасштабируется при огромной нагрузки, но хватит на очень много. Рассматривай варианты где каждый сервер будет узнавать о разных изменениях стейтов клиентов, или использовать routing карту чтобы узнать списки серверов (может быть несколько снова), исходя из пользователя которому шлем или комнаты.
С комнатами лучше всех клиентов комнаты на одном сервере держать - так проще.
Также можно использовать MQ систему: RabbitMQ, ZeroMQ и т.п.

Только затем идет сохранение сообщений, и история.
"недавняя" история, типо последние 10 сообщений нужно держать в памяти все равно на серверах, чтобы избежать необходимости обращаться к базе данных при новых клиентах для получения недавних сообщений.
История отдельно сохраняется на сервере и никак не влияет на систему рассылок и т.п.

Далее тебе нужно будет позаботиться о push notifications системе. Учитывая что пользователь может быть на нескольких клиентах залогинен сразу (web и android например). А может быть и не одном из них.
Тут нужно быть окуратным. Если ты общаешься в web'е нормально, а на android у тебя клиент выключен, тебе не нужно слать push notification на android, т.к. пользователь уже прочитал сообщение в web'е.


Если честно, делать такое на PHP + MySQL это подход ранних 2000, это как делать ММО на PHP.
Не стоит.
Используй для этого подходящие инструменты: erlang, redis, cassandra.
Прототип чата на node.js + sockjs пишеться буквально за 20 минут.
Тебе не нужня реляционная база данных тут, лучше использоваь NoSQL который хорошо подходит к огромным объемам данных (чат - это много реал-тайм данных).

Также хранить историю переписки на серверах - учитывай что это весьма серъезное решение. Тут могут быть легальные и этические вопросы.
(Offline)
 
Ответить с цитированием
Сообщение было полезно следующим пользователям:
St_AnGer (18.10.2015)
Старый 18.10.2015, 20:26   #4
St_AnGer
Элита
 
Аватар для St_AnGer
 
Регистрация: 21.01.2010
Адрес: Россия, Рязанская область, г.Михайлов
Сообщений: 2,024
Написано 1,158 полезных сообщений
(для 2,780 пользователей)
Ответ: Чат: PHP + MySQLi или что то другое?

Много полезной информации почерпнул, перепланировал сильно дальнейшую разработку, спасибо вам!

PHP + MySQL были выбраны из за того, что ими я владею на уровне среднего писаря (в отличии от всех остальных технологий для вебчика и серверов). Так то хочется чего то нового и более подходящего, как раз Redis хочу попробовать.

Но и от MySQL не хочу полностью отказываться, потому что мне с ней общаться удобно (на данный момент), в ней можно хранить самих пользователей и историю сообщений, а онлайн-часть держать в той же Redis, изредка (раз в 3-5 минут) сбрасываю инфу в MySQL. Мне кажется это удобным (ну, опять же на данный момент и с моими текущими знаниями). На счёт "моральных" проблем хранения истории - это не мои проблемы. Вайберу можно, а мне нет? На самом деле первоначальным условием написания чата было поставлено именно хранение истории.

До node.js всё никак не доберусь, надо его пощупать будет, и, возможно им и пользоваться для организации самого api, а не старым-добрым пыхом. Просто на данный момент я вообще не представляю что это такое, как и с чем его едят, есть ли под него библиотеки для "Push Notifications", в общем - ничего о нём не знаю.

С веб.мордой соединения держать нужно, с мобилками - не обязательно (приоритетная платформа яОСь, а там есть чудесные push notifications, на которых и был реализован мой первый тестовый чат). С постоянными соединениями у меня пока что проблемы, потому и не пишу эту часть, а выбираю технологии на которых будет удобно и гибко возможно реализовать задуманное.

Горизонтальное масштабирование - штука крутая, но при серьёзных нагрузках. У меня в чате, если его реально распространить по предприятию, будет 200-300 человек. Думаю такую нагрузку при грамотной организации даже PHP+MySQL потащит на нынешнем железе. Но это мелочи, надо делать сразу с масштабируемостью, согласен. Даже если не взлетит - опыт будет получен колоссальный.

UPD собственно, только что сбацал на VPS себе node.js, начинаю изучать что к чему. Одна беда - пых я знаю, а node.js в глаза не видел (js конечно умею, но не на столько, чтоб сервер писать).
__________________
Main PC:
Intel Core i5 4260U 1.44 GHz + LPDDR3 1x4096 1600 MHz + Intel HD Graphics 5000.

Asus Ёжик T101-MT:
Intel Atom N-570 1.66 Ghz + DDR2 2x1024 800 Mhz + Intel GMA 3150 128 Mb DDR2


Скачать Doom 2D: Remake v0.3.8a
(Offline)
 
Ответить с цитированием
Сообщение было полезно следующим пользователям:
moka (18.10.2015)
Старый 18.10.2015, 22:08   #5
moka
.
 
Регистрация: 04.08.2006
Сообщений: 10,429
Написано 3,454 полезных сообщений
(для 6,861 пользователей)
Ответ: Чат: PHP + MySQLi или что то другое?

По node'у полным полно примеров, простых модулей и т.п.
Главное начать.

То что будет 200-300 человек, ты не упомянул однако ранее или я не дочитал :D
Тогда да, можно чатик за пару дней с клиентами и всеми фичами написать. Или даже что-то готовое поднять.
Push Notifications - не нужно использовать когда клиент открыт, т.к. он должен соединяться.
PN - нужен только если клиент вырублен и пользователь не был активен на других клиентах. ~5 сек задержки после активности все же нужно, чтобы избежать ситуации где прочитал в вебе, а он еще и на мобилку пушает.

Если речь идет о 200-300 человек, то и redis не нужен. Поднимай один процесс и храни все в памяти.
Бд чисто для аутентификации и хранения истории.
С nodejs mongodb идет очень легко и гладко. Ты пример какой-нибудь глянь, там все просто.
var mongo = require('mongodb').MongoClient;
var 
url 'mongodb://localhost:27017/mydb';

mongo.connect(url, function(errdb) {
    if (
err) throw err;
    
console.log("connected to db");

    
db
    
.collection('users')
    .
findOne({
        
login'user',
        
password'hash'
    
}, function(erruser) {
        if (
err) throw err;

        if (! 
user)
            return 
console.log('wrong login / password');

        
console.log(user);
    });
}); 
Соединение конечно держать нужно одно, а не много (как в пхп). Ну и тут пример как найти поьзователя с бд.
Крч, копай и пробуй, примеров в интернете куча, и на форуме помогу если где-то застопоришься.
(Offline)
 
Ответить с цитированием
Сообщение было полезно следующим пользователям:
St_AnGer (19.10.2015)
Старый 19.10.2015, 06:36   #6
St_AnGer
Элита
 
Аватар для St_AnGer
 
Регистрация: 21.01.2010
Адрес: Россия, Рязанская область, г.Михайлов
Сообщений: 2,024
Написано 1,158 полезных сообщений
(для 2,780 пользователей)
Ответ: Чат: PHP + MySQLi или что то другое?

Сообщение от moka Посмотреть сообщение
То что будет 200-300 человек, ты не упомянул однако ранее или я не дочитал :D
Тогда да, можно чатик за пару дней с клиентами и всеми фичами написать. Или даже что-то готовое поднять.
Сколько будет человек - дела не меняет. Хочу сделать всё грамотно, что бы в теме понимать, и, что бы работало как надо если вдруг будет решено запускать это дело в ход. Чужое (готовое) можно поднять конечно, это проще, но... Мне надо самому научиться, яжпрограммист!

Node.js поднял, кое как стартовал на субдомене своём. Можешь посоветовать, нормальный ли способ я выбрал для этого (оно взлетело и работает, но всё же)? Порт 3000-й выбран осознанно, субдомен api2 тоже осознанно выбран (на api висит текущая версия на php+mysql).
var subdomain = require('express-subdomain');
var express = require('express');
var app = express();

var router = express.Router();

router.get('/', function(req, res) {
	res.send('trololo');
});

app.use(subdomain('api2', router));
app.listen(3000);
UPD Прототип "чата" на node.js + socket.io написал реально за 30 минут, из которых 20 изучал что такое node.js и socket.io. Учимся дальше...

UPD2 Грусть пичаль таска абида. Mongo DB не взлетает на VPS основанной на OpenVZ (установлена Debian 8.2).

UPD3 после плясок с бубном - Mongo DB взлетела. Учимся дальше...
__________________
Main PC:
Intel Core i5 4260U 1.44 GHz + LPDDR3 1x4096 1600 MHz + Intel HD Graphics 5000.

Asus Ёжик T101-MT:
Intel Atom N-570 1.66 Ghz + DDR2 2x1024 800 Mhz + Intel GMA 3150 128 Mb DDR2


Скачать Doom 2D: Remake v0.3.8a

Последний раз редактировалось St_AnGer, 19.10.2015 в 10:54.
(Offline)
 
Ответить с цитированием
Сообщение было полезно следующим пользователям:
moka (19.10.2015)
Старый 19.10.2015, 12:18   #7
moka
.
 
Регистрация: 04.08.2006
Сообщений: 10,429
Написано 3,454 полезных сообщений
(для 6,861 пользователей)
Ответ: Чат: PHP + MySQLi или что то другое?

Сообщение от St_AnGer Посмотреть сообщение
Сколько будет человек - дела не меняет. Хочу сделать всё грамотно, что бы в теме понимать, и, что бы работало как надо если вдруг будет решено запускать это дело в ход. Чужое (готовое) можно поднять конечно, это проще, но... Мне надо самому научиться, яжпрограммист!
Учиться - верно, всегда хорошо. Но многие вещи не нужно делать до того как у тебя будет в них необходимость, иначе и не дойдешь до первого релиза.

Сообщение от St_AnGer Посмотреть сообщение
Node.js поднял, кое как стартовал на субдомене своём. Можешь посоветовать, нормальный ли способ я выбрал для этого (оно взлетело и работает, но всё же)? Порт 3000-й выбран осознанно, субдомен api2 тоже осознанно выбран (на api висит текущая версия на php+mysql).
var subdomain = require('express-subdomain');
var express = require('express');
var app = express();

var router = express.Router();

router.get('/', function(req, res) {
	res.send('trololo');
});

app.use(subdomain('api2', router));
app.listen(3000);
UPD Прототип "чата" на node.js + socket.io написал реально за 30 минут, из которых 20 изучал что такое node.js и socket.io. Учимся дальше...

UPD2 Грусть пичаль таска абида. Mongo DB не взлетает на VPS основанной на OpenVZ (установлена Debian 8.2).

UPD3 после плясок с бубном - Mongo DB взлетела. Учимся дальше...
У тебя nginx или apache стоит на сервере? Если nginx, то там легко делается прокси, по субдомену, таким образом порт 3000 в ссылке не нужно будет иметь.
(Offline)
 
Ответить с цитированием
Сообщение было полезно следующим пользователям:
St_AnGer (19.10.2015)
Старый 19.10.2015, 13:00   #8
St_AnGer
Элита
 
Аватар для St_AnGer
 
Регистрация: 21.01.2010
Адрес: Россия, Рязанская область, г.Михайлов
Сообщений: 2,024
Написано 1,158 полезных сообщений
(для 2,780 пользователей)
Ответ: Чат: PHP + MySQLi или что то другое?

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

У тебя nginx или apache стоит на сервере? Если nginx, то там легко делается прокси, по субдомену, таким образом порт 3000 в ссылке не нужно будет иметь.
У меня довольно жестоко сделано - связка nginx+apache. Nginx под статику, apache под динамику. Но основной Nginx. Собственно, уже сделал чтоб порт не прописывать, за наводку спасибо, что то даже не подумал про это
__________________
Main PC:
Intel Core i5 4260U 1.44 GHz + LPDDR3 1x4096 1600 MHz + Intel HD Graphics 5000.

Asus Ёжик T101-MT:
Intel Atom N-570 1.66 Ghz + DDR2 2x1024 800 Mhz + Intel GMA 3150 128 Mb DDR2


Скачать Doom 2D: Remake v0.3.8a
(Offline)
 
Ответить с цитированием
Старый 19.10.2015, 13:34   #9
moka
.
 
Регистрация: 04.08.2006
Сообщений: 10,429
Написано 3,454 полезных сообщений
(для 6,861 пользователей)
Ответ: Чат: PHP + MySQLi или что то другое?

Для прокси WebSocket трафика там доп. настройки нужны тоже:
http {
    
upstream chatsrv {
        
server 127.0.0.1:3000;
    }

    
server {
        
listen 80;
        
server_name sub.domain.com;

        
location / {
            
proxy_pass          http://chatsrv/;

            
proxy_set_header    Host $host;
            
proxy_set_header    Upgrade $http_upgrade;
            
proxy_set_header    Connection "upgrade";
            
proxy_set_header    X-Real-IP $remote_addr;
            
proxy_set_header    X-Forwarded-For $proxy_add_x_forwarded_for;
            
proxy_set_header    X-NginX-Proxy true;
        }
    }

(Offline)
 
Ответить с цитированием
Сообщение было полезно следующим пользователям:
St_AnGer (19.10.2015)
Старый 20.10.2015, 06:35   #10
St_AnGer
Элита
 
Аватар для St_AnGer
 
Регистрация: 21.01.2010
Адрес: Россия, Рязанская область, г.Михайлов
Сообщений: 2,024
Написано 1,158 полезных сообщений
(для 2,780 пользователей)
Ответ: Чат: PHP + MySQLi или что то другое?

И внезапно возник вопрос. Пока я не углубился в кущи MongoDB хотелось бы узнать, а подходит ли РЕАЛЬНО документарная база данных для сложного чата ("чат" звучит очень грубо, скорее микрокопия фейсбука без ленты активности, о чём я, разумеется забыл упомянуть выше... да хотя сейчас даже старая-добрая Аська уже не просто банальная чатилка, какой была раньше)? Ведь у меня будут пользователи и информация о пользователях (которая возможно будет меняться), будут группы и информация о группах (которая возможно будет меняться), в конце концов будут сообщения, которые будут содержать отправителя (информация о котором будет будет возможно меняться).

В общем, проведя глубокий анализ и прочитав статью про "Диаспору" (вот прям только что) уже не очень уверен, что MongoDB правильный выбор для меня. Даже банальный (банальный ли?) MySQL больше подходит своей реляционностью, лично мне так кажется. А MongoDB больше подходит под справочники (аля Кинозал) своей документарностью. Если же делать примерно что запланировал я, то будет очень много дублирования данных в тех же беседах и сообщениях (сообщение ведь содержит отправителя, а это значит что и всю инфу о нём, если следовать парадигме денормализации данных). А в случае если отправитель ВНЕЗАПНО решит поменять инфу о себе (добавил/изменил ник/дату рождения/пол/да что угодно о себе), то ПРИДЁТСЯ менять всю эту инфу во всех документах где этот пользователь фигурирует. Можно конечно хранить ссылки на пользователей в беседах и сообщениях, это позволит избежать дублирования данных, но абсолютно уничтожит все полюсы документарной СУБД превратив её в реляционную (причём ещё и с отсутствием "аппаратных" джоинов, что приводит к "программным" джоинам в коде).

Вывод: денормализация это хорошо, но там где она нужна. Сейчас я прихожу к выводу что мне денормализация не нужна никоим образом, а значит и плюсов от использования MongoDB я особых не получу.

Пойду почитаю про PosgreSQL...

Кстати, понял что я пытался сделать в первой версии своего "чата". Я пытался сделать что то среднее между реляционной и документарной структурой данных , что бы если надо было добавить к какой то записи ещё одно поле - ненадо было добавлять его ко всем записям (используя MySQL хранил, например, в сообщениях JSON о отправителе с краткой информацией, которую можно было просто расширить добавив конкретно в это сообщение в JSON нужное поле), и как раз наткнулся на беду, описанную мной выше... Невозможность человеческого изменения данных, простого, без тучи кода по изменению ВСЕХ мест где используются эти данные.
__________________
Main PC:
Intel Core i5 4260U 1.44 GHz + LPDDR3 1x4096 1600 MHz + Intel HD Graphics 5000.

Asus Ёжик T101-MT:
Intel Atom N-570 1.66 Ghz + DDR2 2x1024 800 Mhz + Intel GMA 3150 128 Mb DDR2


Скачать Doom 2D: Remake v0.3.8a
(Offline)
 
Ответить с цитированием
Старый 20.10.2015, 07:52   #11
pax
Unity/C# кодер
 
Аватар для pax
 
Регистрация: 03.10.2005
Адрес: Россия, Рязань
Сообщений: 7,505
Написано 2,960 полезных сообщений
(для 5,247 пользователей)
Ответ: Чат: PHP + MySQLi или что то другое?

Это какие такие дублирования данных? Можно же id записей одной коллекции хранить в полях другой. Запросы реляционные не сделать это да, но а часто это не нужно. Получил список сообщений, получи для них пользователей отдельным запросом.

PS: даже не думай о дублировании данных. Такой подход в любой СУБД не правильный.
__________________
Blitz3d to Unity Wiki
(Offline)
 
Ответить с цитированием
Эти 2 пользователя(ей) сказали Спасибо pax за это полезное сообщение:
moka (20.10.2015), St_AnGer (20.10.2015)
Старый 20.10.2015, 08:28   #12
St_AnGer
Элита
 
Аватар для St_AnGer
 
Регистрация: 21.01.2010
Адрес: Россия, Рязанская область, г.Михайлов
Сообщений: 2,024
Написано 1,158 полезных сообщений
(для 2,780 пользователей)
Ответ: Чат: PHP + MySQLi или что то другое?

Сообщение от pax Посмотреть сообщение
Это какие такие дублирования данных? Можно же id записей одной коллекции хранить в полях другой. Запросы реляционные не сделать это да, но а часто это не нужно. Получил список сообщений, получи для них пользователей отдельным запросом.
Можно то можно, но в чём тогде смысл документарной базы данных, если мы из неё делаем реляционную (не избыточную и целостную), просто на "программном" уровне (в коде приложения), а не на аппаратном (в архитектуре самой СУБД). Причём вот такой запрос в той же MySQL
SELECT a.id, 
           a.name 
       FROM chat AS a
       LEFT JOIN chat_users AS b ON b.chat_id = a.id
   WHERE b.user_id = 1
(поля chat - id, name;
поля chat_users - id, chat_id, user_id)

Отработает горррраздо быстрее и проще, чем я сначала отдельным запросом запрошу документ пользователя с user_id = 1, а вторым запросом я вытащу документ чата, в котором найду "чаты" данного пользователя.
Я почему то твёрдо уверен что документарная структура мне не очень хорошо подходит.
Вот картинка (с хабра, не моя), описывает банальную ситуацию с лайками и коментами (я конечно не делаю лайки, но ситуация схожа):


То что выделено зелёным - имеет один и тот же тип данных (то есть users). То есть, что бы достать инфу о всех лайках и комментариях у пользователя, надо:
- или хранить полностью всю инфу о самих комментерах и лайкерах следуя парадигме документарной СУБД, что бы все данные вытаскивать одним документом (дублирование (читай денормализация, избыточность данных) данных + чревато если лайкер/комментер, например, сменил какие то свои данные, например имя);
- или хранить ключи (id) на самих комментеров и лайкеров, но тогда придётся писать свой собственный "программный" JOIN, что является лишним гемороем и убивает основную фичу документарной СУБД - скорость за счёт доставания инфы одним документом (то есть попытку приведения документарной СУБД к недореляционной).

Цитата с хабра (перевод статьи про Диаспору), как раз именно так я и понимаю использование MongoDB для моей задачи:

Примерно так выглядит плотностью денормализованная лента активности.


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

....

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

При этом подходе вместо встраивания данных там, где они нужны, вы даете каждому пользователю ID. После этого вместо встраивания данных пользователя вы сохраняете только ссылки на пользователей. На картинке ID выделены зеленым:

(MongoDB фактически использует идентификаторы BSON — строки, похожие на GUID. На картинке числа, чтобы легче было читать.)

Это исключает нашу проблему дублирования. При изменении данных пользователей, есть только один документ, который нужно изменить. Тем не менее, мы создали новую проблему для себя. Потому что мы больше не можем построить ленту активности из одного документа. Это менее эффективное и более сложное решение. Построение ленты активности в настоящее время требует, чтобы мы 1) получить документ ленты активности, а затем 2) получили все документы пользователей, чтобы заполнить имена и аватары.


Ну, то есть, на реляционной СУБД конкретно такая задача не проблема вообще (заводим несколько таблиц и джоинами вытаскиваем нужные данные). Мой личный вывод по MongoDB - крутая хранилка не связанных друг с другом JSON-данных (аля медиатека). Если данные в двух разных хранилищах связаны какими то ключами друг с другом - значит мы используем MongoDB как реляционную СУБД, то есть не по назначению. Вот так я понимаю работу с MongoDB.

зыЖ Я ни в коем случае не говорю что MongoDB это плохо (во первых, - я с ней знаком всего 2 дня, во вторых - да кто я такой, что бы это говорить) и скорее всего не понимаю всех плюсов документарной СУБД и буду рад если мне кто нибудь примерно зарисует/распишет как надо правильно им пользоваться в моём направлении. Полную структуру что должно храниться и в какие моменты извлекаться могу расписать.
На любой SQL-СУБД реализую всё что мне надо довольно просто и быстро, с сохранением целостности и нормализации данных в случае какого либо изменения этих данных, чего абсолютно не гарантирует денормализованная структура (а если делать нормализованную структуру - то смысл в MongoDB для этой задачи отпадает).
__________________
Main PC:
Intel Core i5 4260U 1.44 GHz + LPDDR3 1x4096 1600 MHz + Intel HD Graphics 5000.

Asus Ёжик T101-MT:
Intel Atom N-570 1.66 Ghz + DDR2 2x1024 800 Mhz + Intel GMA 3150 128 Mb DDR2


Скачать Doom 2D: Remake v0.3.8a
(Offline)
 
Ответить с цитированием
Старый 20.10.2015, 08:53   #13
pax
Unity/C# кодер
 
Аватар для pax
 
Регистрация: 03.10.2005
Адрес: Россия, Рязань
Сообщений: 7,505
Написано 2,960 полезных сообщений
(для 5,247 пользователей)
Ответ: Чат: PHP + MySQLi или что то другое?

ИМХО всякие вконтакты и фейсбуки достают дополнительную инфу о лайках по необходимости отдельными AJAX запросами. В момент, когда ты наводишься на кнопку лайка. Получать их одним запросом и перегружать страницу избыточными данными не нужно. Для отображения списка сообщений тебе достаточно два запроса: сами сообщения + их авторы. Всю дополнительную инфу можно получить позже отдельными запросами в момент, когда они понадобятся. В самих сообщениях можно хранить счетчики лайков и только.

PS: А вообще я понял кажется твое сообщение. Зачем хранить в базе данных то, в каком чате какой пользователь? Как сказал мока, это реалтаймовая фигня. Ее надо хранить в памяти сервера, а не в базе. При отсылке сообщения в комнату сервер будет в базу только писать само сообщение и не будет выдергивать из нее пользователей, кому это сообщение нужно доставить. Доставляется сообщение только подключенным пользователям.
__________________
Blitz3d to Unity Wiki
(Offline)
 
Ответить с цитированием
Сообщение было полезно следующим пользователям:
St_AnGer (20.10.2015)
Старый 20.10.2015, 11:10   #14
moka
.
 
Регистрация: 04.08.2006
Сообщений: 10,429
Написано 3,454 полезных сообщений
(для 6,861 пользователей)
Ответ: Чат: PHP + MySQLi или что то другое?

pax правильно говорит - сообщению не нужно знать имя пользователя. Нужно знать только ID.
Если client-1 не знает о user-1, то это забота сервера рассказать ему о user-1. Таким образом client-1 без проблем должен просто получать сообщения с userId, которые это уже работа клиента заменять на username.

Тут не нужна реляционная работа, и не нужны никакие join'ы.
Все должно быть примитивно и связано на ID.
Слать username с сообщениями - просто не нужно. Тем более избыточно.
(Offline)
 
Ответить с цитированием
Сообщение было полезно следующим пользователям:
St_AnGer (20.10.2015)
Старый 20.10.2015, 12:19   #15
St_AnGer
Элита
 
Аватар для St_AnGer
 
Регистрация: 21.01.2010
Адрес: Россия, Рязанская область, г.Михайлов
Сообщений: 2,024
Написано 1,158 полезных сообщений
(для 2,780 пользователей)
Ответ: Чат: PHP + MySQLi или что то другое?

Принцип понял, наверно.

Сейчас, распишу что понял и задам вопросы, если не затруднит - ответьте и поправьте ошибки в логике.
Базу буду использовать примерно как использовал бы MySQL (без избыточной информации, с ссылками по id).
Для переписки достаточно будет 2 коллекции - users и chats.
Коллекция Users - содержит инфу для авторизации и вообще инфу о пользователях.
Коллекция Chats - содержит сами чаты, в которых имеются подколлекции users (id состоящих в них пользователей) и messages (собственно сообщения со всеми нужными данными).
Можно сделать ещё третью коллекцию - Attachments (передача файлов тоже планируется, каждый аттач будет связан со своим сообщением).

Имеем (допустим идеальный случай) 3 зареганых пользователя - A, B, C). Каждый пользователь сидит ТОЛЬКО С ОДНОГО устройства и никак иначе (идеальные условия).
Пользователь C находится офлайн.
Пользователь A создаёт беседу "Room1" и приглашает в неё пользователей B и C.
Комната прилетает на сервер и записывается в БД. Начало общения в ней положено.

Вопрос 1. Как оповестить пользователей о том что они приглашены в беседу?
Как вижу я - если пользователь держит постоянный коннект с сервером то в целом просто, по этому коннекту пользователя и оповещаем.
И от сюда вопрос 2. Как быть если пользователь офлайн?
Как вижу я - при подключении пользователя (переход в онлайн) обращаться с запросом "очереди событий" (которую надо как то содержать), от куда и получает приглашение в беседу

Далее. Пользователи приглашены в беседу, всё хорошо.
Пользователь A отсылает в беседу сообщение.
Сообщение прилетает на сервер, записывается в базу к беседе (для истории). Попутно оно отсылается через коннект пользователю B.
Пользователи A и B общаются между собой, пользователь C всё ещё офлайн.
Через какое то время пользователь C таки появляется онлайн и входит в данную беседу.

Вопрос 3 и 4. Как пользователю получить историю сообщений? Когда у него нет истории всё просто - скачиваем что есть (грубо всё, или лимитировано по 10-20 сообщений со смещениями). А что делать если пользователь вошёл в диалог, какое то время там был, а потом стал оффлайн? То есть, что ему отдавать когда он снова станет онлайн? Всю историю беседы точно отдавать ненадо (она ведь уже есть), но существуют некоторые пробелы, которые надо заполнить. Можно конечно забить болт на это дело и пойти по принципу "пропустил - твои проблемы", но не хотелось бы, ведь во всяких вконтактах и вайберах же нету такого "болтозабивательства", вайбер так вообще очень слаженно скачивает историю. То есть, если общался на мобилке, то на компе всегда появляется достоверная без пробелов история, если запустить приложение после переписки на мобилке (и наоборот соответственно тоже работает), проверял специально.

И вопрос 5. Вычитал что у MongoDB ограничение на коллекцию 16 мегабайт. Что делать когда коллекция chats достигнет этого предела (16 мегабайт не так то и много)? Надо как то будет распределять данные в другие коллекции?
__________________
Main PC:
Intel Core i5 4260U 1.44 GHz + LPDDR3 1x4096 1600 MHz + Intel HD Graphics 5000.

Asus Ёжик T101-MT:
Intel Atom N-570 1.66 Ghz + DDR2 2x1024 800 Mhz + Intel GMA 3150 128 Mb DDR2


Скачать Doom 2D: Remake v0.3.8a
(Offline)
 
Ответить с цитированием
Ответ


Опции темы

Ваши права в разделе
Вы не можете создавать темы
Вы не можете отвечать на сообщения
Вы не можете прикреплять файлы
Вы не можете редактировать сообщения

BB коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.


Часовой пояс GMT +1, время: 06:55.


vBulletin® Version 3.6.5.
Copyright ©2000 - 2018, Jelsoft Enterprises Ltd.
Перевод: zCarot
Style crйe par Allan - vBulletin-Ressources.com