Architettura del database: cronologia completa di Mi piace / condivisioni / ecc. per tutti gli account?

1

Il programma di installazione

Questo è un sito basato sull'iscrizione. Ogni account è in grado di:

  1. Creazione di post (illimitato, nessuna restrizione sulla frequenza)

  2. Mi piace, condividi e commenta (on) gli altri post

L'obiettivo è fornire la più piccola possibile risoluzione per le statistiche dell'account. Invece di raggruppare likes in gruppi di intervalli di 15 minuti (o qualcosa di simile), stiamo memorizzando ogni cosa come succede.

Al momento, è un Mi piace per tracciare una sola tabella, per tutti gli account. Qualcosa come ...

Table: users (Accounts)

user_id (pk)
... remaining data (name, login, etc)

----

Table: posts

post_id (pk)
user_id (fk)
... remaining data (time, content, etc)

----

Table: post_likes

id (ai, pk)
post_id (fk)
user_id (fk)
action
timestamp

... dove user_id è quella dell'utente che ama il post e action è +1 o -1, per come o antipatia , rispettivamente.

Nota: A antipatia è solo annullare un like (i like per un post non possono sommarsi a meno di zero). Ci sono validi motivi per non avere preferenze di tracciamento, contrariamente all'eliminazione della voce originale come

Il DB è attualmente MySQL e non può essere modificato in questo momento. Tutte le tabelle sono InnoDB per facilitare il blocco delle righe.

Il problema

Giocando un piccolo gioco di Best Case Scenario, supponiamo di avere noi stessi 10 milioni di utenti.

- > la tabella 'utenti' ha 10M voci

E dì, dopo un anno, l'utente medio ha fatto 250 post

- > la tabella 'post' ha 2.5B voci (250 * 10M)

E di 'che ogni post ha una media di 15 operazioni simili (Mi piace e Non mi piace).

- > la tabella 'post_likes' ha 37.5B voci (250 * 10M * 15)

Quando un utente visualizza le sue metriche, la query sarà simile a:

Current likes count:

SELECT SUM(action) as cur_likes FROM post_likes WHERE post_id=?

Comprehensive likes history:

SELECT (action, timestamp) FROM  post_likes WHERE post_id=? ORDER BY timestamp ACS

Miliardi di voci in un unico tavolo mi sembrano molto importanti. Ora, non sono un guru del database, ma un paio di cose mi saltano addosso come problemi:

  1. Cosa succede quando la tabella supera la memoria assegnata?

    Secondo i documenti , per le tabelle InnoDB (dopo aver esteso il tablespace )

    The maximum tablespace size is 64TB.

    Ma quella tabella PER ? Per tutti i tavoli? Ho esaminato Spazi tabelle generici InnoDB , ma non sono ancora chiaro in merito. Capisco che numeri così alti sono un sogno da pipa a questo punto, ma preferirei essere al sicuro che dispiaciuti.

  2. Sicuramente le prestazioni delle operazioni sul tavolo soffrono di un numero così elevato di voci?

    Supponendo che il punto precedente sia discutibile (è improbabile che raggiungeremo o supereremo mai più di 64 TB di dati), vi è molta incoerenza tra i post relativi alla dimensione della tabella rispetto alle prestazioni. Alcuni ti diranno che c'è no legato alla dimensione della tabella , mentre altri dicono che solo pochi milioni sono già dannosi per le prestazioni.

    La mia specialità non è l'amministrazione di DB, e non posso tranquillamente dire in entrambi i modi su questo argomento.

I miei tentativi

Originariamente, avevo una tabella univoca per ogni post:

Table: post_(post_id)_likes

user_id
action
timestamp

... e fu subito detto, "non farlo" - nient'altro, quindi non posso essere sicuro di quale sarebbe stata la soluzione. Situazione simile per una tabella che tiene traccia di tutti i post Mi piace per un singolo utente / account:

Table: posts_(user_id)_likes

user_id
post_id
action
timestamp

Saprò sempre per quale account o post recuperare le metriche, quindi per me era logico creare una tabella specifica per quel post o account. Quindi so esattamente dove guardare, e il numero di record da esaminare sarebbe impallidire rispetto ai miliardi sopra descritti.

C'è un post simile qui , ma le soluzioni imitano le gigantesche tabelle di cui sono preoccupato.

Considerazioni finali

Le cose che sto facendo in questo momento non devono essere perfette e possono sempre essere modificate in un secondo momento, ma mi piacerebbe averlo configurato in un modo che ha senso, pur mantenendo le prestazioni e mantenendolo abbastanza facile da modificare / fissare ulteriormente lungo la strada. Anche se ogni like fosse memorizzato all'interno di un singolo tavolo, probabilmente avremmo un sacco di tempo per ospitare la crescita. Ma se solo pochi milioni sono sufficienti per rallentare, allora ho bisogno di guardare a soluzioni preventive prima di scavarmi in un buco.

Grazie per averlo fatto così lontano!

    
posta Birrel 22.10.2016 - 18:35
fonte

2 risposte

6

Bene, prima di tutto, stai inventando scenari fittizi usando numeri ipotetici estremamente ottimisti. L'attività dell'utente segue una curva esponenziale; la stragrande maggioranza delle attività degli utenti verrà da un gruppo relativamente ristretto di utenti principali. Il resto dei tuoi utenti contribuirà con una notevole quantità di traffico, ma ogni singolo contributo sarà piuttosto piccolo. Per mettere in prospettiva i numeri ipotetici, metà di tutti gli utenti registrati su Stack Overflow hanno una reputazione pari a 1.

In secondo luogo, non ne avrai bisogno. Portare il tuo sito web sul mercato è molto più importante che assicurarti che si adatti al mondo intero. Nell'improbabile caso in cui il tuo sito web raggiunga Facebook o Twitter, avrai i soldi di cui hai bisogno per aggiustare ciò che sarà sicuramente un buon problema.

Terzo, non è possibile prevedere in anticipo i problemi di rendimento. Gli sviluppatori di software sono notoriamente cattivi in questo tipo di previsione. L'unico modo sensato per gestire i problemi di prestazioni è utilizzare fin dall'inizio la progettazione software adeguata e affrontare i problemi di prestazioni nel momento in cui si verificano, utilizzando strumenti che indicano esattamente dove si trova il problema delle prestazioni.

Il design che stai pensando di usare MySQL è esattamente il tipo di design che dovresti contemplare. Gestirà il 95% di tutti gli scenari di traffico e, se mai arrivi al punto in cui devi costruire un sito web del 5% per sostituire quello vecchio, avrai i soldi per farlo.

    
risposta data 22.10.2016 - 19:09
fonte
3

Alla già eccellente risposta di Robert Harvey , vorrei aggiungere un paio di osservazioni :

  • È una pessima progettazione del database per creare una tabella per ogni post o anche una tabella per ogni post di un utente! In un database relazionale, la struttura dovrebbe in linea di massima essere determinata il più possibile e non essere dipendente dal contenuto.
  • Non dovresti preoccuparti delle dimensioni. I DBMS sono progettati per far fronte al volume. Usano strutture dati e indici speciali per questo. E dispongono di ottimizzatori per analizzare il modo migliore per eseguire una query in base alle strutture della tabella e agli indici exsiting e alle dimensioni relative delle tabelle.
  • Ad esempio InnoDB usa b + alberi per gli indici. Questi sono simili agli alberi binari ma sono progettati tenendo conto di un accesso impaginato in un file di database. Per trovare un record tra 37,5 miliardi richiede un massimo di 11 accessi al disco (se ci sono 10 voci di indice per pagina di file, 6 accessi se ci sono 100 voci per pagina).
  • È necessario cercare una struttura ottimale ed evitare i dati ridondanti. Quindi avrebbe avuto senso avere una grande tabella per i post e un'altra per altri eventi relativi ai post (facendo riferimento all'id del post). Da analizzare, ma puoi ulteriormente distinguere i commenti, dai Mi piace e dalle condivisioni, poiché questi arricchiscono tutti i dati dell'evento (utente, tempo, post correlati) con dati diversi (commento, grado di gradimento, ecc.)
  • un database è memorizzato in più tablespace, ognuno dei quali è composto da uno o più file. Secondo la documentazione a cui hai fatto riferimento, la dimensione di una tabella è limitata dalla dimensione massima del sistema operativo (è più piccola del 64TB che menzioni). Se ho capito bene, InnoDB deve trovarsi in un unico tablespace, che pone un limite superiore a tutte le tabelle del tuo schema. Fino a quando non raggiungerai questi limiti, avrai un po 'di tempo per pensare a uno schema di partizione (per distribuire i dati tra due o più database), e forse puoi permetterti RDBMS più potenti (alcuni hanno i limiti a 64K tablespace x 128 TB per spazio tabelle).
risposta data 22.10.2016 - 19:54
fonte