Domanda

Come si fermano le condizioni di gara in MySQL? il problema attuale è causato da un semplice algoritmo:

  1. seleziona una riga dalla tabella
  2. se non esiste, inseriscilo

e quindi ottieni una riga duplicata o, se la prevedi tramite chiavi uniche / primarie, un errore.

Ora normalmente penso che le transazioni aiutino qui, ma poiché la riga non esiste, la transazione in realtà non aiuta (o mi sto perdendo qualcosa?).

LOCK TABLE suona come un eccessivo, soprattutto se la tabella viene aggiornata più volte al secondo.

L'unica altra soluzione che mi viene in mente è GET_LOCK () per ogni ID diverso, ma non esiste un modo migliore? Non ci sono problemi di scalabilità anche qui? Inoltre, farlo per ogni tabella suona un po 'innaturale, poiché sembra un problema molto comune nei database ad alta concorrenza per me.

È stato utile?

Soluzione

quello che vuoi è LOCK TABLES

o se questo sembra eccessivo, che ne dici di INSERT IGNORE con un controllo che la riga sia stata effettivamente inserita.

  

Se si utilizza la parola chiave IGNORE, errori   che si verificano durante l'esecuzione di INSERT   L'istruzione viene considerata come avvertenza   invece.

Altri suggerimenti

Mi sembra che dovresti avere un indice univoco nella tua colonna ID, quindi un inserimento ripetuto innescherebbe un errore invece di essere accettato di nuovo in modo accecante.

Ciò può essere fatto definendo l'id come chiave primaria o usando un indice univoco da solo.

Penso che la prima domanda che devi porre sia perché hai molti thread che fanno esattamente lo stesso lavoro? Perché dovrebbero inserire la stessa riga esatta?

Dopo aver ricevuto risposta, penso che ignorare gli errori sia la soluzione più efficace, ma misurare entrambi gli approcci (GET_LOCK v / s ignora gli errori) e vedere di persona.

Non c'è altro modo che io conosca. Perché vuoi evitare errori? Devi ancora codificare il caso quando si verifica un altro tipo di errore.

Come staticsan afferma che le transazioni aiutano ma, come di solito sono implicite, se due inserimenti sono gestiti da thread diversi, entrambi saranno all'interno di una transazione implicita e vedranno viste coerenti del database.

Il blocco dell'intera tabella è davvero eccessivo. Per ottenere l'effetto desiderato, hai bisogno di qualcosa che la letteratura chiama "predicate locks". Nessuno ha mai visto quelli tranne quelli stampati sul documento su cui sono pubblicati studi accademici. La prossima cosa migliore sono i blocchi sui "percorsi di accesso" ai dati (in alcuni DBMS: " blocchi di pagine ").

Alcuni sistemi non SQL ti consentono di eseguire sia (1) che (2) in una singola istruzione, più o meno significando le potenziali condizioni di competizione derivanti dal tuo sistema operativo che sospende il thread di esecuzione tra (1) e (2) , sono completamente eliminati.

Tuttavia, in assenza di blocchi predicati, tali sistemi dovranno ancora ricorrere a un qualche tipo di schema di blocco, e più fine è la "granularità". (/ " scope ") dei blocchi necessari, meglio è per la concorrenza.

(E per concludere: alcuni DBMS - in particolare quelli per i quali non è necessario pagare - non offrono in effetti una granularità di blocco più fine di " l'intera tabella " ;.)

A livello tecnico, una transazione aiuterà qui perché gli altri thread non vedranno la nuova riga fino a quando non commetti la transazione.

Ma in pratica ciò non risolve il problema, ma solo lo sposta . L'applicazione deve ora verificare se il commit non riesce e decidere cosa fare. Normalmente vorrei ripristinare ciò che hai fatto e riavviare la transazione perché ora la riga sarà visibile. Ecco come dovrebbe funzionare il programmatore basato sulle transazioni.

Ho riscontrato lo stesso problema e ho cercato in rete per un momento :)

Alla fine ho trovato una soluzione simile al metodo creazione di oggetti filesystem in directory condivise (temporanee) per aprire in modo sicuro file temporanei:

$exists = $success = false;
do{
 $exists = check();// select a row in the table 
 if (!$exists)
  $success = create_record();
  if ($success){
   $exists = true;
  }else if ($success != ERROR_DUP_ROW){
    log_error("failed to create row not 'coz DUP_ROW!");
    break;
  }else{
    //probably other process has already created the record,
    //so try check again if exists
  }
}while(!$exists)

Non aver paura di busy-loop - normalmente verrà eseguito una o due volte.

Previeni le righe duplicate in modo molto semplice inserendo indici univoci nelle tue tabelle. Ciò non ha nulla a che fare con BLOCCHI o TRANSAZIONI.

Ti interessa se un inserto fallisce perché è un duplicato? Devi essere avvisato se fallisce? O è tutto ciò che conta che la riga sia stata inserita e non importa da chi o quanti inserti duplicati non sono riusciti?

Se non ti interessa, allora tutto ciò di cui hai bisogno è INSERT IGNORE . Non è necessario pensare a transazioni o blocchi di tabelle.

InnoDB ha automaticamente il blocco a livello di riga, ma questo vale solo per gli aggiornamenti e le eliminazioni. Hai ragione che non si applica agli inserti. Non puoi bloccare ciò che non esiste ancora!

Puoi esplicitamente LOCK l'intera tabella. Ma se il tuo scopo è prevenire i duplicati, allora stai sbagliando. Ancora una volta, utilizza un indice univoco.

Se è necessario apportare una serie di modifiche e si desidera un risultato tutto o niente (o anche un insieme di risultati tutto o niente all'interno di un risultato più o meno grande), utilizzare le transazioni e punti di salvataggio. Quindi utilizzare ROLLBACK o ROLLBACK TO SAVEPOINT * savepoint_name * per annullare le modifiche, tra cui eliminazioni, aggiornamenti e inserti.

Le tabelle

LOCK non sostituiscono le transazioni, ma sono la tua unica opzione con le tabelle MyISAM, che non supportano le transazioni. Puoi anche usarlo con le tabelle InnoDB se il blocco a livello di riga non è sufficiente. Vedi questa pagina per maggiori informazioni su utilizzando le transazioni con le istruzioni della tabella di blocco.

Ho un problema simile. Ho una tabella che nella maggior parte dei casi dovrebbe avere un valore ticket_id univoco, ma ci sono alcuni casi in cui avrò dei duplicati; non è il miglior design, ma è quello che è.

  1. L'utente A controlla se il biglietto è riservato, non lo è
  2. L'utente B verifica se il biglietto è riservato, non lo è
  3. L'utente B inserisce un record "riservato" nella tabella per quel ticket
  4. L'utente A inserisce un record "riservato" nella tabella per quel ticket
  5. L'utente B verifica la presenza di duplicati? Sì, il mio record è più recente? Sì, lascialo
  6. Utente Un controllo per duplicati? Sì, il mio record è più recente? No, cancellalo

L'utente B ha prenotato il biglietto, l'utente A riporta che il biglietto è stato ritirato da qualcun altro.

La chiave nel mio caso è che hai bisogno di un tie-breaker, nel mio caso è l'id di incremento automatico sulla riga.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top