2010-04-01 3 views
7

Errore di deadlock nella mia transazione mysql.Ritorno MySQL Deadlock con riga di inserimento e FK bloccato 'per aggiornamento'

Il semplice esempio della mia situazione:

Thread1 > BEGIN; 
Query OK, 0 rows affected (0.00 sec) 

Thread1 > SELECT * FROM A WHERE ID=1000 FOR UPDATE; 
1 row in set (0.00 sec) 

Thread2 > BEGIN; 
Query OK, 0 rows affected (0.00 sec) 

Thread2 > INSERT INTO B (AID, NAME) VALUES (1000, 'Hello world'); 
[Hangs] 

Thread1 > INSERT INTO B (AID, NAME) VALUES (1000, 'Hello world2'); 
ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction 

Thread2 > 
Query OK, 1 row affected (10.00 sec) 

B.AID è una chiave esterna riferiva a A.ID

Vedo tre soluzioni: errore di stallo

  1. cattura nel codice e riprovare la query.
  2. uso innodb_locks_unsafe_for_binlog in my.cnf
  3. serratura (per l'aggiornamento) Tabella A del Thread2 prima inserto

C'è qualche altre soluzioni?

+0

Non riesco a capire perché questo sarebbe un punto morto e il tuo titolo menziona FK. C'è una relazione di chiave esterna tra A e B che non hai menzionato – Elemental

+0

@Elemental: Sì, c'è una chiave esterna. OP ha menzionato questo, ma in modo piuttosto criptico. Ho modificato post per renderlo chiaro. –

risposta

1

Non so quale codice circonda questo esempio, ma potrebbe valere la pena di utilizzare LOCK IN SHARE MODE per entrambi i thread, poiché in realtà non si sta aggiornando la riga stessa. Se è necessario utilizzare LOCK FOR UPDATE, penso che il blocco dell'altro thread sarebbe l'unico percorso logico.

Inoltre, se si apre lo spostamento da MySQL, ho trovato che PostgreSQL ha una risoluzione molto migliore dei deadlock. In alcuni casi, stavo trovando MySQL deadlock ogni volta che eseguivo lo stesso script su> 1 thread. Dove lo stesso script in PostgreSQL poteva gestirlo bene per qualsiasi numero di thread paralleli.

+0

'Lock in share mode' non è accettabile nel mio progetto; ( – user306814

+0

+1. Anche se non sembra la soluzione migliore, è molto meglio di LOCK FOR UPDATE. –

0

Sulla base di una funzione dal mysql high performance blog.

sono stato in grado di implementare la gestione di codice in PHP seguente situazione di stallo:

/* maximum number of attempts for deadlock */ 
$MAX_ATTEMPS = 20; 

/* query */ 
$sql = "INSERT INTO B (AID, NAME) VALUES (1000, 'Hello world')"; 

/* current attempt counter */ 
$current = 0; 

/* try to query */ 
while ($current++ <$MAX_ATTEMPS) 
{ 
    $result = mysql_query($sql); 
    if(!$result && (mysql_errno== '1205' || mysql_errno == '1213' )) 
     continue; 
    else 
     break; 
} 
} 

Speriamo che questo potrebbe darvi alcune buone idee.

+0

è una buona soluzione, ma voglio trovare altro percorso – user306814

+0

Potrebbe essere una buona soluzione quando c'è la sola istruzione INSERT nella transazione. Ma in generale non è accettabile dal momento che devi ripetere tutta una serie di azioni da quando è iniziata la transazione, che potrebbe estendersi su diverse funzioni (metodi). –

0

Non ci sono deadlock qui, quale versione di MySQL e quale livello di isolamento usi? ho ottenuto questi risultati, aggiungendo colonna timestamp alla tabella B:

Thread1 > BEGIN; 

Thread1 > SELECT * FROM A WHERE ID=1000 FOR UPDATE; 
/* 0 rows affected, 1 rows found */ 

Thread2 > BEGIN; 

Thread2 > INSERT INTO B (AID, NAME, date) VALUES (1000, 'Hello world', NOW()); 
[Hangs] 

-- after 5 seconds 

Thread1 > INSERT INTO B (AID, NAME, date) VALUES (1000, 'Hello world2', NOW()); 
/* 1 rows affected, 0 rows found */ 

Thread1 > COMMIT; 

Thread2 > COMMIT; 

B conterrà 2 righe che assomigliano:

  1. 1000 'Ciao mondo' '2011-06-11 19:23: 15'
  2. 1000 'Ciao world2' '2011-06-11 19:23:20'

la situazione che hai descritto avviene solo quando B.NAME è indice univoco e si sta tentando di inserire la stessa valori. Il primo inserimento attende che venga rilasciato l'indice A.ID, che non si verificherà mai a causa della duplicazione del valore per B.NAME.