Los deadlocks son un problema clásico de las bases de datos transaccionales, pero no son peligrosos a menos que sean tan frecuentes que no se puedan ejecutar en absoluto ciertas transacciones. Normalmente, las aplicaciones deben ser escritas de modo que esten preparadas para emitir nuevamente una transacción si ésta es cancelada debido a un deadlock.
InnoDB emplea bloqueos automáticos a nivel
de fila. Se pueden producir deadlocks aún en el caso de
transacciones que solamente insertan o eliminan una fila
individual. Esto se debe a que estas operaciones no son
realmente “atómicas”; sino que establecen
automáticamente bloqueos enlos (posiblemente varios) registros
de índice de la fila insertada o eliminada.
Con las siguientes técnicas se puede estar a cubierto de los deadlocks y reducir la probabilidad de que ocurran:
Emplear SHOW INNODB STATUS para
determinar la causa del último deadlock. Puede ayudar a
afinar la aplicación para evitar que ocurran otros.
Siempre hay que estar preparado para emitir nuevamente una transacción que haya fallado por un deadlock. Los deadlocks no revisten peligro, simplemente hay que intentar de nuevo.
Confirmar las transacciones frecuentemente. Las transacciones pequeñas son menos propensas a originar conflictos.
Si se están usando lecturas que establecen bloqueos
(SELECT ... FOR UPDATE o ...
LOCK IN SHARE MODE), hay que intentar utilizar un
nivel de aislamiento bajo, como READ
COMMITTED.
Acceder a las tablas y filas en un orden fijo. Entonces, las transacciones forman secuencias bien definidas y no originan deadlocks.
Agregar a las tablas índices adecuadamente elegidos.
Entonces las consultas necesitarán examinar menos registros
de índice y en consecuencia establecerán menos bloqueos.
Utilizar EXPLAIN SELECT para determinar
los índices que MySQL considera más apropiados para las
consultas.
Utilizar menos el bloqueo. Si es aceptable que
SELECT devuelva datos de una captura de
la base de datos que no sea la más actualizada, no hay que
agregarle las cláusulas FOR UPDATE o
LOCK IN SHARE MODE. En este caso es
adecuado utilizar el nivel de aislamiento READ
COMMITTED, porque cada lectura consistente dentro
de la misma transacción leerá de su propia captura más
reciente.
Si nada de esto ayuda, habrá que serializar las
transacciones con bloqueos a nivel de tabla. La forma
correcta de emplear LOCK TABLES con
tablas transaccionales, como InnoDB, es establecer
AUTOCOMMIT = 0 y no invocar a
UNLOCK TABLES hasta que se haya
confirmado explícitamente la transacción. Por ejemplo, si
se necesitara escribir en una tabla t1 y
leer desde una tabla t2, se puede hacer
esto:
SET AUTOCOMMIT=0; LOCK TABLES t1 WRITE, t2 READ, ...; [aquí se hace algo con las tablas t1 y t2]; COMMIT; UNLOCK TABLES;
Los bloqueos a nivel de tabla favorecen el funcionamiento de la cola de transacciones, y evitan los deadlocks.
Otra manera de serializar transacciones es crear una tabla
“semáforo” auxiliar que contenga sólo una
fila. Hay que hacer que cada transacción actualice esa fila
antes de acceder otras tablas. De ese modo, todas las
transacciones se producirán en serie. Nótese que el
algoritmo de detección instantánea de deadlocks de
InnoDB también funciona en este caso,
porque el bloqueo de serialización es un bloqueo a nivel de
fila. Con los bloqueos a nivel de tabla de MySQL, debe
emplearse el método de timeout para solucionar deadlocks.
En aquellas aplicaciones que emplean el comando de MySQL
LOCK TABLES, MySQL no establece bloqueos
de tabla si AUTOCOMMIT=1.
Ésta es una traducción del manual de referencia de MySQL, que puede encontrarse en dev.mysql.com. El manual de referencia original de MySQL está escrito en inglés, y esta traducción no necesariamente está tan actualizada como la versión original. Para cualquier sugerencia sobre la traducción y para señalar errores de cualquier tipo, no dude en dirigirse a mysql-es@vespito.com.
