Як я можу покращити цю стрічку новин PHP / MySQL?


74

Дозвольте мені розпочати відразу, сказавши, що я знаю, що це не найкраще рішення. Я знаю, що це студійно і хак функції. Але тому я тут!

Це питання / робота побудовано на деякій дискусії щодо Quora з Ендрю Босвортом , творцем стрічки новин Facebook.

Я будую своєрідну стрічку новин . Він вбудований виключно в PHPі MySQL.

текст заміщення


MySQL

Реляційна модель корму складається з двох таблиць. Одна таблиця функціонує як журнал активності; насправді це названо activity_log. Інша таблиця newsfeed. Ці таблиці майже однакові.

Схема для журналу є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'];

}

Покращення стрічки новин

Тепер пробачте моє обмежене розуміння найкращих практик для розробки стрічки новин, але я розумію підхід, який я використовую, як обмежену версію того, що називається фан-аут при записі , обмежений у тому сенсі, що я запускаю роботу cron як проміжний крок замість того, щоб писати безпосередньо в стрічки новин користувачів. Але це сильно відрізняється від моделі витягування, в тому сенсі, що стрічка новин користувача складається не на завантаженні, а на регулярній основі.

Це велике питання, яке, мабуть, заслуговує на велику кількість туди-сюди, але я думаю, що воно може слугувати пробним каменем для багатьох важливих бесід, які повинні мати такі нові розробники, як я. Я просто намагаюся зрозуміти, що я роблю не так, як я можу поліпшитись або як мені, можливо, навіть почати з нуля та спробувати інший підхід.

Ще одне, що мене хвилює у цій моделі, це те, що вона працює, базуючись на нещодавно, а не на релевантності. Якщо хтось може підказати, як це можна покращити, щоб покращити свою актуальність, я був би всім вухом. Я використовую API Directed Edge для генерації рекомендацій, але, схоже, для чогось на зразок стрічки новин рекомендувачі не працюватимуть (оскільки раніше нічого не було вибране!).


Що? Цього разу жодного жартівливого зображення не додано? Ви можете зробити це краще! : P
alex


1
@Josh Smith чи є у кожного користувача таблиця стрічки новин?
chromedude

1
@josh smith Якщо ви відновлюєте вищезазначений алгоритм, опублікуйте його. Дякуємо
Namal

1
@JoshSmith, ти виконуєш один запит SQL для кожного друга, щоб захопити їх діяльність?
Джон Сміт,

Відповіді:


15

Дійсно круте питання. Я насправді сам посеред реалізації чогось подібного. Отже, я трохи подумаю вголос.

Ось недоліки, які я бачу у своєму розумі з вашим поточним впровадженням:

  1. Ви обробляєте всіх друзів для всіх користувачів, але в кінцевому підсумку ви будете обробляти одних і тих самих користувачів багато разів через те, що однакові групи людей мають подібних друзів.

  2. Якщо хтось із моїх друзів щось публікує, це не відображатиметься у моїй стрічці новин щонайбільше 5 хвилин. Тоді як це повинно з’явитися негайно, так?

  3. Ми читаємо всю стрічку новин для користувача. Хіба нам не потрібно просто захоплюватися новими заходами з часу останнього часу, коли ми хрустили колоди?

  4. Це не так добре масштабується.

Потік новин виглядає точно такими ж даними, як і журнал активності, я б дотримувався тієї самої таблиці журналу активності.

Якщо ви розділите журнали своєї діяльності в базах даних, це дозволить вам легше масштабувати. Ви також можете подрібнити своїх користувачів, але навіть якщо у вас є 10 мільйонів записів користувачів в одній таблиці, mysql повинен чудово робити читання. Отже, коли ви шукаєте користувача, ви знаєте, з якого осколка отримати доступ до журналів користувача. Якщо ви щораз частіше архівуєте свої старі журнали і підтримуєте лише новий набір журналів, вам не доведеться стільки обробляти. А може навіть взагалі. Ви можете керувати багатьма мільйонами записів у MySQL, якщо налаштовані навіть помірно добре.

Я б використав memcached для таблиці ваших користувачів і, можливо, навіть для самих журналів. Memcached дозволяє вводити кеш-пам’яті розміром до 1 Мб, і якщо ви розумно організували свої ключі, ви могли б потенційно отримати всі найновіші журнали з кешу.

Що стосується архітектури, це було б більше роботи, але це дозволить вам працювати в режимі реального часу та масштабуватися в майбутньому ... особливо, коли ви хочете, щоб користувачі почали коментувати кожну публікацію. ;)

Ви бачили цю статтю?

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


1

між ними можна використовувати прапори користувачів та кешування. Скажімо, мати нове поле для користувача як last_activity. Оновлюйте це поле щоразу, коли користувач вводить будь-яку діяльність. Зберігайте прапорець, доки ви не отримаєте канали, скажімо, це feed_updated_on.

Тепер оновіть функцію $ user-> getAllUsers (); повернути лише тих користувачів, у яких час останньої дії пізніше, ніж feed_updated_on. Це виключить усіх користувачів, які не мають жодного журналу активності :). Подібний процес для друзів користувачів.

Ви також можете використовувати кешування, як memcache або кешування на рівні файлу.

Або скористайтеся деякою базою даних nosql для зберігання всіх каналів як одного документа.


1

Я намагаюся самостійно створити стрічку новин у стилі Facebook. Замість того, щоб створювати іншу таблицю для реєстрації дій користувачів, я обчислював "край" на основі СОЮЗУ повідомлень, коментарів тощо.

Трохи математики, я обчислюю "край", використовуючи модель експоненціального занепаду, при цьому проміжок часу є незалежною змінною, беручи до уваги кількість коментарів, оцінок "подобається" тощо. Кожна публікація повинна формулювати лямбда-константу. Спочатку край швидко зменшується, але поступово через кілька днів згладжується майже до 0 (але ніколи не досягне 0)

Під час показу подачі кожне ребро множиться за допомогою RAND (). Публікації з вищим краєм з’являться частіше

Таким чином, більш популярні публікації мають більшу ймовірність з’являтися у стрічці новин довше.


4
Ви не згадували, чи попередньо обчислюється Edge чи обчислюється час роботи?
meson10

1

Замість запуску завдання cron, сценарію після фіксації. Я не знаю конкретно, які можливості PHP та MySQL у цьому відношенні - якщо я правильно згадую, MySQL InnoDB надає більш розширені функції, ніж інші різновиди, але я не пам’ятаю, чи є в останній версії такі речі, як тригери.

у будь-якому випадку, простий різновид, який не покладається на багато магії бази даних:

коли користувач X додає вміст:

1) здійснити асинхронний виклик зі своєї PHP-сторінки після фіксації бази даних (звичайно, асинхронний, щоб користувач, який переглядає сторінку, не мусив його чекати!)

Виклик запускає екземпляр логічного сценарію.

2) логічний скрипт проходить лише через список друзів [A, B, C] користувача, який створив новий вміст (на відміну від списку всіх у БД!), І додає дію користувача X до стрічок для кожного цих користувачів.

Ви можете просто зберігати ці канали як прямі файли JSON і додавати нові дані в кінці кожного. Краще, звичайно, зберігати канали в кеші з резервною копією до файлової системи або BerkeleyDB, або Mongo, або чого завгодно.

Це лише основна ідея для каналів, яка базується на нещодавно, а не на актуальності. Ви МОЖЕТЕ зберігати дані таким чином послідовно, а потім проводити додатковий синтаксичний аналіз для кожного користувача, щоб відфільтрувати за релевантністю, але це важка проблема в будь-якій програмі, ймовірно, не у такій, яку анонімний користувач може легко вирішити без детальної інформації. знання ваших вимог;)

jsh


0

Ви б додали статистичне ключове слово? Я здійснив (грубу) реалізацію, розірвавши тіло мого документа, зачистивши HTML, видаливши загальні слова та підрахувавши найпоширеніші слова. Я зробив це кілька років тому просто для розваги (як і в будь-якому подібному проекті, джерело зник), але це спрацювало для мого тимчасового налаштування тестового блогу / форуму. Можливо, це підійде для вашої стрічки новин ...


3D Це насправді легше реалізувати за допомогою такої FULLTEXTпошукової системи, як Sphinx, що є ще одним можливим підходом. Мене турбує щось подібне або підхід, запропонований @stillstanding, полягає в тому, що він відчуває себе як хак поверх хакерів. Що б я насправді хотів зробити, щоб визначити релевантність, це обчислити підсумований показник спорідненості користувача з творцем вмісту, вагою для типу вмісту та коефіцієнтом занепаду часу. Але я ще не впевнений, як це робити ...
Джош Сміт,

До якого рівня складності ви дозволите цьому зростати? Це здається досить великим розподілом ваги, але це можливо. Вам доведеться додати деякого логарифмічного занепаду до актуальності з віком, але отримання „типу вмісту” досить розмито. Вам доведеться налаштувати масив ключових слів для відповідності, щоб визначити це (як швидке рішення. Це не буде ідеальним у великомасштабних додатках). Це вимагає інтенсивної статистики та навичок читання комп’ютера ...
Blender

Ймовірно, це буде досить складно; думаю, що стрічка новин Facebook. Але це те, що, мабуть, потребуватиме більш масштабного переосмислення понад те, що я маю тут.
Джош Сміт,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.