2010-03-18 9 views
10

tengo la siguiente consulta (todas las tablas son InnoDB)cómo evitar el estancamiento en MySQL

INSERT INTO busy_machines(machine) 
       SELECT machine FROM all_machines 
       WHERE machine NOT IN (SELECT machine FROM busy_machines) 
       and machine_name!='Main' 
       LIMIT 1 

¿Qué hace que un punto muerto cuando lo ejecuto en hilos, obviamente debido a la SELECT interna, ¿verdad?

El error que consigo es:

(1213, 'Deadlock found when trying to get lock; try restarting transaction') 

¿Cómo puedo evitar el estancamiento? ¿Hay alguna manera de cambiar a la consulta para que funcione, o tengo que hacer algo más?

El error no ocurre siempre, por supuesto, solo después de ejecutar esta consulta muchas veces y en varios hilos.

+0

¿Usted está experimentando estancamiento o la contención de bloqueo? – Quassnoi

+0

@Quassnoi: Agregué la información a la pregunta - (1213, 'Interbloqueo encontrado al intentar obtener el bloqueo; intente reiniciar la transacción') – olamundo

+0

'mostrar estado de innodb' describiría brevemente la causa del último interbloqueo – jonny

Respuesta

5

Probablemente obtendrá un mejor rendimiento si reemplaza su "NOT IN" con una unión externa.

También puede separar esto en dos consultas para evitar insertar y seleccionar la misma tabla en una sola consulta.

Algo como esto:

  SELECT a.machine 
      into @machine 
      FROM all_machines a 
      LEFT OUTER JOIN busy_machines b on b.machine = a.machine 
      WHERE a.machine_name!='Main' 
      and b.machine IS NULL 
      LIMIT 1; 

      INSERT INTO busy_machines(machine) 
      VALUES (@machine); 
+0

Esta es una gran solución si está utilizando MySQL 5.0 o superior, de lo contrario no tendrá variables de usuario disponibles. Esto también debería evitar los problemas de interbloqueo. –

+0

No entiendo cómo es tu consulta similar a la mía: la unión externa devolverá también máquinas que están en máquinas ocupadas, y no solo aquellas que no son – olamundo

+0

@noam, usando la combinación externa combinada con "y b.machine IS NULL "en la cláusula where excluye las máquinas que están en busy_machines. Devolverá los mismos datos que NOT IN, pero lo hará de manera mucho más eficiente. –

11

Según entiendo, una selección no adquiere bloqueo y no debe ser la causa del punto muerto.

Cada vez que inserta/actualiza/o elimina una fila, se adquiere un bloqueo. Para evitar el interbloqueo, debe asegurarse de que las transacciones simultáneas no actualicen la fila en un orden que podría provocar un interbloqueo. En términos generales, para evitar el interbloqueo debe adquirir el bloqueo siempre en el mismo orden incluso en diferentes transacciones (por ejemplo, siempre la tabla A primero, luego la tabla B).

Pero si dentro de una transacción inserta en una sola tabla, se cumple esta condición, y esto normalmente no debería llevar a un interbloqueo. ¿Estás haciendo algo más en la transacción?

Sin embargo, puede producirse un interbloqueo si hay índices faltantes. Cuando se inserta/actualiza/elimina una fila, la base de datos necesita verificar las restricciones relacionales, es decir, asegurarse de que las relaciones sean consistentes. Para hacerlo, la base de datos debe verificar las claves externas en las tablas relacionadas. Es podría resultar en la adquisición de otro bloqueo que la fila que se modifica. Asegúrese de tener siempre el índice en las claves externas (y, por supuesto, las principales), de lo contrario podría resultar en un bloqueo de tabla en lugar de un bloqueo de fila. Si se produce un bloqueo de tabla, la contención de bloqueo es mayor y aumenta la probabilidad de interbloqueo.

No estoy seguro de qué sucede exactamente en su caso, pero tal vez ayude.

+0

-1; MySQL ni siquiera le permitirá crear una clave externa entre dos columnas, a menos que ambas estén indexadas, por lo que los índices faltantes en claves externas no pueden explicar nada. –

Cuestiones relacionadas