Как я могу улучшить эту ленту новостей 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 69

5 ответов:

очень классный вопрос. Я на самом деле в середине реализации что-то вроде этого сам. Так что я немного подумаю вслух.

вот недостатки, которые я вижу в своем уме с вашей текущей реализацией:

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

  2. Если один из моих друзей публикует что-то, это не будет отображаться в моей ленте новостей не более 5 минут. Тогда как он должен появиться немедленно, верно?

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

  4. Это не масштабируется так хорошо.

лента новостей выглядит точно так же, как и журнал активности, я бы придерживался этого журнала активности стол.

Если вы сегментируете свои журналы активности по базам данных, это позволит вам легче масштабировать. Вы также можете сегментировать своих пользователей, если хотите, но даже если у вас есть 10 миллионов записей пользователей в одной таблице, mysql должен хорошо выполнять чтение. Поэтому всякий раз, когда вы ищете пользователя, вы знаете, какой осколок для доступа к журналам пользователя. Если вы архивируете свои старые журналы так часто и поддерживаете только новый набор журналов, вам не придется разбивать столько же. А может быть, и вовсе. Вы можете управлять многими миллионы записей в MySQL, если вы настроены даже умеренно хорошо.

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

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

вы видели эту статью?

http://bret.appspot.com/entry/how-friendfeed-uses-mysql

не могли бы вы добавить статистические ключевые слова? Я сделал (грубую) реализацию путем взрыва тела моего документа, удаления 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