So implementieren Sie den Aktivitätsstrom in einem sozialen Netzwerk

140

Ich entwickle mein eigenes soziales Netzwerk und habe im Web keine Beispiele für die Implementierung des Streams der Benutzeraktionen gefunden. Wie kann ich beispielsweise Aktionen für jeden Benutzer filtern? Wie speichere ich die Aktionsereignisse? Welches Datenmodell und Objektmodell kann ich für den Aktionsstrom und für die Aktionen selbst verwenden?

Nicolò Martini
quelle
9
Viel Glück, dies ist die unendliche Frage, die wir alle wissen wollen, wie Facebook es schafft, die Antwort ist sehr komplex und wir wissen möglicherweise nie, wie dies am effizientesten funktioniert. Wenn Sie einen GUTEN Ansatz finden, veröffentlichen Sie ihn bitte hier, damit andere ihn sehen können. Übrigens wurde dies schon viele Male auf SO diskutiert. Suchen Sie also einfach und Sie werden einige Tipps finden
JasonDavis
1
Stream Framework ist die am weitesten verbreitete Lösung: github.com/tschellenbach/Stream-Framework Siehe auch diese Liste von Paketen: djangopackages.com/grids/g/activities
Thierry
1
In Bezug auf die Personalisierung basiert es auf Analytik und maschinellem Lernen. Siehe auch getstream.io/personalization
Thierry

Antworten:

241

Zusammenfassung : Für ungefähr 1 Million aktive Benutzer und 150 Millionen gespeicherte Aktivitäten halte ich es einfach:

  • Verwenden Sie eine relationale Datenbank zum Speichern eindeutiger Aktivitäten (1 Datensatz pro Aktivität / "Ereignis"). Machen Sie die Datensätze so kompakt wie möglich. Strukturieren Sie diese so, dass Sie schnell einen Stapel von Aktivitäten nach Aktivitäts-ID oder mithilfe einer Reihe von Freund-IDs mit zeitlichen Einschränkungen abrufen können.
  • Veröffentlichen Sie die Aktivitäts-IDs bei jeder Erstellung eines Aktivitätsdatensatzes in Redis und fügen Sie die ID einer Liste "Aktivitätsdatenstrom" für jeden Benutzer hinzu, der ein Freund / Abonnent ist, der die Aktivität sehen soll.

Fragen Sie Redis ab, um den Aktivitätsdatenstrom für einen beliebigen Benutzer abzurufen, und rufen Sie dann die zugehörigen Daten nach Bedarf aus der Datenbank ab. Versuchen Sie, die Datenbank nach Zeit abzufragen, wenn der Benutzer in der Zeit weit zurückblättern muss (wenn Sie dies überhaupt anbieten).


Ich verwende eine einfache alte MySQL-Tabelle für etwa 15 Millionen Aktivitäten.

Es sieht ungefähr so ​​aus:

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_typesagt mir die Art der Aktivität, source_idsagt mir den Datensatz, mit dem die Aktivität zusammenhängt. Wenn der Aktivitätstyp "Favorit hinzugefügt" bedeutet, weiß ich, dass sich die Quell-ID auf die ID eines Favoriten-Datensatzes bezieht.

Die parent_id/ parent_typesind nützlich für meine App - sie sagen mir, worauf sich die Aktivität bezieht. Wenn ein Buch favorisiert wurde, würde parent_id / parent_type mir mitteilen, dass sich die Aktivität auf ein Buch (Typ) mit einem bestimmten Primärschlüssel (ID) bezieht.

Ich indiziere (user_id, time)und frage nach Aktivitäten, die sind user_id IN (...friends...) AND time > some-cutoff-point. Es könnte eine gute Idee sein, die ID fallen zu lassen und einen anderen Clustered-Index auszuwählen - damit habe ich nicht experimentiert.

Ziemlich einfaches Zeug, aber es funktioniert, es ist einfach und es ist einfach, damit zu arbeiten, wenn sich Ihre Bedürfnisse ändern. Wenn Sie MySQL nicht verwenden, können Sie möglicherweise indextechnisch bessere Ergebnisse erzielen.


Um schneller auf die neuesten Aktivitäten zugreifen zu können, habe ich mit Redis experimentiert . Redis speichert alle seine Daten im Speicher, sodass Sie nicht alle Ihre Aktivitäten dort ablegen können, aber Sie könnten genug für die meisten häufig verwendeten Bildschirme auf Ihrer Website speichern. Die letzten 100 für jeden Benutzer oder so ähnlich. Mit Redis in der Mischung könnte es so funktionieren:

  • Erstellen Sie Ihren MySQL-Aktivitätsdatensatz
  • Schieben Sie für jeden Freund des Benutzers, der die Aktivität erstellt hat, die ID in seine Aktivitätsliste in Redis.
  • Schneiden Sie jede Liste auf die letzten X Elemente

Redis ist schnell und bietet eine Möglichkeit, Befehle über eine Verbindung zu leiten. Das Versenden einer Aktivität an 1000 Freunde dauert also Millisekunden.

Eine ausführlichere Erklärung dessen, worüber ich spreche, finden Sie im Twitter-Beispiel von Redis: http://redis.io/topics/twitter-clone

Update Februar 2011 Ich habe momentan 50 Millionen aktive Aktivitäten und nichts geändert. Eine schöne Sache bei etwas Ähnlichem ist, dass es kompakte, kleine Reihen verwendet. Ich plane einige Änderungen vorzunehmen, die viel mehr Aktivitäten und mehr Abfragen dieser Aktivitäten beinhalten würden, und ich werde Redis definitiv verwenden, um die Dinge schnell zu halten. Ich benutze Redis in anderen Bereichen und es funktioniert wirklich gut für bestimmte Arten von Problemen.

Update Juli 2014 Wir haben bis zu 700.000 aktive Benutzer pro Monat. In den letzten Jahren habe ich Redis (wie in der Liste mit Aufzählungszeichen beschrieben) zum Speichern der letzten 1000 Aktivitäts-IDs für jeden Benutzer verwendet. Es gibt normalerweise ungefähr 100 Millionen Aktivitätsdatensätze im System und sie werden immer noch in MySQL gespeichert und haben immer noch das gleiche Layout. Mit diesen Aufzeichnungen können wir mit weniger Redis-Speicher davonkommen, sie dienen als Aufzeichnung von Aktivitätsdaten und wir verwenden sie, wenn Benutzer in der Zeit weiter zurückblättern müssen, um etwas zu finden.

Dies war keine clevere oder besonders interessante Lösung, aber sie hat mir gute Dienste geleistet.

outcassed
quelle
2
+1 für Redis. v2 verwendet virtuellen Speicher, so dass es möglich sein sollte, sich vollständig auf Redis
stagas
16
Wie können Sie diese Tabelle mit tatsächlichen Aktivitäten verknüpfen, wenn es mehrere Aktivitätsquellen gibt (Hinzufügen, Kommentieren usw.)? Verwenden Sie mehrere Linksverknüpfungen (jeweils für eine Aktivitätstabelle)?
Ali Shakiba
1
@casey Echoing @JohnS 'Frage - wie führt man das JOINan den verschiedenen activity_typeTischen durch? Sind diese Verbindungen in Bezug auf die Leistung teuer?
Rob Sobers
1
Hat jemand eine Antwort auf Johns Frage zum "JOIN". Kann jemand einen Link posten, wo er erklärt werden könnte? Ich muss etwas Ähnliches tun und es wäre sehr hilfreich für mich.
Waseem
3
Keine Joins. Eine Abfrage pro Unique activity_type, um die anderen Daten abzurufen, die Sie benötigen.
Outcassed
21

Dies ist meine Implementierung eines Aktivitätsstroms mit MySQL. Es gibt drei Klassen: Activity, ActivityFeed, Subscriber.

Aktivität stellt einen Aktivitätseintrag dar und seine Tabelle sieht folgendermaßen aus:

id
subject_id
object_id
type
verb
data
time

Subject_idist die ID des Objekts, das die Aktion ausführt, object_iddie ID des Objekts, das die Aktion empfängt. typeund verbbeschreibt die Aktion selbst (wenn ein Benutzer beispielsweise einen Kommentar zu einem Artikel hinzufügt, würde er "Kommentar" bzw. "Erstellt" sein), enthalten Daten zusätzliche Daten, um Verknüpfungen zu vermeiden (zum Beispiel kann sie den Betreffnamen enthalten und Nachname, Titel und URL des Artikels, Kommentarkörper usw.).

Jede Aktivität gehört zu einem oder mehreren ActivityFeeds und wird durch eine Tabelle verknüpft, die folgendermaßen aussieht:

feed_name
activity_id

In meiner Anwendung habe ich einen Feed für jeden Benutzer und einen Feed für jeden Artikel (normalerweise Blog-Artikel), aber sie können beliebig sein.

Ein Abonnent ist normalerweise ein Benutzer Ihrer Site, es kann sich jedoch auch um ein beliebiges Objekt in Ihrem Objektmodell handeln (z. B. könnte ein Artikel die feed_action seines Erstellers abonniert haben).

Jeder Abonnent gehört zu einem oder mehreren ActivityFeeds und ist wie oben durch eine Verknüpfungstabelle dieser Art verbunden:

feed_name
subscriber_id
reason

Das reasonFeld hier erklärt, warum der Abonnent den Feed abonniert hat. Wenn ein Benutzer beispielsweise einen Blog-Beitrag mit einem Lesezeichen versehen kann, lautet der Grund "Lesezeichen". Dies hilft mir später beim Filtern von Aktionen für Benachrichtigungen an die Benutzer.

Um die Aktivität für einen Abonnenten abzurufen, füge ich die drei Tabellen einfach zusammen. Der Beitritt ist schnell, da ich dank einer WHEREBedingung, die wie jetzt aussieht , nur wenige Aktivitäten auswähle time > some hours. Ich vermeide andere Verknüpfungen dank des Datenfelds in der Aktivitätstabelle.

Weitere Erklärung vor reasonOrt. Wenn ich beispielsweise Aktionen für E-Mail-Benachrichtigungen an den Benutzer filtern möchte und der Benutzer einen Blog-Beitrag mit einem Lesezeichen versehen hat (und daher den Beitrags-Feed mit dem Grund "Lesezeichen" abonniert), möchte ich nicht, dass der Benutzer ihn erhält E-Mail-Benachrichtigungen über Aktionen für dieses Element. Wenn er den Beitrag kommentiert (und daher den Beitrags-Feed mit dem Grund "Kommentar" abonniert), möchte ich, dass er benachrichtigt wird, wenn andere Benutzer Kommentare zum selben Beitrag hinzufügen. Das Feld "Grund" hilft mir bei dieser Unterscheidung (ich habe es über eine ActivityFilter-Klasse implementiert) zusammen mit den Benachrichtigungseinstellungen des Benutzers.

Nicolò Martini
quelle
Nicolo Martini Ich wollte einen Antwortkommentar zur Aktivität hinzufügen und ihn darunter zeigen. Wie ist das mit Ihrer Struktur möglich? Soll ich eine andere Tabelle hinzufügen oder nur dieselbe verwenden, wenn dieselbe, was sind dann Ihre Vorschläge?
Basit
Wie ist die Leistung dieser Implementierung? Irgendwelche Tests an großen Tischen?
Joshua F. Rountree
16

Es gibt ein aktuelles Format für Aktivitätsströme, das von einer Reihe bekannter Personen entwickelt wird.

http://activitystrea.ms/ .

Grundsätzlich hat jede Aktivität einen Akteur (der die Aktivität ausführt), ein Verb (die Aktion der Aktivität), ein Objekt (an dem der Akteur arbeitet) und ein Ziel.

Zum Beispiel: Max hat einen Link zu Adams Wand gepostet.

Die JSON-Spezifikation hat zum Zeitpunkt des Schreibens die Version 1.0 erreicht, in der das Muster für die Aktivität angezeigt wird, die Sie anwenden können.

Ihr Format wurde bereits von BBC, Gnip, Google Buzz Gowalla, IBM, MySpace, Opera, Socialcast, Superfeedr, TypePad, Windows Live, YIID und vielen anderen übernommen.

Sơn Trần-Nguyễn
quelle
hi @sntran Ich weiß, dass dieser Beitrag vor Jahren war, aber ich habe eine Frage mehr zum Aktivitätsstrom. Gibt es eine Möglichkeit, wie Sie helfen können?
Hiswendy
Sicher. Was ist deine Frage?
Sơn Trần-Nguyễn
Meine Frage ist tatsächlich hier gepostet! Link . Ich glaube, ich habe ein grundlegendes Verständnis des Aktivitätsstroms, bin mir aber nicht sicher, wie ich ihn implementieren soll (dh soll ich Angular oder Node.js verwenden?). Und von dort aus, wie erstelle ich tatsächlich einen Aktivitätsstrom mit eingehende API JSON? Dies sind so grundlegende Fragen, aber ich konnte online keine Antworten finden. Wenn Sie helfen können, würde ich es wirklich schätzen. Danke dir!
Hiswendy
13

Ich denke, dass eine Erklärung zur Funktionsweise des Benachrichtigungssystems auf großen Websites in der Frage zum Stapelüberlauf enthalten ist, wie Websites für soziale Netzwerke Freunde-Updates berechnen. , in der Antwort der Jeremy Wall . Er schlägt die Verwendung von Message Qeue vor und gibt zwei Open Source-Softwareprogramme an, die diese implementieren:

  1. RabbitMQ
  2. Apache QPid

Siehe auch die Frage Wie lässt sich ein sozialer Aktivitätsstrom am besten implementieren?

Nicolò Martini
quelle
1

Sie benötigen unbedingt eine performante und verteilte Nachrichtenwarteschlange. Aber es endet nicht damit, dass Sie entscheiden müssen, was als persistente Daten und was als vorübergehend usw. gespeichert werden soll.

Wie auch immer, es ist wirklich eine schwierige Aufgabe, mein Freund, wenn Sie nach einem leistungsstarken und skalierbaren System suchen. Aber natürlich haben einige großzügige Ingenieure ihre Erfahrungen damit geteilt. LinkedIn hat kürzlich sein Nachrichtenwarteschlangensystem Kafka zu Open Source gemacht. Zuvor hatte Facebook Scribe bereits für die Open Source-Community bereitgestellt. Kafka ist in Scala geschrieben und es dauert zunächst einige Zeit, bis es ausgeführt wird, aber ich habe es mit ein paar virtuellen Servern getestet. Es ist sehr schnell.

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

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

Cagatay Kalan
quelle
0

Anstatt Ihren eigenen zu rollen, können Sie sich an einen Dienst eines Drittanbieters wenden, der über eine API verwendet wird. Ich habe ein Collabinate ( http://www.collabinate.com ) gestartet, das über ein Graphendatenbank- Backend und einige ziemlich ausgefeilte Algorithmen verfügt, mit denen große Datenmengen auf eine sehr gleichzeitige und leistungsstarke Weise verarbeitet werden können. Es verfügt zwar nicht über die Funktionen, die Facebook oder Twitter bieten, ist jedoch für die meisten Anwendungsfälle mehr als ausreichend, in denen Sie Aktivitätsströme, soziale Feeds oder Microblogging-Funktionen in eine Anwendung integrieren müssen.

Mafuba
quelle