Jak mogę ulepszyć ten kanał wiadomości PHP/MySQL?

Zacznę od tego, że wiem, że to nie jest najlepsze rozwiązanie. Wiem, że to kludgy i hack funkcji. ale dlatego tu jestem!

To pytanie / praca opiera się niektóre rozmowy na Quora z Andrew Bosworth , twórca Facebook news feed.

Buduję kanał informacyjny w pewnym sensie. Jest zbudowany wyłącznie w PHP i MySQL.

alt text


MySQL

Model relacyjny dla Karma składa się z dwóch tabel. Jedna tabela funkcjonuje jako dziennik aktywności; w rzeczywistości nazywa się activity_log. Druga tabela To newsfeed. te tabele są prawie identyczne.

Schemat logu to activity_log(uid INT(11), activity ENUM, activity_id INT(11), title TEXT, date TIMESTAMP)

...i schemat dla paszy to newsfeed(uid INT(11), poster_uid INT(11), activity ENUM, activity_id INT(11), title TEXT, date TIMESTAMP).

Za każdym razem, gdy użytkownik zrobi coś związanego z aktualnościami, na przykład zadając pytanie, zostanie zalogowany do dziennika aktywności natychmiast.


Generowanie kanałów informacyjnych

Następnie co X minut (5 minut w tej chwili zmieni się na 15-30 minut później), uruchamiam zadanie crona, które wykonuje skrypt poniżej. Ten skrypt zapętla wszystkich użytkowników w bazie danych, wyszukuje wszystkie działania dla wszystkich znajomych tego użytkownika, a następnie zapisuje te działania do kanału wiadomości.

W tej chwili SQL, które wywołują aktywność (wywołaną ActivityLog::getUsersActivity()), mA LIMIT 100 narzucone ze względu na wydajność*. Nie żebym wiedział, o czym mówię.

<?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']);
      }
    }
  }
}

Wyświetlanie kanałów informacyjnych

W kodzie klienta, podczas pobierania kanału wiadomości użytkownika, robię coś w stylu:

$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'];

}

Ulepszanie kanału wiadomości

Teraz wybacz moje ograniczone zrozumienie najlepszych praktyk tworzenia kanału informacyjnego, ale rozumiem podejście, którego używam, aby być ograniczoną wersją tego, co nazywa się fan-out na write , ograniczone w tym sensie, że Uruchamiam zadanie cron jako krok pośredni, zamiast pisać bezpośrednio do kanałów informacyjnych użytkowników. Ale to bardzo różni się od modelu pull, w tym sensie, że kanał wiadomości użytkownika nie jest kompilowany na obciążeniu, ale raczej regularnie.

Jest to duże pytanie, które prawdopodobnie zasługuje na dużą ilość tam iz powrotem, ale myślę, że może służyć jako kamień węgielny dla wielu ważnych rozmów, które nowi Programiści, jak ja, muszą mieć. Próbuję się dowiedzieć, czym jestem. robiąc źle, jak mogę poprawić, lub jak powinienem zacząć od zera i spróbować innego podejścia.

Inną rzeczą, która mnie denerwuje w tym modelu, jest to, że działa on w oparciu o aktualność, a nie trafność. Jeśli ktoś może zasugerować, jak można to poprawić, aby pracować w relevancy, byłbym za. Używam interfejsu API Directed Edge do generowania rekomendacji, ale wydaje się, że dla czegoś takiego jak kanał informacyjny, polecający nie będą działać (ponieważ nic nie zostało ulubione poprzednio!).

Author: Josh Smith, 2010-11-12

5 answers

Naprawdę fajne pytanie. Sam jestem w trakcie realizacji czegoś takiego. Pomyślę trochę na głos.

Oto wady jakie widzę w mojej głowie z Twoją obecną implementacją:

  1. Przetwarzasz wszystkich znajomych dla wszystkich użytkowników, ale skończysz przetwarzać tych samych użytkowników wiele razy ze względu na fakt, że te same grupy ludzi mają podobnych przyjaciół.

  2. Jak ktos z moich znajomych cos wrzuci to nie pojawi sie na mój kanał wiadomości przez najwyżej 5 minut. A powinno się pojawić natychmiast, prawda?

  3. Czytamy cały kanał wiadomości dla użytkownika. Czy nie musimy po prostu chwytać nowych czynności od czasu, gdy ostatnio łamaliśmy kłody?

  4. To nie jest tak dobrze skalowane.

Newsfeed wygląda dokładnie tak samo jak dziennik aktywności, trzymałbym się tej jednej tabeli dziennika aktywności.

Jeśli podzielisz dzienniki aktywności w bazach danych, pozwoli to ty łatwiej skalować. Możesz również dzielić użytkowników, jeśli chcesz, ale nawet jeśli masz 10 milionów rekordów użytkowników w jednej tabeli, mysql powinien być w porządku czytając. Więc za każdym razem, gdy wyszukujesz użytkownika, wiesz, z którego odłamka uzyskać dostęp do dzienników użytkownika. Jeśli archiwizujesz swoje starsze dzienniki co jakiś czas i utrzymujesz tylko świeży zestaw dzienników, nie będziesz musiał tak bardzo odłamywać. A może nawet w ogóle. Możesz zarządzać wieloma milionami rekordów w MySQL, jeśli jesteś dostrojony nawet umiarkowanie dobrze.

I would wykorzystaj memcached do tabeli użytkowników, a może nawet samych dzienników. Memcached umożliwia wpisy pamięci podręcznej o rozmiarze do 1mb, a jeśli byłeś sprytny w organizowaniu kluczy, możesz potencjalnie pobrać wszystkie najnowsze dzienniki z pamięci podręcznej.

Będzie to więcej pracy w architekturze, ale pozwoli Ci pracować w czasie rzeczywistym i skalować się w przyszłości...szczególnie, gdy chcesz, aby użytkownicy zaczęli komentować przy każdym wpisie. ;)

Czy widzisz ten artykuł?

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

 12
Author: Dan Spiteri,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-06-30 07:44:25

Czy dodałbyś statystyczny keywording? Zrobiłem (prymitywną) implementację poprzez eksplodowanie ciała mojego dokumentu, usuwanie HTML, usuwanie wspólnych słów i liczenie najczęściej używanych słów. Zrobiłem to kilka lat temu dla zabawy (jak przy każdym takim projekcie, źródła nie ma), ale zadziałało na mój tymczasowy test-konfiguracja bloga/forum. Może to zadziała dla Twojego kanału informacyjnego...

 0
Author: Blender,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-11-12 06:12:32

Pomiędzy można użyć flagi użytkownika i buforowania. Powiedzmy, że ma nowe pole dla użytkownika jako last_activity. Aktualizuj to pole za każdym razem, gdy użytkownik wprowadzi jakąkolwiek aktywność. Zachowaj flagę, aż do czasu, kiedy pobrałeś kanały, powiedzmy, że feed_updated_on.

Teraz zaktualizuj funkcję $user- > getAllUsers (); aby zwrócić tylko użytkowników, którzy mają last_activity czas później niż feed_updated_on. Spowoduje to wykluczenie wszystkich użytkowników, którzy nie mają żadnego dziennika aktywności :). Podobny proces dla znajomych użytkowników.

Ty może również korzystać z buforowania, takiego jak memcache lub buforowanie na poziomie plików.

Lub użyj nosql DB do przechowywania wszystkich kanałów jako jednego dokumentu.

 0
Author: Aakash Sharma,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-06-26 12:24:18

Staram się zbudować kanał informacyjny w stylu Facebook na własną rękę. Zamiast tworzyć kolejną tabelę do logowania aktywności użytkowników, obliczyłem "krawędź" ze Związku postów, komentarzy itp.

Z odrobiną matematyki obliczam 'krawędź' za pomocą wykładniczego modelu rozkładu, z upływem czasu jako zmienną niezależną, biorąc pod uwagę liczbę komentarzy, polubień itp. każdy post musi sformułować stałą lambda. Na początku krawędź szybko się zmniejsza, ale stopniowo spłaszcza się do prawie 0 po kilku dniach (ale nigdy nie osiągnie 0)

Podczas wyświetlania kanału każda krawędź jest mnożona przez RAND (). Posty z wyższą krawędzią pojawią się częściej

W ten sposób bardziej popularne posty mają większe prawdopodobieństwo, aby pojawić się w news feed, przez dłuższy czas.

 0
Author: Freeman Latif,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-06-30 14:22:43

Zamiast uruchamiać zadanie crona, jakiś skrypt po zatwierdzeniu. Nie wiem dokładnie jakie są pod tym względem możliwości PHP i MySQL - o ile dobrze pamiętam MySQL InnoDB umożliwia bardziej zaawansowane funkcje niż inne odmiany, ale nie pamiętam, czy są takie rzeczy jak wyzwalacze w najnowszej wersji.

W każdym razie prosta odmiana, która nie polega na dużej magii bazy danych:

Gdy użytkownik X dodaje treść:

1) Wykonaj asynchroniczne połączenie z PHP strona po zatwierdzeniu bazy danych (oczywiście asynchroniczna, aby użytkownik przeglądający Stronę nie musiał na nią czekać!)

Wywołanie uruchamia instancję twojego skryptu logicznego.

2) skrypt logiczny przechodzi tylko przez listę znajomych [A,B,C] użytkownika, który wprowadził nową zawartość (w przeciwieństwie do listy wszystkich w DB!) i dołącza akcję użytkownika X do kanałów dla każdego z tych użytkowników.

Możesz po prostu przechowywać te kanały jako proste pliki JSON i Dołącz nowe dane na końcu każdego z nich. Lepiej oczywiście trzymać kanały w cache z backupem do filesystem lub BerkeleyDB lub Mongo czy jak tam chcesz.

To tylko podstawowy pomysł na kanały oparte na aktualności, a nie na znaczeniu. Możesz przechowywać dane sekwencyjnie w ten sposób, a następnie wykonać dodatkowe parsowanie dla każdego użytkownika, aby filtrować według trafności, ale jest to trudny problem w każdej aplikacji i prawdopodobnie nie taki, który może być łatwo rozwiązany przez anonimowego użytkownika sieciowego bez szczegółowa znajomość twoich wymagań;)

Jsh

 0
Author: jsh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-06-30 20:04:03