Caspita, questa è una domanda semplice, che offre una vasta gamma di possibili risposte. La parte più esplicita della tua domanda ti chiede se è più scalabile interfacciarsi con il tuo database direttamente o attraverso un servizio web. Questa risposta è semplice: interrogare direttamente il database. Passare attraverso il servizio Web aggiunge un sacco di latenza che è completamente inutile per il codice che opera dietro un firewall (nel complesso). Un servizio Web, ad esempio, richiede che un componente riceva una richiesta, la deserializzi, interroghi il DB, serializzi una risposta e la restituisca. Quindi, se il tuo codice funziona tutto dietro un firewall, risparmia il problema ed esegui una query direttamente sul DB.
Rendere scalabile un sito Web va ben oltre la domanda che hai posto inizialmente. Quindi perdonami se vado su una tangente qui, ma ho pensato che potrebbe essere utile considerando che hai menzionato Facebook in particolare.
Ti consiglierei di leggere il lavoro e gli strumenti sviluppati da Brad Fitzpatrick (fondatore di LiveJournal e ora su Google). Quando ho lavorato con lui in Six Apart, ecco alcune delle cose che ho imparato da lui e sull'architettura di LiveJournal che l'ha resa così scalabile.
Utilizzare tabelle di database ristrette anziché ampie . Ciò che è stato affascinante di questo è stato imparare ciò che ha motivato questa architettura, che stava creando un sistema facile e veloceaggiornato. Se si utilizzano tabelle larghe o tabelle per le quali ciascun campo o proprietà è una colonna nella tabella, quando arriva il momento di aggiornare lo schema del database, ad esempio aggiungendo una nuova colonna, il sistema dovrà bloccare la tabella mentre lo schema il cambiamento è implementato. Quando si opera su larga scala, ciò significherebbe che una semplice modifica allo schema del database potrebbe comportare un'interruzione del database di grandi dimensioni. Il che fa schifo ovviamente. Una tabella stretta d'altra parte memorizza semplicemente ogni singola proprietà associata a un oggetto come una singola riga nel database. Pertanto, quando si desidera aggiungere una nuova colonna al database, è sufficiente inserire i record INSERT in una tabella, che è un'operazione non bloccante. Ok, questo è un po 'di background, vediamo come questo modello si traduce effettivamente in un sistema funzionante come LiveJournal.
Supponiamo che tu voglia caricare le ultime 10 voci del diario sul blog di una persona e supponiamo che ogni voce del diario abbia dieci proprietà. In un layout di tabella ampia classica, ogni proprietà sarebbe correlata a una colonna su una tabella. Un utente dovrebbe quindi interrogare la tabella una volta per recuperare tutti i dati di cui ha bisogno. La query restituirebbe 10 righe e ogni riga avrebbe tutti i dati necessari (ad es. SELEZIONA * DA voci ORDINA PER data LIMIT 10). In una tabella stretta, tuttavia, le cose sono leggermente diverse. In questo esempio in realtà ci sono due tabelle: la prima tabella (tabella A) memorizza semplici criteri per cui si vorrebbe cercare, ad esempio l'id della voce, l'id dell'autore, la data della voce, ecc. Una seconda tabella (tabella B) quindi memorizza tutte le proprietà associate a una voce. Questa seconda tabella ha tre colonne: entry_id, chiave e valore. Per ogni riga nella tabella A, ci sarebbero 10 righe nella tabella B (una riga per ogni proprietà). Pertanto, per recuperare e visualizzare le ultime dieci voci, sono necessarie 11 query. La prima query fornisce l'elenco degli ID delle voci, quindi le dieci query successive recuperano le proprietà associate a ciascuna delle voci restituite nella prima query.
"Santo moly!" dici "come mai può essere più scalabile ?!" È totalmente controintuitivo, giusto? Nel primo scenario abbiamo appena avuto una query di database, ma nella seconda soluzione "più scalabile" abbiamo 11 query di database. Non ha senso. La risposta a questa domanda si basa interamente sul prossimo proiettile.
Usa memcache liberamente. Nel caso in cui non si fosse a conoscenza, memcache è un sistema di memorizzazione nella cache distribuito, senza stato, a bassa latenza e basato sulla rete. È utilizzato da Facebook, Google, Yahoo e quasi tutti i siti Web popolari e scalabili del pianeta. È stato inventato da Brad Fitzpatrick in parte per aiutare a compensare l'overhead del database inerente a una progettazione di database a tabella ristretta. Diamo un'occhiata allo stesso esempio di cui al punto 1 sopra, ma questa volta introduciamo memcache.
Cominciamo quando un utente visita per la prima volta una pagina e non c'è nulla nella cache. Si inizia interrogando la tabella A che restituisce gli ID delle 10 voci che si desidera visualizzare sulla pagina. Per ognuna di queste voci, si richiede quindi al database di recuperare le proprietà associate a quella voce e quindi l'utilizzo di tali proprietà costituisce un oggetto con cui il codice può interfacciarsi (ad esempio un oggetto). Quindi riponi quell'oggetto (o una forma serializzata di quell'oggetto) in memcache.
La seconda volta che qualcuno carica la stessa pagina, inizi nello stesso modo: eseguendo una query sulla tabella A per l'elenco degli ID voce che verranno visualizzati. Per ogni voce, vai prima su memcache e dici "hai la voce #X nella cache?" Se sì, memcache ti restituisce l'oggetto di immissione. In caso contrario, è necessario interrogare nuovamente il database per recuperare le sue proprietà, costituire l'oggetto e riporlo in memcache. Il più delle volte, la seconda volta che qualcuno visita la stessa pagina c'è solo una query del database, tutti gli altri dati vengono quindi estratti direttamente da memcache.
In pratica, ciò che alla fine è accaduto per la maggior parte di LiveJournal è che la maggior parte dei dati del sistema, in particolare i dati meno volatili, sono stati memorizzati nella cache di memcache e le query aggiuntive al database necessarie per supportare lo schema di tabelle ristrette erano quasi completamente compensate.
Questo design ha fatto risolvere il problema connesso con l'assemblaggio di un elenco dei posti connessi con tutti i tuoi amici in un ruscello, o "muro" molto, molto più facile.
Quindi, prendere in considerazione il partizionamento del database. Il modello discusso sopra presenta ancora un altro problema, e cioè i tuoi tavoli stretti tenderanno ad essere molto grandi / lunghi. E più righe hanno quelle tabelle, più diventano difficili le altre attività amministrative. Per compensare ciò, potrebbe avere senso gestire le dimensioni delle tabelle partizionando le tabelle in qualche modo, in modo che i cluster di utenti siano serviti da un database e un altro cluster di utenti sia servito da un database separato. Ciò distribuisce il carico sul database e mantiene efficienti le query.
Infine, hai bisogno di indici fantastici. La velocità delle tue domande dipenderà in gran parte da quanto bene sono indicizzate le tabelle del tuo database. Non passerò troppo tempo a discutere di cosa sia un indice, se non per dire che è molto simile a un sistema di catalogo di carte giganti per rendere più efficiente la ricerca di aghi in un pagliaio. Se usi mysql, ti consiglio di attivare il registro delle query lente per monitorare le query che richiedono molto tempo per essere soddisfatte. Quando viene visualizzata una query sul radar (ad es. Perché è lenta), quindi capire quale indice è necessario aggiungere alla tabella per accelerarlo.
"Grazie per tutto questo fantastico background, ma santo crudele, è un sacco di codice che dovrò scrivere."
Non necessariamente. Sono state scritte molte librerie che rendono davvero semplice l'interfacciamento con memcache. Ancora altre biblioteche hanno codificato l'intero processo sopra descritto; Data :: ObjectDriver in Perl è proprio una libreria del genere. Per quanto riguarda le altre lingue, dovrai fare le tue ricerche.
Spero che tu abbia trovato utile questa risposta. Quello che ho scoperto più spesso è che la scalabilità di un sistema spesso si riduce sempre meno al codice, e sempre di più a una solida strategia di archiviazione e gestione dei dati / progettazione tecnica.