76 votes

Comment puis-je améliorer ce fil d'actualités PHP/MySQL ?

Permettez-moi de dire d'emblée que je sais que ce n'est pas la meilleure solution. Je sais que c'est maladroit et que c'est une fonction pirate. Mais c'est pour ça que je suis là !

Cette question/ce travail s'appuie sur une discussion sur Quora avec Andrew Bosworth le créateur du fil d'actualité de Facebook.

Je crée un flux d'informations en quelque sorte. C'est construit uniquement en PHP et MySQL .

alt text


L'application MySQL

Le modèle relationnel de l'alimentation est composé de deux tables. Une table fonctionne comme un journal d'activité ; en fait, elle est nommée activity_log . L'autre table est newsfeed . Ces tables sont presque identiques.

Le site schéma pour le journal est activity_log(uid INT(11), activity ENUM, activity_id INT(11), title TEXT, date TIMESTAMP)

...et le schéma pour le flux est newsfeed(uid INT(11), poster_uid INT(11), activity ENUM, activity_id INT(11), title TEXT, date TIMESTAMP) .

Chaque fois qu'un utilisateur fait quelque chose en rapport avec le fil d'actualité, par exemple en posant une question, il sera enregistré dans le journal d'activité immédiatement.


Génération des flux d'informations

Puis toutes les X minutes (5 minutes pour le moment, passera à 15-30 minutes plus tard), J'exécute un travail cron qui exécute le script ci-dessous. Ce script boucle sur tous les utilisateurs de la base de données, trouve toutes les activités de tous les amis de cet utilisateur, puis écrit ces activités dans le fil d'actualité.

Pour l'instant, le SQL qui élimine l'activité (appelée en ActivityLog::getUsersActivity() ) a un LIMIT 100 imposée pour des raisons de performance*. *Non pas que je sache de quoi je parle.

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

Affichage des flux d'actualités

Dans le code client, lorsque je récupère le fil d'actualité de l'utilisateur, je fais quelque chose comme.. :

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

}

Améliorer le fil d'actualité

Maintenant, pardonnez ma compréhension limitée des meilleures pratiques pour développer un fil d'actualité, mais je comprends que l'approche que j'utilise est une version limitée de ce que l'on appelle fan-out sur l'écriture Le projet est limité dans le sens où j'exécute une tâche cron comme étape intermédiaire au lieu d'écrire directement dans le fil d'actualité des utilisateurs. Mais c'est très différent d'un modèle pull, dans le sens où le fil d'actualité de l'utilisateur n'est pas compilé à la charge, mais plutôt de façon régulière.

C'est une grande question qui mérite probablement une grande quantité d'allers-retours, mais je pense qu'elle peut servir de pierre de touche pour de nombreuses conversations importantes que les nouveaux développeurs comme moi doivent avoir. J'essaie juste de comprendre ce que je fais mal, comment je peux m'améliorer, ou comment je devrais peut-être même repartir de zéro et essayer une approche différente.

Une autre chose qui me gêne dans ce modèle est qu'il fonctionne sur la base de la récence plutôt que de la pertinence. Si quelqu'un peut suggérer comment l'améliorer pour y intégrer la pertinence, je suis tout ouïe. J'utilise l'API de Directed Edge pour générer des recommandations, mais il semble que pour quelque chose comme un flux d'actualités, les recommandations ne fonctionnent pas (puisque rien n'a été favorisé auparavant !).

0 votes

Quoi ? Pas d'image humoristique attachée cette fois-ci ? Vous pouvez faire mieux que ça ! :P

0 votes

@alex Oh, mec, tu as raison. Je dois être en train de vieillir.

1 votes

@Josh Essayez ceci :)

16voto

Dan Spiteri Points 624

Question vraiment cool. Je suis en fait en train de mettre en œuvre quelque chose comme ça moi-même. Donc, je vais réfléchir à voix haute un peu.

Voici les défauts que je vois dans mon esprit avec votre implémentation actuelle :

  1. Vous traitez tous les amis de tous les utilisateurs, mais vous finirez par traiter les mêmes utilisateurs plusieurs fois, car les mêmes groupes de personnes ont des amis similaires.

  2. Si l'un de mes amis publie quelque chose, cela n'apparaîtra pas sur mon fil d'actualité avant au moins 5 minutes. Alors qu'il devrait apparaître immédiatement, non ?

  3. Nous lisons l'ensemble du fil d'actualités d'un utilisateur. N'avons-nous pas juste besoin de saisir les nouvelles activités depuis la dernière fois que nous avons analysé les journaux ?

  4. Ça ne marche pas très bien.

Le fil d'actualité semble contenir exactement les mêmes données que le journal d'activité, je m'en tiendrais à cette seule table de journal d'activité.

Si vous répartissez vos journaux d'activité entre plusieurs bases de données, vous pourrez évoluer plus facilement. Vous pouvez également répartir vos utilisateurs si vous le souhaitez, mais même si vous avez 10 millions d'enregistrements d'utilisateurs dans une table, mysql devrait pouvoir effectuer des lectures sans problème. Ainsi, chaque fois que vous recherchez un utilisateur, vous savez à partir de quel shard vous devez accéder aux journaux de l'utilisateur. Si vous archivez vos anciens journaux de temps en temps et que vous ne conservez qu'un jeu de journaux récents, vous n'aurez pas besoin de sharder autant. Ou peut-être même pas du tout. Vous pouvez gérer plusieurs millions d'enregistrements dans MySQL si vous êtes réglé même modérément bien.

J'utiliserais memcached pour votre table des utilisateurs et peut-être même pour les journaux eux-mêmes. Memcached permet des entrées de cache d'une taille maximale de 1 Mo, et si vous organisez intelligemment vos clés, vous pouvez potentiellement récupérer tous les journaux les plus récents dans le cache.

Il s'agit d'un travail plus important en termes d'architecture, mais il vous permettra de travailler en temps réel et d'évoluer à l'avenir... surtout si vous souhaitez que les utilisateurs commencent à utiliser le système. commentaires sur chaque poste ;)

Vous avez vu cet article ?

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

1voto

Aakash Sharma Points 24

Entre vous pouvez utiliser les drapeaux d'utilisateur et la mise en cache. Disons qu'il y a un nouveau champ pour l'utilisateur comme last_activity. Mettez à jour ce champ chaque fois que l'utilisateur entre dans une activité. Gardez un drapeau, jusqu'à quelle heure vous avez récupéré les flux, disons feed_updated_on.

Mettez maintenant à jour la fonction $user->getAllUsers() ; pour ne renvoyer que les utilisateurs dont le temps de dernière_activité est postérieur à feed_updated_on. Cela exclura tous les utilisateurs qui n'ont pas de journal d'activité :). Processus similaire pour les utilisateurs amis.

Vous pouvez également utiliser la mise en cache comme memcache ou la mise en cache au niveau des fichiers.

Ou utiliser une base de données nosql pour stocker tous les flux en un seul document.

1voto

Freeman Latif Points 401

J'essaie de créer un fil d'actualité de type Facebook. Au lieu de créer une autre table pour enregistrer les activités des utilisateurs, j'ai calculé le "bord" à partir de l'UNION des messages, des commentaires, etc.

Avec un peu de mathématiques, je calcule le "bord" en utilisant un modèle de décroissance exponentielle, le temps écoulé étant la variable indépendante, en tenant compte du nombre de commentaires, de likes, etc. de chaque message pour formuler la constante lambda. Le bord diminue rapidement au début, mais s'aplatit progressivement pour atteindre presque 0 après quelques jours (mais n'atteindra jamais 0).

Lors de l'affichage du flux, chaque bord est multiplié à l'aide de RAND(). Les messages avec un bord plus élevé apparaîtront plus souvent.

De cette façon, les messages les plus populaires ont plus de chances d'apparaître dans le fil d'actualité, et ce plus longtemps.

5 votes

Vous n'avez pas mentionné si le bord est pré-calculé ou calculé au moment de l'exécution ?

1voto

jsh Points 1067

Au lieu de lancer une tâche cron, un script post-commit de quelque sorte. Je ne sais pas spécifiquement quelles sont les capacités de PHP et de MySQL à cet égard - si je me souviens bien, MySQL InnoDB permet des fonctionnalités plus avancées que les autres variétés, mais je ne me souviens pas s'il y a des choses comme les triggers dans la dernière version.

de toute façon, une variété simple qui ne repose pas sur beaucoup de magie de base de données :

lorsque l'utilisateur X ajoute du contenu :

1) faire un appel asynchrone à partir de votre page PHP après la validation de la base de données (asynchrone bien sûr pour que l'utilisateur qui consulte la page n'ait pas à l'attendre !)

L'appel démarre une instance de votre script logique.

2) la logique script va uniquement à travers la liste d'amis [A,B,C] de l'utilisateur qui a publié le nouveau contenu (par opposition à la liste de toutes les personnes présentes dans la base de données) et ajoute l'action de l'utilisateur X aux flux de chacun de ces utilisateurs.

Vous pourriez simplement stocker ces flux sous forme de fichiers JSON et ajouter de nouvelles données à la fin de chacun d'eux. Il est bien sûr préférable de conserver les flux dans un cache avec une sauvegarde dans un système de fichiers ou dans BerkeleyDB ou Mongo ou ce que vous voulez.

Il s'agit simplement d'une idée de base pour les flux basés sur la récence, et non sur la pertinence. Vous POURRIEZ stocker les données séquentiellement de cette manière et ensuite faire une analyse supplémentaire par utilisateur pour filtrer par pertinence, mais c'est un problème difficile dans n'importe quelle application et probablement pas un problème qui peut être facilement résolu par un utilisateur web anonyme sans connaissance détaillée de vos exigences ;)

jsh

0voto

Blender Points 114729

Pourriez-vous ajouter des mots-clés statistiques ? J'ai fait une implémentation (grossière) en explosant le corps de mon document, en enlevant le HTML, en supprimant les mots courants et en comptant les mots les plus courants. J'ai fait cela il y a quelques années juste pour le plaisir (comme pour tout projet de ce type, la source a disparu), mais cela a fonctionné pour mon installation temporaire de test-blog/forum. Peut-être cela fonctionnera-t-il pour votre fil d'actualité...

0 votes

3D En fait, cela est plus facile à mettre en œuvre avec une FULLTEXT moteur de recherche comme Sphinx, qui est une autre approche possible. Ce qui me préoccupe avec quelque chose comme ça ou l'approche suggérée par @stillstanding, c'est que cela ressemble à une pirouette par-dessus une pirouette. Ce que j'aimerais vraiment faire pour déterminer la pertinence, c'est calculer le score d'affinité additionné de l'utilisateur avec le créateur du contenu, le poids du type de contenu et un facteur de décroissance dans le temps. Mais je ne sais pas encore très bien comment m'y prendre...

0 votes

Jusqu'à quel niveau de complexité laisserez-vous cela se développer ? Cela semble être une distribution de poids assez lourde, mais c'est faisable. Il faudrait ajouter une décroissance logarithmique à la pertinence avec l'âge, mais obtenir le "type de contenu" est assez vague. Il faudrait configurer un tableau de mots-clés à comparer pour le déterminer (solution rapide, mais qui ne serait pas idéale pour une application à grande échelle). Cela demande des compétences intenses en statistiques et en lecture informatique...

0 votes

Ce sera probablement assez complexe ; pensez au fil d'actualité de Facebook. Mais c'est quelque chose qui va probablement nécessiter une réflexion à plus grande échelle, au-delà de ce que j'ai ici.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X