Comment puis-je améliorer cette PHP/MySQL fil d'actualité?
Permettez-moi de commencer dès le départ en disant que je sais que ce n'est pas la meilleure solution. Je sais que c'est encombrants et d'un hack d'une fonctionnalité. Mais c'est pourquoi je suis ici!
Cette question/travail repose sur une discussion sur Quora avec Andrew Bosworth, le créateur de Facebook du flux d'actualités.
Je suis en train de construire un flux d'actualités de toutes sortes. Il est construit uniquement dans PHP
et MySQL
.
MySQL
Le modèle relationnel de l'alimentation est composée de deux tables. Une table fonctionne comme un journal d'activité; en fait, il est nommé activity_log
. L'autre tableau est newsfeed
. Ces tables sont presque identiques.
La schéma pour le journal est activity_log(uid INT(11), activity ENUM, activity_id INT(11), title TEXT, date TIMESTAMP)
...et la schéma de l'alimentation 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 de pertinents pour le flux d'actualités, par exemple en posant une question, il sont consignés dans le journal d'activité immédiatement.
Générer le flux de nouvelles
Puis toutes les X minutes (5 minutes au moment du changement de 15 à 30 minutes plus tard), - je exécuter une tâche cron qui exécute le script ci-dessous. Ce script fait une boucle par tous les utilisateurs dans la base de données, trouve toutes les activités pour tous que l'utilisateur amis, puis écrit ces activités à l'actualité.
À l'heure actuelle, la SQL
que les résidus de l'activité (appelé dans ActivityLog::getUsersActivity()
) a un LIMIT 100
imposées pour la performance* raisons. *Pas que je sais 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']);
}
}
}
}
Afficher le flux de nouvelles
Dans le code du client, lors de l'extraction de l'utilisateur du fil d'actualité, 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 flux d'actualités
Maintenant, pardonne ma compréhension limitée des meilleures pratiques pour l'élaboration d'un flux d'actualités, mais je comprends la démarche, je suis en utilisant une version limitée de ce qu'on appelle fan-out sur l'écriture, limitée dans le sens que je suis en cours d'exécution d'une tâche cron comme une étape intermédiaire au lieu d'écrire pour les utilisateurs de nouvelles rss directement. Mais c'est très différent d'un modèle d'extraction, dans le sens que l'utilisateur du fil d'actualité n'est pas compilé sur la charge, mais plutôt sur une base régulière.
C'est une grande question qui mérite sans doute une grande quantité de va-et-vient, mais je pense qu'il peut servir de pierre de touche pour de nombreuses conversations importantes que les nouveaux développeurs, comme moi, ont besoin d'avoir. Je suis juste essayer de comprendre ce que je fais mal, comment je peux l'améliorer, ou comment je devrais peut-être même commencer à partir de zéro et de tenter une approche différente.
Une autre chose qui me dérange à propos de ce modèle est qu'il travaille en se fondant sur l'expérience récente plutôt que de pertinence. Si quelqu'un peut suggérer comment cela peut être amélioré pour les travaux de pertinence dans le, je serais à toutes les oreilles. Je suis Orientée à Bord de l'API pour générer des recommandations, mais il semble que quelque chose comme un flux d'actualités, prescripteurs ne fonctionne pas (car rien de ce qui a été précédemment ajoutés à vos favoris!).
- Quoi? Aucun humour image ci-jointe de ce temps? Vous pouvez faire mieux que ça! 😛
- Oh, l'homme, vous avez raison. Je dois être vieux.
- Essayez ceci 🙂
- Je vais le faire maintenant. Vous tentatrice.
- Geddit? Haha!
- Si vous êtes intéressé, édité ici pour ajouter un lien à une discussion intéressante sur Quora avec Andrew Bosworth, à partir de laquelle ce travail a été inspiré quora.com/...
- vous êtes à 3 de 3 ce soir.
- 3 pour 3? Mon vendredi après-midi, le cerveau ne fait pas de calcul...
- cela signifie 3 succès sur 3 tentatives.
- Un inconvénient de l'utilisation d'ENUM, c'est que si vous voulez ajouter de nouveaux types d'activité, alors vous aurez besoin pour effectuer une commande ALTER table, qui sera prohibitif si vous avez un grand nombre de lignes. (un ALTER table des verrous pour l'écrit, et pendant une partie du processus, il permet pour les lectures).
- Je ne connaissais pas ce sujet
ENUM
. Est-il une alternative vous suggère à la place? - Smith est-ce que chaque utilisateur d'avoir un fil d'actualité de la table?
- smith Si vous reconstruisez votre algorithme ci-dessus, veuillez la publier. Merci
- avez-vous d'exécuter une requête SQL pour chaque ami à saisir leurs activités?
- Je suis en train de faire les news, basée sur l'utilisateur suivant. L'utilisateur peut avoir 1000s de disciples. Mon idée est de créer un tableau distinct pour chaque utilisateur (lors de l'enregistrement de l'utilisateur) qui permet de stocker de l'activité de tous les utilisateurs, il sera la suite. Ma question est de savoir quand une personne frappe bouton j'aime et il a disons 1000 followers. Pour stocker des données dans chacun des disciples "mangeoire", je vais utiliser une boucle foreach. Est l'aide de boucle foreach bon ou mauvais? Que faire si les boucles de pauses et cesse de répondre? Si l'utilisateur est d'avoir un nombre élevé d'adeptes boucle foreach peut prendre trop de temps sur chaque activité de l'utilisateur effectue. Toute solution?
- Peut-être en utilisant une table de jointure, puis un user_id dans (ami de l'id).
Vous devez vous connecter pour publier un commentaire.
Vraiment cool question. Je suis réellement dans le milieu de la mise en œuvre de quelque chose comme ça moi. Donc, je vais penser fort un peu.
Voici les défauts que je vois dans mon esprit, avec la mise en œuvre:
Vous traitez tous les amis pour tous les utilisateurs, mais vous finirez par le traitement de ces mêmes utilisateurs de nombreuses fois en raison du fait que les mêmes groupes de personnes qui ont les mêmes amis.
Si l'un de mes amis postes quelque chose, il ne sera pas affiché sur mon fil d'actualité pour au plus 5 minutes. Alors qu'il devrait s'afficher immédiatement, à droite?
Nous sommes à la lecture de tout le fil d'actualité d'un utilisateur. N'avons-nous pas simplement le besoin de saisir les nouvelles activités depuis la dernière fois que nous nous rassemblons les journaux?
Ce n'est pas que du bien.
Le fil d'actualité ressemble exactement les mêmes données que le journal d'activité, je m'en tiendrais à qui un journal d'activité de la table.
Si vous éclat de vos journaux d'activité dans les bases de données, il vous permettra d'échelle plus facile. Vous pouvez éclat de vos utilisateurs, si vous le souhaitez, mais même si vous disposez de 10 millions d'utilisateurs des enregistrements dans une table mysql doit être fine faire les lectures. Donc, chaque fois que vous la recherche d'un utilisateur, vous connaissez le fragment d'accès de l'utilisateur, les journaux de. Si vous archivez vos vieux journaux de temps en temps et seulement à maintenir un ensemble frais de journaux, vous n'aurez pas à le fragment d'autant. Ou peut-être même à tous. Vous pouvez gérer plusieurs millions d'enregistrements dans MySQL si vous êtes à l'écoute, même modérément bien.
Je voudrais tirer parti de memcached pour votre table des utilisateurs et peut-être même les journaux eux-mêmes. Memcached permet les entrées du cache jusqu'à 1 mo en taille, et si vous étiez intelligent dans l'organisation de vos clés, vous pourriez potentiellement récupérer toutes les plus récentes de journaux à partir du cache.
Ce serait plus de travail autant que l'architecture est concerné, mais il vous permettra de travailler en temps réel et de l'échelle dans l'avenir...surtout quand vous voulez que les utilisateurs commencent à commentant sur l'affichage. 😉
Avez-vous vu cet article?
http://bret.appspot.com/entry/how-friendfeed-uses-mysql
Feriez-vous ajouter des statistiques de keywording? J'ai fait un (brut) mise en œuvre par l'explosion de la corps de mon document, le décapage HTML, en supprimant les mots communs, et de compter les mots les plus communs. J'ai fait il y a quelques années, juste pour le plaisir (comme pour tout projet, la source a disparu), mais cela a fonctionné pour mon test temporaire-blog/forum de l'installation. Peut-être qu'il va travailler pour votre fil d'actualité...
FULLTEXT
moteur de recherche, comme le Sphinx, qui est une autre approche possible. Le souci que j'ai avec quelque chose comme ceci ou de l'approche @stillstanding suggéré, c'est qu'il se sent comme un hack sur le dessus d'un hack. Ce que j'aimerais vraiment faire pour déterminer la pertinence est de calculer l'utilisateur résumé score d'affinité avec le créateur du contenu, le poids pour le type de contenu, et un temps de decay. Mais pas vraiment sûr de savoir comment aller à ce sujet pour l'instant...entre vous pouvez utiliser de l'utilisateur drapeaux et la mise en cache.
Permet de le dire, un nouveau champ pour l'utilisateur comme last_activity.
Mise à jour de ce champ à chaque fois que l'utilisateur entre dans toute activité.
Garder un drapeau, jusqu'à quelle heure vous avez récupéré le flux permet de dire qu'il feed_updated_on.
Maintenant mettre à jour la fonction $utilisateur->getAllUsers(); pour renvoyer uniquement les utilisateurs qui ont last_activity temps plus tard feed_updated_on.
Cela exclut tous les utilisateurs qui ne marche pas toute l'activité du journal :).
Même processus pour les utilisateurs des amis.
Vous pouvez également utiliser la mise en cache comme memcache ou au niveau du fichier de cache.
Ou d'utiliser certains nosql DB pour le stockage de tous les flux en un seul document.
Je suis en train de construire un Facebook-style de fil de nouvelles sur mon propre. Au lieu de créer un autre tableau pour consigner les activités des utilisateurs, j'ai calculé le "bord" de l'UNION des messages, des commentaires, etc.
Avec un peu de mathématiques, j'ai calculer le bord à l'aide d'une décroissance exponentielle du modèle, avec le temps écoulé étant la variable indépendante, en prenant en compte le nombre de commentaires, likes, etc chaque poste doit formuler le lambda constante. Le bord de la diminution rapide au début, mais progressivement s'aplatit à près de 0 après quelques jours (mais ne sera jamais atteindre 0)
Lors de la démonstration de l'alimentation, de chaque bord est multiplié à l'aide de RAND(). Poteaux avec bord supérieur apparaît le plus souvent
De cette façon, de plus en plus populaire postes ont plus de probabilité d'apparaître dans le fil d'actualité, pour un temps plus long.
Au lieu de l'exécution d'une tâche cron, un post-script de validation d'une certaine sorte. Je ne sais pas précisément ce que les capacités de PHP et MySQL sont à cet égard - si je me souviens bien MySQL InnoDB permet de fonctionnalités plus avancées que d'autres variétés, mais je ne me souviens pas si il y a des choses comme les déclencheurs dans la dernière version.
de toute façon, une simple variété qui ne repose pas sur un terrain de la base de données de la magie:
lorsque l'utilisateur X ajoute du contenu:
1) faire un appel asynchrone à partir de votre page PHP après la base de données commit (async du cours, afin que l'utilisateur de l'affichage de la page n'a pas à attendre pour lui!)
Le début de l'appel d'une instance de votre logique de script.
2) la logique de script va seulement par le biais de la liste d'amis [A,B,C] de l'utilisateur qui a commis le nouveau contenu (par opposition à la liste de tout le monde dans la DB!) et ajoute l'action de l'utilisateur X à des flux pour chacun de ces utilisateurs.
Vous pouvez simplement stocker ces flux directement en haut des fichiers JSON et ajouter de nouvelles données à la fin de chaque. Mieux, bien sûr pour garder les aliments dans le cache avec une sauvegarde de système de fichiers ou de BerkeleyDB ou Mongo ou ce que vous voulez.
C'est juste une idée de base pour les flux basés sur la récence, pas de pertinence. Vous pouvez stocker les données de manière séquentielle dans cette façon et puis de faire des analyse sur une base par utilisateur pour filtrer en fonction de leur pertinence, mais c'est un problème difficile dans n'importe quelle application et probablement pas celui qui peut être facilement traitée par un anonyme sur le web de l'utilisateur, sans une connaissance détaillée de vos besoins 😉
jsh