Comment implémenter le flux d'activité dans un réseau social

141

Je développe mon propre réseau social, et je n'ai pas trouvé sur le web d'exemples d'implémentation du flux d'actions des utilisateurs ... Par exemple, comment filtrer les actions pour chaque utilisateur? Comment stocker les événements d'action? Quel modèle de données et quel modèle d'objet puis-je utiliser pour le flux d'actions et pour les actions elles-mêmes?

Nicolò Martini
la source
9
bonne chance, c'est la question sans fin que nous voulons tous savoir, comment Facebook réussit-il, la réponse est très complexe et nous ne saurons peut-être jamais la manière la plus efficace de le faire. Si vous trouvez une bonne approche, veuillez la poster ici pour que d'autres puissent la voir, BTW cela a été discuté à maintes reprises sur SO alors recherchez simplement et vous trouverez quelques conseils
JasonDavis
1
Stream Framework est la solution la plus utilisée: github.com/tschellenbach/Stream-Framework Voir également cette liste de packages: djangopackages.com/grids/g/activities
Thierry
1
En termes de personnalisation, il est basé sur l'analyse et l'apprentissage automatique, voir également getstream.io/personalization
Thierry

Réponses:

242

Résumé : Pour environ 1 million d'utilisateurs actifs et 150 millions d'activités stockées, je reste simple:

  • Utilisez une base de données relationnelle pour le stockage des activités uniques (1 enregistrement par activité / "chose qui s'est produite") Rendez les enregistrements aussi compacts que possible. Structure afin que vous puissiez rapidement saisir un lot d'activités par ID d'activité ou en utilisant un ensemble d'ID d'amis avec des contraintes de temps.
  • Publiez les ID d'activité sur Redis chaque fois qu'un enregistrement d'activité est créé, en ajoutant l'ID à une liste de "flux d'activité" pour chaque utilisateur qui est un ami / abonné qui devrait voir l'activité.

Interrogez Redis pour obtenir le flux d'activité de n'importe quel utilisateur, puis récupérez les données associées de la base de données si nécessaire. Revenez à l'interrogation de la base de données par heure si l'utilisateur a besoin de naviguer très loin dans le temps (si vous offrez même cela)


J'utilise une vieille table MySQL pour traiter environ 15 millions d'activités.

Cela ressemble à quelque chose comme ceci:

id             
user_id       (int)
activity_type (tinyint)
source_id     (int)  
parent_id     (int)
parent_type   (tinyint)
time          (datetime but a smaller type like int would be better) 

activity_typeme dit le type d'activité, source_idme dit le record auquel l'activité est liée. Donc, si le type d'activité signifie "favori ajouté", je sais que le source_id fait référence à l'ID d'un enregistrement favori.

Le parent_id/parent_type sont utiles pour mon application - ils me disent à quoi l'activité est liée. Si un livre a été ajouté aux favoris, alors parent_id / parent_type me dirait que l'activité se rapporte à un livre (type) avec une clé primaire (id) donnée

J'indexe (user_id, time)et interroge les activités qui le sont user_id IN (...friends...) AND time > some-cutoff-point. Abandonner l'identifiant et choisir un autre index clusterisé pourrait être une bonne idée - je n'ai pas expérimenté cela.

Des trucs assez basiques, mais cela fonctionne, c'est simple et il est facile de travailler avec lorsque vos besoins changent. De plus, si vous n'utilisez pas MySQL, vous pourrez peut-être faire mieux en termes d'index.


Pour accéder plus rapidement aux activités les plus récentes, j'ai expérimenté Redis . Redis stocke toutes ses données en mémoire, vous ne pouvez donc pas y placer toutes vos activités, mais vous pouvez en stocker suffisamment pour la plupart des écrans les plus fréquemment consultés sur votre site. Les 100 plus récents pour chaque utilisateur ou quelque chose comme ça. Avec Redis dans le mix, cela pourrait fonctionner comme ceci:

  • Créez votre enregistrement d'activité MySQL
  • Pour chaque ami de l'utilisateur qui a créé l'activité, poussez l'ID sur sa liste d'activités dans Redis.
  • Coupez chaque liste aux X derniers éléments

Redis est rapide et offre un moyen de canaliser les commandes sur une seule connexion - donc, pousser une activité à 1000 amis prend des millisecondes.

Pour une explication plus détaillée de ce dont je parle, voir l'exemple Twitter de Redis: http://redis.io/topics/twitter-clone

Mise à jour Février 2011 J'ai actuellement 50 millions d'activités actives et je n'ai rien changé. Une bonne chose à propos de faire quelque chose de similaire est qu'il utilise de petites lignes compactes. Je prévois d'apporter des changements qui impliqueraient beaucoup plus d'activités et plus de requêtes sur ces activités et j'utiliserai certainement Redis pour accélérer les choses. J'utilise Redis dans d'autres domaines et cela fonctionne vraiment bien pour certains types de problèmes.

Mise à jour juillet 2014 Nous sommes jusqu'à environ 700 000 utilisateurs actifs par mois. Depuis quelques années, j'utilise Redis (comme décrit dans la liste à puces) pour stocker les 1000 derniers identifiants d'activité de chaque utilisateur. Il y a généralement environ 100 millions d'enregistrements d'activité dans le système et ils sont toujours stockés dans MySQL et ont toujours la même disposition. Ces enregistrements nous permettent de nous en sortir avec moins de mémoire Redis, ils servent d'enregistrement des données d'activité et nous les utilisons si les utilisateurs ont besoin de reculer dans le temps pour trouver quelque chose.

Ce n'était pas une solution intelligente ou particulièrement intéressante, mais cela m'a bien servi.

surpassé
la source
2
+1 pour Redis. v2 utilise la mémoire virtuelle, il devrait donc être possible de s'appuyer entièrement sur Redis
stagas
16
S'il y a plusieurs sources d'activité (ajouter, commenter, aimer, etc.), comment joindre cette table aux activités réelles? Utilisez-vous plusieurs jointures à gauche (chacune pour une table d'activité)?
Ali Shakiba le
1
@casey Echoing question de @JohnS - comment effectuez-vous le JOINsur les différentes activity_typetables? Ces jointures sont-elles coûteuses en termes de performances?
Rob Sobers
1
Quelqu'un at-il une réponse à la question de JohnS sur le "JOIN". Quelqu'un peut-il publier un lien où il pourrait être expliqué? Je dois faire la même chose et cela me serait très utile.
Waseem
3
Aucune jointure. Une requête par unique activity_typepour obtenir les autres données dont vous avez besoin.
dépassé le
21

Ceci est mon implémentation d'un flux d'activité, en utilisant mysql. Il existe trois classes: Activity, ActivityFeed, Subscriber.

L'activité représente une entrée d'activité et sa table ressemble à ceci:

id
subject_id
object_id
type
verb
data
time

Subject_id est l'identifiant de l'objet exécutant l'action, object_id l'id de l'objet qui reçoit l'action. typeet verbdécrit l'action elle-même (par exemple, si un utilisateur ajoute un commentaire à un article, il serait respectivement "commentaire" et "créé"), les données contiennent des données supplémentaires afin d'éviter les jointures (par exemple, elles peuvent contenir le nom du sujet et nom, le titre et l'url de l'article, le corps du commentaire, etc.).

Chaque activité appartient à un ou plusieurs ActivityFeeds, et ils sont liés par une table qui ressemble à ceci:

feed_name
activity_id

Dans mon application, j'ai un flux pour chaque utilisateur et un flux pour chaque élément (généralement des articles de blog), mais ils peuvent être ce que vous voulez.

Un Abonné est généralement un utilisateur de votre site, mais il peut aussi s'agir de n'importe quel objet de votre modèle objet (par exemple, un article pourrait être abonné au feed_action de son créateur).

Chaque Abonné appartient à un ou plusieurs ActivityFeeds, et, comme ci-dessus, ils sont liés par une table de liens de ce type:

feed_name
subscriber_id
reason

Le reasonchamp ici explique pourquoi l'abonné s'est abonné au flux. Par exemple, si un utilisateur ajoute un signet à un article de blog, la raison est «signet». Cela m'aide plus tard à filtrer les actions pour les notifications aux utilisateurs.

Pour récupérer l'activité d'un abonné, je fais une simple jointure des trois tables. La jointure est rapide car je sélectionne peu d'activités grâce à une WHEREcondition qui ressemble maintenant - time > some hours. J'évite d'autres jointures grâce au champ de données dans la table d'activité.

Plus d'explications sur le reasonterrain. Si, par exemple, je souhaite filtrer les actions pour les notifications par e-mail à l'utilisateur et que l'utilisateur a mis un article de blog en signet (et qu'il s'abonne donc au fil de publication avec la raison `` signet ''), je ne veux pas que l'utilisateur reçoive notifications par e-mail sur les actions sur cet élément, tandis que s'il commente le message (et qu'il s'abonne au fil de publication avec la raison `` commentaire ''), je veux qu'il soit averti lorsque d'autres utilisateurs ajoutent des commentaires sur le même message. Le champ raison m'aide dans cette discrimination (je l'ai implémenté via une classe ActivityFilter), ainsi que les préférences de notification de l'utilisateur.

Nicolò Martini
la source
Nicolo Martini Je voulais ajouter un commentaire de réponse sur l'activité et le montrer en dessous, comment est-ce possible avec votre structure? devrais-je ajouter un autre tableau ou simplement utiliser le même, si c'est le cas, quelles sont vos suggestions?
Basit le
Quelle est la performance de cette implémentation? Des tests sur de grandes tables?
Joshua F.Rountree
16

Il existe un format actuel pour le flux d'activités qui est développé par un groupe de personnes bien connues.

http://activitystrea.ms/ .

Fondamentalement, chaque activité a un acteur (qui exécute l'activité), un verbe (l'action de l'activité), un objet (sur lequel l'acteur joue) et une cible.

Par exemple: Max a publié un lien vers le mur d'Adam.

Leur spécification JSON a atteint la version 1.0 au moment de la rédaction, ce qui montre le modèle de l'activité que vous pouvez appliquer.

Leur format a déjà été adopté par BBC, Gnip, Google Buzz Gowalla, IBM, MySpace, Opera, Socialcast, Superfeedr, TypePad, Windows Live, YIID, et bien d'autres.

Sơn Trần-Nguyễn
la source
salut @sntran Je sais que ce post date d'il y a des années, mais j'ai une question plus sur le flux d'activité. Pouvez-vous aider?
hiswendy
Sûr. Quelle est ta question?
Sơn Trần-Nguyễn
Ma question est en fait postée ici! lien . Je pense avoir une compréhension de base du flux d'activité, mais je ne sais vraiment pas comment l'implémenter (c'est-à-dire suis-je censé utiliser angular ou node.js?) Et à partir de là, comment créer réellement un flux d'activité avec API entrante JSON? Ce sont des questions de base, mais je n'ai trouvé aucune réponse en ligne. Si vous pouvez aider, je l'apprécierais vraiment. Je vous remercie!
hiswendy
13

Je pense qu'une explication sur le fonctionnement du système de notifications sur les grands sites Web peut être trouvée dans la question de débordement de pile: comment les sites de réseaux sociaux calculent-ils les mises à jour des amis? , dans la réponse de Jeremy Wall . Il suggère l'utilisation de Message Qeue et il indique deux logiciels open source qui l'implémentent:

  1. RabbitMQ
  2. Apache QPid

Voir aussi la question Quelle est la meilleure manière de mettre en œuvre un flux d'activité sociale?

Nicolò Martini
la source
1

Vous avez absolument besoin d'une file d'attente de messages performante et distribuée. Mais cela ne s'arrête pas là, vous devrez prendre des décisions sur ce qu'il faut stocker en tant que données persistantes et ce qui est transitoire, etc.

Quoi qu'il en soit, c'est vraiment une tâche difficile mon ami si vous recherchez un système performant et évolutif. Mais, bien sûr, certains ingénieurs généreux ont partagé leur expérience à ce sujet. LinkedIn a récemment rendu son système de file d'attente de messages Kafka open source. Avant cela, Facebook avait déjà fourni Scribe à la communauté open source. Kafka est écrit en Scala et au début, il faut un certain temps pour le faire fonctionner, mais j'ai testé avec quelques serveurs virtuels. C'est vraiment rapide.

http://blog.linkedin.com/2011/01/11/open-source-linkedin-kafka/

http://incubator.apache.org/kafka/index.html

Cagatay Kalan
la source
0

Au lieu de lancer le vôtre, vous pouvez vous tourner vers un service tiers utilisé via une API. J'en ai lancé un appelé Collabinate ( http://www.collabinate.com ) qui a un backend de base de données de graphes et des algorithmes assez sophistiqués pour gérer de grandes quantités de données de manière hautement simultanée et hautement performante. Bien qu'il n'ait pas l'étendue des fonctionnalités que disent Facebook ou Twitter, cela suffit largement pour la plupart des cas d'utilisation où vous devez créer des flux d'activité, des flux sociaux ou des fonctionnalités de microblogage dans une application.

Mafuba
la source