2010-09-04 13 views
6

Subito dopo alcune opinioni sul modo migliore per raggiungere il seguente esito:MySQL - come ottimizzare query per contare i voti

Vorrei conservare nei miei prodotti di database MySQL che possono essere votate dagli utenti (ogni voto vale +1). Voglio anche essere in grado di vedere quante volte in totale un utente ha votato.

Per la mia mente semplice, la seguente struttura della tabella sarebbe l'ideale:

table: product   table: user   table: user_product_vote  
+----+-------------+ +----+-------------+ +----+------------+---------+ 
| id | product | | id | username | | id | product_id | user_id | 
+----+-------------+ +----+-------------+ +----+------------+---------+ 
| 1 | bananas  | | 1 | matthew  | | 1 | 1   | 2  | 
| 2 | apples  | | 2 | mark  | | 2 | 2   | 2  | 
| .. | ..   | | .. | ..   | | .. | ..   | ..  | 

In questo modo posso fare un conteggio del tavolo user_product_vote per ogni prodotto o utente.

Per esempio, quando ho voglia di guardare su banane e il numero di voti di mostrare su una pagina web ho potuto eseguire la seguente query:

SELECT p.product AS product, COUNT(v.id) as votes 
FROM product p 
LEFT JOIN user_product_vote v ON p.id = v.product_id 
WHERE p.id =1 

Se il mio sito è diventato un enorme successo (tutti noi possiamo sogno) e avevo migliaia di utenti che votavano su migliaia di prodotti, temo che l'esecuzione di un tale COUNT con ogni visualizzazione di pagina sarebbe altamente inefficiente in termini di risorse del server.

Un approccio più semplice consiste nell'avere una colonna 'voti' nella tabella prodotto che viene incrementata ogni volta che viene aggiunta una votazione.

table: product    
+----+-------------+-------+ 
| id | product | votes | 
+----+-------------+-------+ 
| 1 | bananas  | 2  | 
| 2 | apples  | 5  | 
| .. | ..   | .. | 

Mentre questo è più risorsa gentile - ho una perdita di dati (ad esempio, non riesco più a impedire ad una persona di votare due volte in quanto non v'è alcuna traccia della loro attività di voto.).

Le mie domande sono:
i) sono eccessivamente preoccupato per le risorse del server e dovrei limitarmi all'opzione con tre tabelle? (Es. Devo avere più fiducia nella capacità del database per gestire le query di grandi dimensioni)
ii) è il loro un modo più efficiente di ottenere il risultato senza perdere le informazioni

+1

un altro problema è che probabilmente non hai mai posseduto un sito web che ottiene traffico di massa, quindi la tua insicurezza delle funzionalità php/mysql, ti assicuro che mysql può gestire migliaia di query al secondo con prestazioni drastiche a molto – RobertPitt

risposta

6

Non si può mai essere preoccupati per le risorse, quando si inizia a costruire un'applicazione si dovrebbe sempre avere in mente risorse, spazio, velocità, ecc., Se il traffico del tuo sito è cresciuto in modo esponenziale e non hai mai costruito risorse allora inizi a ricevere nei problemi.

Per quanto riguarda il sistema di voto, personalmente vorrei mantenere i voti in questo modo:

table: product   table: user    table: user_product_vote  
+----+-------------+ +----+-------------+ +----+------------+---------+ 
| id | product | | id | username | | id | product_id | user_id | 
+----+-------------+ +----+-------------+ +----+------------+---------+ 
| 1 | bananas  | | 1 | matthew  | | 1 | 1   | 2  | 
| 2 | apples  | | 2 | mark  | | 2 | 2   | 2  | 
| .. | ..   | | .. | ..   | | .. | ..   | ..  | 

Motivi:

In primo luogo user_product_vote non contiene testo, blob, ecc, è puramente intero in modo che copra meno risorse comunque.

In secondo luogo, si dispone di più di una porta per nuove entità all'interno della vostra applicazione, ad esempio Totale voti ultima 24 ore, il prodotto più votati nel corso degli ultimi 24 ore, ecc

Prendete questo esempio per esempio:

table: user_product_vote  
+----+------------+---------+-----------+------+ 
| id | product_id | user_id | vote_type | time | 
+----+------------+---------+-----------+------+ 
| 1 | 1   | 2  | product |224.. | 
| 2 | 2   | 2  | page  |218.. | 
| .. | ..   | ..  | ..  | .. | 

e una semplice query:

SELECT COUNT(id) as total FROM user_product_vote WHERE vote_type = 'product' AND time BETWEEN(....) ORDER BY time DESC LIMIT 20 

Un'altra cosa è se un utente ha votato in 1AM e poi ha tentato di votare di nuovo a 2PM, puoi controllare facilmente l'ultima volta in cui hanno votato e se devono essere autorizzati a votare di nuovo.

Ci sono così tante opportunità che mancherete se continuate con il vostro esempio incrementale.


In merito al suo count(), non importa quanto a ottimizzare le query che non sarebbe davvero fare la differenza su larga scala.

Con una base di utenti estremamente ampia, l'utilizzo delle risorse verrà esaminato da una prospettiva diversa, ad esempio bilanciamento del carico, principalmente impostazioni del server, Apache, rilevamento ecc., C'è solo così tanto che puoi fare con le tue query.

0

Bisogna bilanciare il desiderio per la vostra sito da eseguire rapidamente (in cui il secondo schema sarebbe il migliore) e la possibilità di contare i voti per utenti specifici e impedire il doppio voto (per il quale sceglierei il primo schema). Poiché stai utilizzando solo colonne intere per la tabella user_product_vote, non vedo come le prestazioni potrebbero risentirne troppo. Le relazioni many-to-many sono comuni, come è stato implementato con user_product_vote. Se si desidera contare i voti per utenti specifici e impedire il doppio voto, uno user_product_vote è l'unico modo che posso pensare di implementarlo, poiché qualsiasi altro potrebbe risultare in record sparsi, record duplicati e tutti i tipi di cose cattive.

1

Perché non combinare entrambi? Basta contare i conteggi finali nelle tabelle prodotto e utenti, in modo da non dover contare ogni volta e avere la tabella dei voti, in modo che non ci sia un doppio post.

Modifica: Per spiegarlo un po 'oltre, il prodotto e la tabella utente avranno una colonna denominata "voti". Ogni volta che l'inserimento ha esito positivo in user_product_vote, incrementa i record relativi all'utente e al prodotto. Ciò eviterebbe i voti dupe e non dovrai eseguire la complessa query di conteggio ogni volta.

Edit: Anche Io parto dal presupposto che si è creato un indice univoco product_id e user_id, in questo caso, ogni tentativo di duplicazione fallirà automaticamente e non dovrete controllare nella tabella prima di inserire. Dovrai solo assicurarti che la query inserisca e abbia un valore valido per "id" nel modulo su insert_id

0

Non vuoi aggiornare la tabella di prodotto direttamente con un aggregato ogni volta che qualcuno vota - questo bloccherà le righe di prodotto che avranno quindi effetto su altre query che utilizzano i prodotti.

Supponendo che non tutte le query di prodotto debbano includere la colonna dei voti, è possibile mantenere una tabella di productvotes separata che manterrà i totali correnti e mantenere la tabella userproductvote come mezzo per applicare il voto degli utenti per le regole di business del prodotto/e auditing.

2

Se il mio sito è diventato un enorme successo (tutti noi possiamo sogno) e avevo migliaia di utenti che votano su migliaia di prodotti, temo che l'esecuzione di tale COUNT con ogni visualizzazione di una pagina sarebbe altamente inefficiente in termini di risorse del server .

Non perdere tempo a risolvere i problemi immaginari. mysql è perfettamente in grado di elaborare migliaia di record in frazioni di secondo: ecco a cosa servono i database. Il database semplice e pulito e la struttura del codice sono molto più importanti della mitica "ottimizzazione" di cui nessuno ha bisogno.