Проектирую "Ленту событий" для социального проекта?

Постановка задачи следующая:


1) У каждого пользователя есть друзья («максимальный» вариант — все в друзьях у всех)

2) Каждый пользователь может входить в неограниченное количество групп


Пользователь может как генерировать контент самостоятельно (похоже на личную «Стену», как у ВКонтакте и ФБ), так контент может генерироваться компонентами внутри проекта.


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


В бэкэнде у системы крутится СУБД MySQL, но лично для меня вполне очевиден тот факт, что «обычный» реляционный подход к решению задачи в общем случае не применим из-за накладываемых ограничений — если друзей, к примеру, 2500+, то один WHERE разрастается до чудовищных размеров.


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


Следует ли применять денормализацию — играться с ведением отдельной ленты для каждого пользователя (но в случае выхода из групп или удаления человека из друзей обновление этого списка может занимать огромное время)?


Может, есть какие-то статьи по поводу или признанные варианты реализации?
  • Вопрос задан
  • 4135 просмотров
Пригласить эксперта
Ответы на вопрос 7
@egorinsk
На всяких фейсбуках и вконтактах данные ленты генерирует демон на компилируемом языке, который к тому же кучу информации кеширует в памяти. Осилите? Ну не знаете Си, можете попробовать на Node или Java написать, ну или на PHP в крайнем случае, если заложить возможность масштабирования, тоже будет ОК до поры до времении.

Алгоритм работы примерно такой: 1) выбираем id друзей юзера 2) выбираем последние события у каждого из них 3) отсеиваем защищенные приватностью 4) склеиваем списки, сортируем по дате и отдаем в браузер.

А на MySQL у вас с ростом нагрузки, числа друзей все это быстро положит сервер. Сделаем простейший расчет: средний юзер делает 10 событий в день, если юзеров хотя бы несколько тысяч, таблица событий быстро подпрыгнет в размере до миллионов записей. А всякие конструкции типа JOIN во-первых, грузят сервер, во-вторых, не шардятся.
Ответ написан
taliban
@taliban
php программист
Есть такая вешь как кеш. Кеш бывает разных видов, это не только выборка хранящаяся в файлике под рукой. Банальный пример карма на хабре. Я точно уверен что у них в базе хранится все данные, кто кому куда сколько раз ткнул плюсик или минусик, но видим мы лишь 3 (+4- 1). Своеобразный кеш нужных данные в отдельном месте. Вынесите связи отдельно специально для это й ленты, дублируйте данные, не бойтесь, дубли иногда помогают в производительности. Допустим сделайте для начала табличку активности:
id, user_id, event_desc, event_date
И при каждой активности заносите сюда данные, эта до невозможности простая табличка может одним запросом вывести всю активность пользователя в примитивном виде. Дальше уже развивайте мысль как удобно =)
Ответ написан
Panacea
@Panacea
Не понял, если честно задачи. Вроде бы ничего сложного.
В псевдокоде выглядит что-то вроде
select news.*
from news, users_users as friends
where friends.left_user = :currentUser
and friends.right_user = news.user_id
union
select news.*
from news, groups
where groups_users.user_id = :currentUser
and groups_users.group_id = news.group_id

Или я что-то не учел в условиях?
Ответ написан
Zelgadis
@Zelgadis
Вот еще один пример где RDBMS не уперся и надо использовать, что-то на подобии MongoDB/CouchDB или Neo4j (если данные сильно связанные).
Сделать сначала массив всех ObjectiveID с которыми связан пользователь (один запрос), а потом разов взять все события где автор события находится в этом массиве. Или через DBRef находим, что у события есть связь с текущим пользователем. Первый вариант будет быстрее, но меньше кода,
Ответ написан
denver
@denver
В фейсбуках и твиттерах используется денормализация, причем «очистка» ленты после ухожа из группы/друзей у них отсутствует. Банально — что получено в ленту, то получено. Кроме того лента на фб вроде ограничена по времени, это разумно. В твиттере тоже была проблема с огромной лентой пользователей у которых 20k+ подписок, таких одно время просто банили. В целом — кладем так как удобнее доставать, вариантов достать должно быть немного. Нормализация это не для больших объемов. Да и денормализированный mysql не для больших, если только не разбивать по инстансам (скажем, максимум 100 юзеров со всеми их данными/лентами в одной базе).
Ответ написан
mrspd
@mrspd
Недавно сам интересовался, напоролся на эту статью: myforce.ru/tyeoriya-i-praktika/delaem-lentu-obnovlenij-na-mongodb-php/
Ответ написан
@vitalybaev
Взгляние в сторону Redis и из Sorted set. Каждый сет — лента какого-то человека, в качестве score — ID событий (можно время), редис может держать около 200 000 запросов в секунду, и сам на лету сортирует, умеет делать запросы по обычным лимитам/офсетам либо запросы вида ID < x
Сам же объект — сериализованный массив (ну или как вам улдобнее), а сам объект номера уже берется из мемкеша например. Собственно, как писал товарищ denver, используйте денормализацию.

Описанный метод успешно работает на достаточно посещаемом ресурсе, и легко справляется со случаем, когда человек с 30000 фолловеров создает пост.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы