2010-10-03 13 views
6

Solo per darvi un esempio:In che modo MySQL gestisce più query simultaneamente da più utenti?

Ho uno script PHP che gestisce i voti degli utenti.

Quando un utente vota, lo script fa una query per verificare se qualcuno ha già votato per lo stesso ID/prodotto. Se nessuno ha votato, fa un'altra query e inserisce l'ID in una tabella di voti ID generale e un'altra per inserire i dati in una tabella di voti ID per utente. E questo tipo di comportamento è ripetuto in altri tipi di script.

La domanda è: se due utenti diversi votano contemporaneamente è possibile che le due istanze del codice provino a inserire un nuovo ID (o qualche tipo simile di query) che darà un errore ??

Se sì, come impedisco che ciò accada?

Grazie?

Nota importante: Sto utilizzando MyISAM! Il mio web hosting non consente InnoDB.

+0

Potrebbe essere utile mostrare la struttura della tabella (nomi tabella/colonna) – rojoca

+0

Sto solo dando un esempio ma per aiutarti: la tabella product_votes ha un productID, un votes_count e una total_votes_value column. La tabella user_product_votes ha un ID utente, un productID e una colonna user_vote. – Jonathan

+1

L'unica ragione che chiedo è che in questo esempio sembra che sarebbe meglio riorganizzare la logica e la struttura della tabella in modo che gli inserimenti simultanei non contengano invece di cercando di prevenirli. Stai facendo una domanda generale o hai bisogno di risolvere il problema specifico nel tuo esempio? – rojoca

risposta

6

La domanda è, se due utenti diversi voti simultaneamente la sua possibile che le due istanze del codice tenta di inserire un nuovo ID (o qualche simile tipo di query) che darà un erro

Sì, si potrebbe finire con due query facendo l'inserto. A seconda dei vincoli sulla tabella, uno di questi genererà un errore o finirai con due righe nel tuo database.

Si potrebbe risolvere questo, credo, con l'applicazione di un blocco; ad es.se è necessario aggiungere un voto al prodotto con id theProductId: (pseudo codice)

START TRANSACTION; 
//lock on the row for our product id (assumes the product really exists) 
select 1 from products where id=theProductId for update; 
//assume the vote exist, and increment the no.of votes 
update votes set numberOfVotes = numberOfVotes + 1 where productId=theProductId ; 
//if the last update didn't affect any rows, the row didn't exist 
if(rowsAffected == 0) 
    insert into votes(numberOfVotes,productId) values(1,theProductId) 
//insert the new vote in the per user votes 
insert into user_votes(productId,userId) values(theProductId,theUserId); 
COMMIT; 

qualche info in più here

MySQL offre un'altra soluzione pure, che potrebbe essere applicabile qui, insert on duplicate

es si potrebbe essere in grado di fare proprio:

insert into votes(numberOfVotes,productId) values(1,theProductId) on duplicate key 
    update numberOfVotes = numberOfVotes + 1; 

Se i voti della tabella hanno una chiave univoca per la colonna del prodotto id, quanto sopra sarà fare un inserto se il particolare theProductId non esiste, altrimenti si farà un aggiornamento, dove incrementa la colonna numberOfVotes di 1

Probabilmente si potrebbe evitare molto questo se si creava una riga nella tabella dei voti nello stesso momento in cui si aggiungeva il prodotto al database. In questo modo puoi essere sicuro che c'è sempre una riga per il tuo prodotto e basta emettere un AGGIORNAMENTO su quella riga.

+0

Le transazioni esistono esattamente per risolvere questo problema. Usando le transazioni come questa, si garantisce che nessuna query possa mai vedere uno stato intermedio nel database. – SingleNegationElimination

+0

+1 per 'su chiave duplicata'. Funzionamento atomico> bloccaggio manuale. – egrunin

-4

Questo quando il modello singleton è utile. Garantisce che un codice venga eseguito solo da un processo in un istante.

http://en.wikipedia.org/wiki/Singleton_pattern

si deve fare una classe Singleton per l'accesso al database questa ti impedirà il tipo di errore che si descrive.

Cheers.

+0

Cattiva idea, e non risolverà il problema dell'accesso DB concorrente in PHP. – nos

+1

Una classe singleton (anche in un tipico ambiente java) non risolverà il problema. Significa solo che possono esserci solo una sua istanza, ma questo su istanza è accessibile da più thread in parallelo. –

+0

Grazie per la spiegazione :) – enokd

1

La domanda è, se due diversi utenti voti simultaneamente la propria possibile che le due istanze del codice tenta di inserire un nuovo ID (o qualche simile tipo di query) che darà un errore? ?

Sì, in generale questo è possibile. Questo è un esempio di un problema molto comune nei sistemi concorrenti, chiamato race condition.

L'evitamento può essere piuttosto complicato, ma in generale è necessario assicurarsi che le operazioni non possano interlacciare nel modo in cui descrivi, ad es. bloccando il database per un po '.

Ci sono diverse soluzioni pratiche a questo, tutte con i loro vantaggi e rischi (ad esempio, blocchi morti). Vedere l'articolo di Wikipedia per una discussione e ulteriori indicazioni per le informazioni.

0

Il modo più semplice:

LOCK TABLES table1 WRITE, table2 WRITE, table3 WRITE 
-- check for record, insert if not exists, etc... 
UNLOCK TABLES 

Se il voto non si verifica molte volte al secondo, quindi quanto sopra dovrebbe essere sufficiente.

Le tabelle InnoDB offrono transazioni, che potrebbero essere utili anche qui. Altri hanno già commentato, quindi non entrerò nei dettagli.

In alternativa, è possibile risolverlo a livello di codice utilizzando una sorta di mutex di memoria condivisa che disabilita l'esecuzione simultanea di quella sezione di codice PHP.

Problemi correlati