Progettazione del database di Facebook?


133

Mi sono sempre chiesto come Facebook ha progettato la relazione utente <-> amico.

Immagino che la tabella degli utenti sia simile a questa:

user_email PK
user_id PK
password 

Immagino la tabella con i dati dell'utente (sesso, età, ecc. Collegati via e-mail dell'utente presumo).

Come collega tutti gli amici a questo utente?

Qualcosa come questo?

user_id
friend_id_1
friend_id_2
friend_id_3
friend_id_N 

Probabilmente no. Perché il numero di utenti è sconosciuto e si espanderà.


13
C'è una pagina di ingegneria di Facebook che contiene molte informazioni di questo tipo, ma non proprio quello che stai chiedendo. Potresti voler chiedere lì e vedere se riesci a ottenere una risposta. facebook.com/FacebookEngineering
John Meagher,

1
Google graph database. Di sicuro non è un RDBMS.

Risposte:


90

Mantenere una tabella di amici che contiene UserID e quindi UserID dell'amico (lo chiameremo FriendID). Entrambe le colonne sarebbero chiavi esterne nella tabella Users.

Esempio abbastanza utile:

Table Name: User
Columns:
    UserID PK
    EmailAddress
    Password
    Gender
    DOB
    Location

TableName: Friends
Columns:
    UserID PK FK
    FriendID PK FK
    (This table features a composite primary key made up of the two foreign 
     keys, both pointing back to the user table. One ID will point to the
     logged in user, the other ID will point to the individual friend
     of that user)

Esempio di utilizzo:

Table User
--------------
UserID EmailAddress Password Gender DOB      Location
------------------------------------------------------
1      bob@bob.com  bobbie   M      1/1/2009 New York City
2      jon@jon.com  jonathan M      2/2/2008 Los Angeles
3      joe@joe.com  joseph   M      1/2/2007 Pittsburgh

Table Friends
---------------
UserID FriendID
----------------
1      2
1      3
2      3

Questo mostrerà che Bob è amico di Jon e Joe e che anche Jon è amico di Joe. In questo esempio supponiamo che l'amicizia sia sempre in due modi, quindi non avresti bisogno di una riga nella tabella come (2,1) o (3,2) perché sono già rappresentati nell'altra direzione. Per esempi in cui l'amicizia o altre relazioni non sono esplicitamente bidirezionali, è necessario disporre anche di quelle righe per indicare la relazione bidirezionale.


8
pensa a quanto sia inefficiente questo fatto: devi fare una query disgiuntiva sulle colonne dei molti-a-molti, raddoppiando i tempi di ricerca in media.
Anthony Bishopric,

2
Personalmente, non vorrei che quei due campi formassero una chiave primaria composita. Una chiave unica, assolutamente. L'indice cluster su quella chiave univoca, sicuramente. Ma metterei anche una sorta di identità non composita come PK con un indice non cluster. Ciò consentirebbe ad altri tavoli che necessitano di un "ID relazione amico" FK di legarsi facilmente a questo tavolo e che vari trigger potrebbero innescare eventi a cascata di amicizia, amicizia, ecc.
Jesse C. Slicer,

1
Ha detto che Facebook ha circa 1'000'000'000 utenti. Se l'utente medio ha 100 amici, ciò significa che la tabella conterrebbe 100'000'000'000 righe. Partizionamento MySQL?
Veidelis,

Dimentica questo approccio. Se ottieni un numero considerevole di utenti, diventerà sicuramente molto lento. Vedi la mia risposta e prova a confrontarla tu stesso. Ho fatto alcuni benchmark con 10k utenti e 2,5 milioni di connessioni di amicizia e il risultato è stato deludente. Se gestisci una piccola comunità funzionerà bene ma ci sono problemi di prestazioni da considerare.
Burzum,

7
puoi essere sicuro che Facebook non utilizza un RDBMS per questo, è risaputo che loro, Twitter e tutti gli altri che hanno bisogno di eseguire query come questa usano un database grafico di qualche tipo. ci sono almeno 69 persone che non hanno mai lavorato su nessun tipo di scala o non sanno come fare matematica su larga scala.

51

Dai un'occhiata al seguente schema di database, retroingegnerizzato da Anatoly Lubarsky :

Schema di Facebook


7
Questo è un diagramma di classe, non uno schema di database
Lemon Juice

2
Quindi ogni "Utente" avrebbe il proprio database dedicato? Come quello sopra? Come funzionerebbe? Ad esempio, quando l'utente accede a FB verifica se si tratta di un User + Pass valido e quindi se è valido Facebook li reindirizzerà al database che visualizza quindi tutto dal database sopra
James111,

Questo negozio contiene solo le informazioni relative all'utente, sto specificatamente cercando la Posta e il suo pubblico?
Waseem Ahmad Naeem,

47

TL; DR:

Usano un'architettura di stack con grafici memorizzati nella cache per tutto ciò che si trova sopra il fondo MySQL del loro stack.

Risposta lunga:

Ho fatto delle ricerche su questo me stesso perché ero curioso di sapere come gestiscono la loro enorme quantità di dati e li cercano in modo rapido. Ho visto persone lamentarsi degli script dei social network su misura diventare lenti quando la base di utenti cresce. Dopo aver fatto un po 'di benchmarking con soli 10k utenti e 2,5 milioni di connessioni di amici - senza nemmeno provare a preoccuparmi delle autorizzazioni di gruppo, dei Mi piace e dei post sul muro - ho rapidamente scoperto che questo approccio è difettoso. Quindi ho trascorso un po 'di tempo a cercare sul web come farlo meglio e ho trovato questo articolo ufficiale di Facebook:

Ho davvero vi consiglio di guardare la presentazione del primo link qui sopra prima di continuare a leggere. È probabilmente la migliore spiegazione di come funziona FB dietro le quinte che puoi trovare.

Il video e l'articolo ti dicono alcune cose:

  • Stanno usando MySQL in fondo al loro stack
  • Sopra il DB SQL è presente il livello TAO che contiene almeno due livelli di memorizzazione nella cache e utilizza grafici per descrivere le connessioni.
  • Non sono riuscito a trovare nulla su quale software / DB utilizzino effettivamente per i loro grafici memorizzati nella cache

Diamo un'occhiata a questo, le connessioni degli amici sono in alto a sinistra:

inserisci qui la descrizione dell'immagine

Bene, questo è un grafico. :) Non ti dice come costruirlo in SQL, ci sono diversi modi per farlo, ma questo sito ha una buona quantità di approcci diversi. Attenzione: considera che un DB relazionale è quello che è: si pensa che memorizzi dati normalizzati, non una struttura grafica. Quindi non funzionerà come un database grafico specializzato.

Considera anche che devi fare query più complesse rispetto ai soli amici di amici, ad esempio quando vuoi filtrare tutte le posizioni intorno a una data coordinata che piacciono a te e ai tuoi amici di amici. Un grafico è la soluzione perfetta qui.

Non posso dirti come costruirlo in modo che funzioni bene, ma richiede chiaramente alcune prove, errori e benchmark.

Ecco il mio test deludente per i risultati solo amici di amici:

Schema DB:

CREATE TABLE IF NOT EXISTS `friends` (
`id` int(11) NOT NULL,
  `user_id` int(11) NOT NULL,
  `friend_id` int(11) NOT NULL
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

Query di Friends of Friends:

(
        select friend_id
        from friends
        where user_id = 1
    ) union (
        select distinct ff.friend_id
        from
            friends f
            join friends ff on ff.user_id = f.friend_id
        where f.user_id = 1
    )

Ti consiglio vivamente di crearti alcuni dati di esempio con almeno 10k record utente e ciascuno di essi con almeno 250 connessioni amico e quindi eseguire questa query. Sul mio computer (i7 4770k, SSD, 16 GB di RAM) il risultato è stato di ~ 0,18 secondi per quella query. Forse può essere ottimizzato, non sono un genio del DB (i suggerimenti sono ben accetti). Tuttavia, se questo scala lineare sei già a 1,8 secondi per soli 100k utenti, 18 secondi per 1 milione di utenti.

Questo potrebbe ancora sembrare OKish per ~ 100.000 utenti, ma considera che hai appena recuperato amici di amici e non hai fatto alcuna query più complessa come " mostrami solo post di amici di amici + fai il permesso di controllare se sono autorizzato o NON permesso per vederne alcuni + fai una sottointerrogazione per verificare se mi è piaciuto qualcuno di loro ". Vuoi lasciare che il DB faccia il check-in se ti è già piaciuto o meno un post o dovrai farlo nel codice. Considera anche che questa non è l'unica query che esegui e che hai più di un utente attivo contemporaneamente su un sito più o meno popolare.

Penso che la mia risposta risponda alla domanda su come Facebook abbia progettato molto bene la relazione dei loro amici, ma mi dispiace di non poterti dire come implementarlo in modo che funzioni velocemente. L'implementazione di un social network è facile ma assicurarsi che funzioni bene non lo è chiaramente - IMHO.

Ho iniziato a sperimentare OrientDB per eseguire le query sui grafici e mappare i miei bordi sul database SQL sottostante. Se mai lo avessi fatto, scriverò un articolo a riguardo.


quindi ... sei mai andato in giro a scrivere l'articolo?
FlowUI. SimpleUITesting.com

1
No, sono piuttosto impegnato a parte la programmazione e non ho avuto il tempo e l'umore per farlo. La risposta qui contiene tutto ciò che devi sapere se vuoi implementare associazioni di amici performanti. Memorizza nella cache le liste di amici per utente o mappa il tuo DB relazionale in parti o tutto su un grafico e interroga il DB grafico. Puoi usare OrientDB o Neo4j per questo. Mi piacerebbe scrivere il mio software di social network open source ma ci sono un sacco di altre cose da fare. Qualunque cosa tu faccia: fai benchmark. :)
burzum,

Ancora no Ma la documentazione di OrientDB spiega le connessioni degli amici e tutto il resto può essere modellato una volta comprese le basi. orientdb.com/docs/2.1/Tutorial-Working-with-graphs.html Se si desidera utilizzare un DB relazionale come base, è sufficiente aggiungere un codice nei callback "dopo il salvataggio" e "dopo l'eliminazione" per aggiornare il proprio DB grafico (che useresti per leggere i dati). Se non si dispone di tali callback, implementarli, ma immagino che quasi tutti i tipi di implementazioni e framework ORM abbiano qualcosa del genere. In realtà OrientDB può anche archiviare documenti.
Burzum,

1
quindi ... sei mai andato in giro a scrivere l'articolo?
Connor Gurney,

1
Ancora no, ma facciamo qualcosa di simile sul lavoro: mappiamo i nostri dati relazionali su un indice di ricerca elastica, come ho scritto nel mio commento prima, è semplicemente una questione di ottenere i dati che si desidera memorizzare nell'indice o nel grafico dopo una certa azione (afterSave () / afterDelete () callback nel nostro caso) e quindi aggiornando l'indice o il grafico. Abbastanza semplice? :) Lo stesso potrebbe essere fatto con gli elenchi di amici a proposito, non importa se li memorizzi in ES, un grafico o una cache basata su memoria (purché tu abbia abbastanza RAM). Non è davvero difficile, la parte difficile è far ridimensionare il tutto quando cresci.
Burzum,

32

La mia scommessa migliore è che hanno creato una struttura grafica . I nodi sono utenti e le "amicizie" sono bordi.

Mantieni una tabella di utenti, mantieni un'altra tabella di bordi. Quindi puoi conservare i dati sui bordi, come "giorno in cui sono diventati amici" e "stato approvato", ecc.


40
Ho la sensazione che dovrai spiegarlo un po 'di più per alcune persone qui.
TheXI

4
Penso che una domanda più interessante sarebbe come mantenere una struttura così grande (stiamo parlando di circa 200 milioni di nodi e miliardi di bordi) in modo che possa essere facilmente cercata e aggiornata.
Dirk Vollmar,

1
@divo: uso intelligente di indici e partizioni.
belgariontheking,

20

È molto probabile una relazione da molte a molte:

FriendList (tabella)

user_id -> users.user_id
friend_id -> users.user_id
friendVisibilityLevel

MODIFICARE

La tabella utenti probabilmente non ha user_email come PK, possibilmente come chiave univoca.

utenti (tabella)

user_id PK
user_email
password

4
Anche se questo ha sicuramente più senso, penso che le prestazioni sarebbero orrende dato il numero di utenti che Facebook ha e quanti amici ogni utente di Facebook ha.
Kevin Pang,

17

Dai un'occhiata a questi articoli che descrivono come sono costruiti LinkedIn e Digg:

Esistono anche "Big Data: punti di vista del team di dati di Facebook" che potrebbero essere utili:

http://developer.yahoo.net/blogs/theater/archives/2008/01/nextyahoonet_big_data_viewpoints_from_the_fac.html

Inoltre, c'è questo articolo che parla di database non relazionali e di come vengono utilizzati da alcune aziende:

http://www.readwriteweb.com/archives/is_the_relational_database_doomed.php

Vedrai che queste aziende hanno a che fare con data warehouse, database partizionati, memorizzazione nella cache dei dati e altri concetti di livello superiore di cui la maggior parte di noi non si occupa mai quotidianamente. O almeno, forse non sappiamo che lo facciamo.

Ci sono molti link nei primi due articoli che dovrebbero darti qualche informazione in più.

AGGIORNAMENTO 20/10/2014

Murat Demirbas ha scritto un riassunto su

  • TAO: archivio dati distribuito di Facebook per il grafico sociale (ATC'13)
  • F4: caldo sistema di archiviazione BLOB di Facebook (OSDI'14)

http://muratbuffalo.blogspot.com/2014/10/facebooks-software-architecture.html

HTH


9

Non è possibile recuperare i dati da RDBMS per i dati degli amici degli utenti per i dati che attraversano più di mezzo miliardo alla volta in modo costante, quindi Facebook lo ha implementato usando un database hash (senza SQL) e hanno aperto il database chiamato Cassandra.

Quindi ogni utente ha la sua chiave e i dettagli degli amici in una coda; per sapere come funziona cassandra guarda questo:

http://prasath.posterous.com/cassandra-55


Molto interessante, grazie amico mio. Quando sono passati a cassandra da sql? sai per caso?
Marin,

1
Attenzione: Posterous Spaces è morto ... quindi il link.
TechNyquist,


5

Stai cercando chiavi esterne. Fondamentalmente non è possibile avere un array in un database a meno che non abbia una propria tabella.


Schema di esempio:

    Tabella degli utenti
        userID PK
        altri dati
    Tavolo degli amici
        userID - FK alla tabella degli utenti che rappresenta l'utente che ha un amico.
        friendID - FK nella tabella Users che rappresenta l'id utente dell'amico

5
Perché i downvotes? Almeno fai sapere a qualcuno perché li hai votati.
Sasha Chedygov,

3
@freak: perché? L'intero concetto di voto su questo sito è di votare anonimo. Perché ritieni che il malfist abbia diritto a qualsiasi cosa?
GEOCHET,

4
Soprattutto quando è una risposta valida e fa eco dalle altre risposte (anche se non ho copiato da loro, quando ho risposto, lì dove non ci sono risposte)
Malfist,

4
@TheTXI: Penso che i commenti sui voti negativi siano una cortesia, specialmente sulle risposte che ovviamente non li meritano, ma sono anche d'accordo che i commenti non dovrebbero essere obbligati.
Robert S.

2
Le persone che votano in modo anonimo su risposte non ovvie sono quelle che temono che il loro ragionamento superficiale sarebbe esposto se lasciassero un commento che spiegava un downvote.
Vinayak,


1

Tenere presente che le tabelle del database sono progettate per crescere in verticale (più righe), non in orizzontale (più colonne)


24
NON DIMENTICARE MAI! Mio padre è morto perché un tavolo db era cresciuto troppo in verticale per le sue colonne. Mi mancherai papà.
belgariontheking,

1
hmm, perché il downvote? E il commento sopra questo non ha senso.
Neil N,

2
No, il commento non ha senso. Sembra che qualcuno abbia cercato di essere divertente, quindi non importa.
Dirk Vollmar,

0

Per quanto riguarda le prestazioni di una tabella molti-a-molti, se si dispone di 2 ints a 32 bit che collegano gli ID utente, l'archiviazione di dati di base per 200.000.000 di utenti con una media di 200 amici ciascuno è poco meno di 300 GB.

Ovviamente, avresti bisogno di un po 'di partizionamento e indicizzazione e non lo manterrai in memoria per tutti gli utenti.


0

Probabilmente esiste una tabella che memorizza la relazione utente <-> utente, ad esempio "frnd_list", con i campi "user_id", "frnd_id".

Ogni volta che un utente aggiunge un altro utente come amico, vengono create due nuove righe.

Ad esempio, supponiamo che il mio ID sia 'deep9c' e aggiungo un utente con ID 'akash3b' come mio amico, quindi due nuove righe vengono create nella tabella "frnd_list" con valori ('deep9c', 'akash3b') e ('akash3b ', 'deep9c').

Ora, quando mostra la lista amici a un determinato utente, un semplice sql lo farebbe: "seleziona frnd_id da frnd_list dove user_id =" dove si trova l'id dell'utente che ha effettuato l'accesso (memorizzato come attributo di sessione).

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.