Как я могу улучшить эту ленту новостей PHP/MySQL?
позвольте мне начать с самого начала, сказав, что я знаю, что это не лучшее решение. Я знаю, что это клуджи и взлом функции. но именно поэтому я здесь!
этот вопрос/работа строитсяобсуждение на Quora с Эндрю Босворт, создатель ленты новостей Facebook.
я строю ленту новостей сортов. Он построен исключительно в PHP
и MySQL
.
MySQL
реляционная модель для канала состоит из двух таблиц. Одна таблица функционирует как журнал действий; фактически, она называется activity_log
. Другая таблица -newsfeed
. эти таблицы почти идентичны.
The схема для журнала и activity_log(uid INT(11), activity ENUM, activity_id INT(11), title TEXT, date TIMESTAMP)
...а то схемы для кормов и newsfeed(uid INT(11), poster_uid INT(11), activity ENUM, activity_id INT(11), title TEXT, date TIMESTAMP)
.
в любое время пользователь делает что-то отношение к ленте новостей, например, задавая вопрос, он будет записан в журнал активности немедленно.
создание ленты новостей
затем каждые X минут (5 минут на данный момент, изменится на 15-30 минут позже), я запускаю задание cron который выполняет сценарий ниже. Этот скрипт перебирает всех пользователей в базе данных, находит все действия для всего этого друзей пользователя, а затем записывает эти действия в ленту новостей.
на данный момент SQL
что отбраковывает активность (вызывается в ActivityLog::getUsersActivity()
), имеет LIMIT 100
наложено по соображениям производительности*. *Не то чтобы я знаю, о чем говорю.
<?php
$user = new User();
$activityLog = new ActivityLog();
$friend = new Friend();
$newsFeed = new NewsFeed();
// Get all the users
$usersArray = $user->getAllUsers();
foreach($usersArray as $userArray) {
$uid = $userArray['uid'];
// Get the user's friends
$friendsJSON = $friend->getFriends($uid);
$friendsArray = json_decode($friendsJSON, true);
// Get the activity of each friend
foreach($friendsArray as $friendArray) {
$array = $activityLog->getUsersActivity($friendArray['fid2']);
// Only write if the user has activity
if(!empty($array)) {
// Add each piece of activity to the news feed
foreach($array as $news) {
$newsFeed->addNews($uid, $friendArray['fid2'], $news['activity'], $news['activity_id'], $news['title'], $news['time']);
}
}
}
}
отображение новостей
в клиентском коде, при получении ленты новостей пользователя, я делаю что-то вроде:
$feedArray = $newsFeed->getUsersFeedWithLimitAndOffset($uid, 25, 0);
foreach($feedArray as $feedItem) {
// Use a switch to determine the activity type here, and display based on type
// e.g. User Name asked A Question
// where "A Question" == $feedItem['title'];
}
улучшение Ленты Новостей
теперь простите мое ограниченное понимание лучших практик для разработки ленты новостей, но я понимаю подход, который я использую, чтобы быть ограниченной версией того, что называется разветвление на write, ограниченный в том смысле, что я запускаю задание cron в качестве промежуточного шага вместо прямой записи в новостные ленты пользователей. Но это очень отличается от модели pull, в том смысле, что лента новостей пользователя не компилируется при загрузке, а скорее на регулярной основе.
это a большой вопрос, который, вероятно, заслуживает большого количества взад и вперед, но я думаю, что он может служить пробным камнем для многих важных разговоров, которые должны иметь новые разработчики, такие как я. Я просто пытаюсь понять, что я делаю неправильно, как я могу улучшить, или как я должен, возможно, даже начать с нуля и попробовать другой подход.
еще одна вещь, которая меня беспокоит в этой модели, заключается в том, что она работает на основе новизны, а не релевантности. Если кто-нибудь может подсказать, как это может быть я бы с удовольствием поработал в этом направлении. Я использую API Directed Edge для генерации рекомендаций, но кажется, что для чего-то вроде ленты новостей рекомендатели не будут работать (поскольку ранее ничего не было фаворитов!).
5 ответов:
очень классный вопрос. Я на самом деле в середине реализации что-то вроде этого сам. Так что я немного подумаю вслух.
вот недостатки, которые я вижу в своем уме с вашей текущей реализацией:
вы обрабатываете всех друзей для всех пользователей, но в конечном итоге вы будете обрабатывать одних и тех же пользователей много раз из-за того, что одни и те же группы людей имеют похожих друзей.
Если один из моих друзей публикует что-то, это не будет отображаться в моей ленте новостей не более 5 минут. Тогда как он должен появиться немедленно, верно?
мы читаем всю ленту новостей для пользователя. Разве нам не нужно просто захватить новые действия с тех пор, как мы в последний раз хрустели бревнами?
Это не масштабируется так хорошо.
лента новостей выглядит точно так же, как и журнал активности, я бы придерживался этого журнала активности стол.
Если вы сегментируете свои журналы активности по базам данных, это позволит вам легче масштабировать. Вы также можете сегментировать своих пользователей, если хотите, но даже если у вас есть 10 миллионов записей пользователей в одной таблице, mysql должен хорошо выполнять чтение. Поэтому всякий раз, когда вы ищете пользователя, вы знаете, какой осколок для доступа к журналам пользователя. Если вы архивируете свои старые журналы так часто и поддерживаете только новый набор журналов, вам не придется разбивать столько же. А может быть, и вовсе. Вы можете управлять многими миллионы записей в MySQL, если вы настроены даже умеренно хорошо.
Я бы использовал memcached для вашей таблицы пользователей и, возможно, даже для самих журналов. Memcached позволяет кэшировать записи размером до 1 Мб, и если бы Вы были умны в организации своих ключей, вы могли бы потенциально получить все самые последние журналы из кэша.
Это будет больше работы, Что касается архитектуры, но он позволит вам работать в режиме реального времени и данные в будущее...особенно, когда вы хотите, чтобы пользователи начали комментировать на каждой проводки. ;)
вы видели эту статью?
не могли бы вы добавить статистические ключевые слова? Я сделал (грубую) реализацию путем взрыва тела моего документа, удаления HTML, удаления общих слов и подсчета наиболее распространенных слов. Я сделал это несколько лет назад просто для удовольствия (как и в любом таком проекте, источник ушел), но он работал для моей временной настройки блога/форума. Может быть, это будет работать для вашей ленты новостей...
между Вы можете использовать пользовательские флаги и кэширование. Допустим, есть новое поле для пользователя как last_activity. Обновляйте это поле всякий раз, когда пользователь вводит какое-либо действие. Держите флаг, до тех пор, пока вы не выбрали каналы, скажем, это feed_updated_on.
теперь обновить функцию $user - >getAllUsers (); чтобы вернуть только пользователей, которые имеют last_activity время позже, чем feed_updated_on. Это исключит всех пользователей, которые не имеют никакого журнала активности :). Аналогичный процесс для пользователей друзья.
вы также можете использовать кэширование, как memcache или кэширование на уровне файлов.
или использовать некоторые базы данных nosql для хранения всех каналов в виде одного документа.
Я пытаюсь создать ленту новостей в стиле Facebook самостоятельно. Вместо того, чтобы создавать другую таблицу для регистрации действий пользователей, я рассчитал "край" из объединения сообщений, комментариев и т. д.
с небольшим количеством математики я вычисляю "край", используя экспоненциальную модель распада, с истекшим временем, являющимся независимой переменной, с учетом количества комментариев, лайков и т. д. каждый пост должен сформулировать лямбда-константу. Край будет уменьшаться быстро сначала, но постепенно выравнивается почти до 0 через несколько дней (но никогда не достигнет 0)
при показе ленты каждое ребро умножается с помощью RAND(). Посты с более высоким краем будут появляться чаще
таким образом, более популярные сообщения имеют более высокую вероятность появиться в ленте новостей, в течение более длительного времени.
вместо запуска задания cron, какой-то скрипт после фиксации. Я не знаю конкретно, каковы возможности PHP и MySQL в этом отношении - если я правильно помню, MySQL InnoDB позволяет более продвинутые функции, чем другие разновидности, но я не помню, есть ли такие вещи, как триггеры в последней версии.
в любом случае, простое разнообразие, которое не зависит от большого количества магии базы данных:
когда пользователь X добавляет содержание:
1) сделать асинхронный вызов с вашей страницы PHP после фиксации базы данных (асинхронный, конечно, чтобы пользователю, просматривающему страницу, не пришлось ее ждать!)
вызов запускает экземпляр вашего логического скрипта.
2) логика скрипт только через список друзей [A,B,C] пользователя, который совершил новый контент (в отличие от списка всех в БД!) и добавляет действие пользователя X в каналы для каждого из этих пользователей.
вы могли бы просто хранить эти каналы представляют собой прямые файлы JSON и добавляют новые данные в конец каждого. Лучше, конечно, держать каналы в кэше с резервной копией в файловую систему или BerkeleyDB или Mongo или что вам нравится.
Это просто основная идея для каналов, основанных на недавности, а не релевантности. Вы можете хранить данные последовательно таким образом, а затем выполнять дополнительный анализ на основе каждого пользователя для фильтрации по релевантности, но это сложная проблема в любом приложении и, вероятно, не та, которая может быть легко адресовано анонимным веб-пользователем без детального знания ваших требований;)
jsh