2010-04-01 16 views
7

Obtengo un error de interbloqueo en mi transacción mysql.MySQL return Deadlock con la fila de inserción y FK está bloqueado 'para la actualización'

El ejemplo simple de mi situación:

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 es una clave externa refería a A.ID

Veo tres soluciones: error estancamiento

  1. captura de código y vuelva a intentar la consulta.
  2. uso innodb_locks_unsafe_for_binlog en my.cnf
  3. bloqueo (Actualizar) en el cuadro A Thread2 antes de insertar

¿Hay alguna otra solución?

+0

No puedo ver por qué esto sería un punto muerto y su título menciona FK. ¿Existe una relación de clave externa entre A y B que no mencionó – Elemental

+0

@ Elemental: Sí, hay una clave externa. OP lo mencionó, pero de una manera algo críptica. Modifiqué la publicación para que quede claro. –

Respuesta

1

No sé qué código rodea estos ejemplos, pero podría valer la pena utilizar LOCK IN SHARE MODE para ambos subprocesos, ya que en realidad no está actualizando la fila. Si debe usar LOCK FOR UPDATE, creo que bloquear el otro hilo sería la única ruta lógica.

También si se abre para alejarse de MySQL, he encontrado que PostgreSQL tiene una resolución mucho mejor de interbloqueos. En algunos casos, estaba encontrando MySQL bloqueado cada vez que ejecutaba el mismo script en> 1 hilo. Donde el mismo script en PostgreSQL podría manejarlo bien para cualquier cantidad de hilos paralelos.

+0

'Bloquear en modo compartir 'no es aceptable en mi proyecto; ( – user306814

+0

+1. Aunque no parece la mejor solución, es mucho mejor que LOCK FOR UPDATE. –

0

Sobre la base de una función de la mysql high performance blog.

que fue capaz de poner en práctica el siguiente código de manejo de estancamiento en PHP:

/* 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; 
} 
} 

Esperamos que esto podría darle algunas buenas ideas.

+0

es una buena solución, pero quiero buscar otra ruta – user306814

+0

Puede ser una buena solución cuando existe la única instrucción INSERT en la transacción. Pero, en general, no es aceptable, ya que debe repetir la acción completa desde que se inicia la transacción, que puede abarcar varias funciones (métodos). –

0

No hay puntos muertos aquí, ¿qué versión de MySQL y qué nivel de aislamiento usa? Tengo estos resultados, la adición de la columna de marca de tiempo al cuadro 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 contendrá 2 filas que se parecen:

  1. 1000 'Hola mundo' '2011-06-11 19:23: 15'
  2. 1000 'Hola mundo 2' '2011-06-11 19:23:20'

la situación que usted describe tiene lugar sólo cuando B.NAME es el índice único y que está tratando de insertar el mismo valores. La primera inserción espera que se libere el índice A.ID, lo que nunca ocurrirá debido a la duplicación del valor de B.NAME.

Cuestiones relacionadas